How to Build a Local WordPress Dev Stack on Mac with Homebrew
TL;DR
Tired of MAMP’s limitations? This guide walks you through building a local WordPress development stack on macOS using Homebrew-installed Apache, PHP 8.4, and MariaDB with real trusted SSL via mkcert and WP-CLI baked in. The one-time setup takes about 30–45 minutes. After that, spinning up a fully configured WordPress site at https://sitename.test is a single command: newsite sitename. No clicking through wizards. No workarounds. Just a stack you actually control.
As a long-time WordPress developer, over the years I’ve used every sort of tool available for running WordPress locally for development purposes. I remember starting with XAMPP, moving to MAMP, Desktop Server (RIP), and I event tried Local by WP Engine for a while. They’re great tools for getting started… download, click a button, you’ve got WordPress running. No complaints there.
But the longer I used them, the more I started to bump into their ceilings. The SSL certificates that require workarounds to trust. The PHP version that’s one step behind what production is running. The config files buried somewhere inside an app bundle you can’t easily edit. The fact that spinning up a new site still takes four or five manual steps no matter how many times you’ve done it.
This article is for the WordPress developer who knows their way around a codebase and wants a local setup they actually control. We’re going to build a stack using Homebrew-installed Apache, PHP, and MariaDB, wire up mkcert so every local site gets a real trusted SSL certificate, install WP-CLI, and then wrap it all in a single shell script — newsite — that creates a fully configured WordPress install at https://sitename.test in under a minute. One-time setup cost: about 30–45 minutes. After that, every new site is one command.
Let’s get into it.
Prerequisites
- macOS (Apple Silicon or Intel — Homebrew handles both). Note: all paths in this post use
/opt/homebrew, which is where Homebrew installs on Apple Silicon. If you’re on an Intel Mac, replace/opt/homebrewwith/usr/localthroughout. - Comfort with Terminal. You don’t need to be a shell wizard, but you should be able to open a terminal, run commands, and edit a text file.
- ~30–45 minutes for the initial setup
We’ll be installing: Homebrew, Apache, PHP 8.4, MariaDB, mkcert, and WP-CLI.
Step 1: Install Homebrew
If you don’t already have Homebrew, install it with:
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
Homebrew is the package manager we’ll use for everything in this stack. It keeps components isolated, upgradeable, and easy to manage as a set of services. Once it’s installed, verify it:
brew --version
If you’re on Apple Silicon, follow any post-install instructions Homebrew prints about adding it to your PATH (it’ll tell you exactly what to run).
Step 2: Install and Configure Apache
macOS ships with Apache, but we want the Homebrew version so we can control it:
brew install httpd
Now edit the main config file. Open it in your editor of choice:
open /opt/homebrew/etc/httpd/httpd.conf
Make the following changes:
Listen on port 80 (Homebrew’s Apache defaults to 8080 — change it):
Listen 80
Enable the modules you need by uncommenting these lines (remove the leading #):
LoadModule socache_shmcb_module lib/httpd/modules/mod_socache_shmcb.so
LoadModule ssl_module lib/httpd/modules/mod_ssl.so
LoadModule rewrite_module lib/httpd/modules/mod_rewrite.so
Add the PHP module — find where the other LoadModule lines are and add this line (it won’t be there yet):
LoadModule php_module /opt/homebrew/opt/php/lib/httpd/modules/libphp.so
Note: Do this step after installing PHP (Step 3). Come back here if you’re following along in order.
Set the server name to avoid a warning:
ServerName localhost
Allow .htaccess overrides — find the <Directory "/opt/homebrew/var/www"> block and change AllowOverride None to:
AllowOverride All
Set the directory index to serve PHP files:
DirectoryIndex index.php index.html
Wire Apache to process PHP files — find or add this block:
<FilesMatch \.php$>
SetHandler application/x-httpd-php
</FilesMatch>
Enable SSL config and virtual hosts — find the Include lines near the bottom and make sure these are uncommented:
Include /opt/homebrew/etc/httpd/extra/httpd-ssl.conf
Include /opt/homebrew/etc/httpd/extra/httpd-vhosts.conf
Then open the SSL config and change its default port from 8443 to 443:
open /opt/homebrew/etc/httpd/extra/httpd-ssl.conf
Find Listen 8443 and change it to:
Listen 443
Create the certs directory (we’ll put mkcert-generated certs here):
mkdir -p /opt/homebrew/etc/httpd/certs
Create the logs directory for your sites:
mkdir -p ~/Sites/logs
Create the vhosts file if it doesn’t exist yet:
touch /opt/homebrew/etc/httpd/extra/httpd-vhosts.conf
Now start Apache:
brew services start httpd
And confirm it’s running:
curl -s http://localhost | head -5
You should see some HTML from Apache’s default page.
Step 3: Install and Configure PHP
brew install php
This installs PHP 8.4 (or whichever current stable version Homebrew offers). Now tweak a few settings in php.ini. Find the path for your installed version:
php --ini | grep "Loaded Configuration"
Then open it (replace 8.4 with your version if it differs):
open /opt/homebrew/etc/php/8.4/php.ini
Find and update these values:
memory_limit = 1G
upload_max_filesize = 2000M
post_max_size = 2000M
max_execution_time = 300
display_errors = Off
The generous upload and memory limits prevent headaches when importing databases or uploading large files in development.
Now go back and add the PHP LoadModule line to httpd.conf as described in the Apache section above, then restart Apache:
brew services restart httpd
To verify PHP is wired up, create a quick test file:
echo "<?php phpinfo();" > /opt/homebrew/var/www/info.php
Visit http://localhost/info.php in your browser. You should see the PHP info page. Delete it when you’re done:
rm /opt/homebrew/var/www/info.php
Step 4: Install and Configure MariaDB
We use MariaDB rather than MySQL — it’s a drop-in replacement, fully compatible with WordPress, and the Homebrew formula is well-maintained:
brew install mariadb
brew services start mariadb
Run the security setup script:
mysql_secure_installation
When prompted:
- Set a root password (we use a simple one for local dev — it’s localhost-only)
- Remove anonymous users: Y
- Disallow remote root login: Y
- Remove test database: Y
- Reload privilege tables: Y
You can connect to verify everything’s working:
mysql -u root -p
Type exit to leave the MySQL prompt.
The newsite script will create a new database for each WordPress install automatically using mysql -uroot -pYOUR_ROOT_PASSWORD.
Step 5: Install mkcert for Trusted Local SSL
This is the piece that makes local development feel like production. mkcert creates a local certificate authority (CA) and generates certificates that your browser actually trusts — no more “Your connection is not private” warnings, no more clicking through security exceptions.
brew install mkcert
mkcert -install
The mkcert -install command adds the local CA to your macOS Keychain, which Chrome and Safari trust immediately. Firefox uses its own certificate store — to make it trust local certs too, install nss first: brew install nss, then re-run mkcert -install. You only do this once per machine.
When you run newsite to create a site, the script will call mkcert sitename.test automatically and drop the certificate files into /opt/homebrew/etc/httpd/certs/. The VirtualHost config for that site will point directly at those files.
If you want to generate a cert manually for any reason:
cd /opt/homebrew/etc/httpd/certs
mkcert sitename.test
This creates sitename.test.pem and sitename.test-key.pem in the current directory.
Step 6: Install WP-CLI
WP-CLI is the command-line interface for WordPress. The newsite script uses it to download WordPress, create the config file, and run the installer — no browser required.
curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
chmod +x wp-cli.phar
sudo mv wp-cli.phar /usr/local/bin/wp
Verify it works:
wp --info
You should see output showing the WP-CLI version and your PHP version.
Step 7: The newsite Script
Here’s where it all comes together. This script does everything that used to take five minutes of clicking and configuration:
- Creates the site directory under
~/Sites/ - Downloads WordPress core via WP-CLI
- Creates a MariaDB database named
wp_sitename - Generates
wp-config.phpwith dev-friendly constants pre-configured - Runs the WordPress installer (no browser needed)
- Generates a trusted SSL certificate via mkcert
- Appends a VirtualHost block (HTTP and HTTPS) to your vhosts file
- Restarts Apache
The result: https://sitename.test is live, trusted, and logged into within seconds of the script finishing.
Create a ~/Sites directory if you don’t have one, then save the script there:
mkdir -p ~/Sites
Create the file ~/Sites/newsite with the script found here.
Make the script executable:
chmod +x ~/Sites/newsite
Add ~/Sites to your PATH so you can call newsite from anywhere. Add this line to ~/.zshrc:
export PATH="$HOME/Sites:$PATH"
Then reload your shell:
source ~/.zshrc
Usage:
newsite myclientsite
For a flat (non-WordPress) site:
newsite myprototype --flat
You can also pass URLs for reference:
newsite myclientsite --live=example.com --staging=stage.example.com --dev=dev.example.com
When the script finishes, https://myclientsite.test is live, trusted, and ready to develop on.
What You’ve Built
You now have a local development stack that:
- Runs on infrastructure you control — Apache config, PHP settings, database — all plain text files you can edit directly
- Has real SSL that your browser trusts without workarounds, so you’re testing against the same protocol you’ll be deploying to
- Creates new sites in one command —
newsite sitenamehandles everything from downloading WordPress to restarting Apache - Mirrors production more closely than a GUI tool ever did
The one-time setup cost was worth it. Every project from here is newsite projectname, coffee, and getting to work.
There’s more you can layer on top of this. I’ve got a newsiteclaude variant that also initializes a git repo and sets up Claude Code with WordPress-specific slash commands. And a local tools dashboard — a WordPress site that tracks all your active local sites, their live/staging URLs, and when they were last accessed. But those are posts for another day.
For now: you’ve got the stack. Go build something.