A long time ago I stumbled on this article:
https://mallinson.ca/osx-web-development/
It’s an excellent guide to set up a robust and useful local development server for web devs, including configuring the .test
TLD for local use. For a long time, this was much more convenient in Apache due to some VirtualDocumentRoot
magic. I’d absolutely recommend reading through it if you’re interested.
As Caddy’s grown and acquired more features (especially {labelN} Placeholders), I wanted to see if this guide could be streamlined and made simpler again using Caddy. So here we go: the Caddy version.
The Perfect Web Development Environment for Your New Mac, with Caddy
Required: Xcode and Homebrew
If you’re writing software on a Mac, you probably have heard of these or have them installed. Start with the Xcode developer command line tools:
xcode-select --install
There will be a dialogue box and may involve a large download. Once that’s done, grab Homebrew, the missing macOS package manager:
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
This one will go a bit faster!
Install software
Now you’ve got Homebrew, grab the packages we want to use:
brew install caddy dnsmasq php
Configure DNS
Lets configure dnsmasq and the system resolver first, so you can visit *.test
websites in your browser and automatically resolve them locally. Start by adding .test
to your dnsmasq config:
echo 'address=/.test/127.0.0.1' > $(brew --prefix)/etc/dnsmasq.conf
Next, tell the macOS system resolver to use the local dnsmasq server for requests for the .test
TLD:
sudo bash -c 'mkdir /etc/resolver && echo "nameserver 127.0.0.1" > /etc/resolver/test'
Fire up dnsmasq:
sudo brew services start dnsmasq
You’ll note that this must be done with sudo
(unlike the other brew
commands in this guide). This is due to the requirement to bind port 53, which unprivileged accounts can’t do; dnsmasq must therefore run as root.
It will also now start automatically at boot.
Configure PHP-FPM
PHP-FPM comes configured out of the box on homebrew to listen on 127.0.0.1:9000. So lets fire it up:
brew services start php
And we’re done here.
Configure Caddy
Unfortunately, Caddy’s brew formula doesn’t define a .plist
file, which is needed to run it as a macOS service. Fortunately, the Caddy repo has one for us to use: https://github.com/mholt/caddy/blob/master/dist/init/mac-launchd/com.caddyserver.web.plist. Download it to your /Library/LaunchDaemons
folder:
sudo curl -o /Library/LaunchDaemons/com.caddyserver.web.plist https://raw.githubusercontent.com/mholt/caddy/master/dist/init/mac-launchd/com.caddyserver.web.plist
This .plist
makes some assumptions about where it’s going to store your Caddyfile, ACME assets, and temp folder, so we’ll have to make sure they’re all ready to go:
sudo mkdir -p /etc/caddy /etc/ssl/caddy /var/log/caddy /usr/local/bin /var/tmp
sudo touch /etc/caddy/Caddyfile
sudo chown -R _www:_www /etc/caddy /etc/ssl/caddy /var/log/caddy
sudo chmod 0750 /etc/ssl/caddy
We’ll also want to make a place for our dev sites to go. I like to use ~/projects/www
. You should use your own directory of choice for the following command.
mkdir -p ~/projects/www
Now lets write the Caddyfile:
sudo nano /etc/caddy/Caddyfile
Add the following configuration, replacing the root
with the directory you chose a moment ago:
http://*.test {
# Don't forget to update this site root!
root /Users/whitestrake/projects/www
fastcgi / localhost:9000 php
rewrite {
to /{label1}{uri} /home{uri} {uri}
}
log stdout
errors stderr
}
And now lets run Caddy (and have it start up at boot):
sudo launchctl load -w /Library/LaunchDaemons/com.caddyserver.web.plist
And we’re done here, too. You can find logs for troubleshooting purposes in /var/log/caddy
.
The end result
Put files in ~/projects/www/client1
. Browse to http://client1.test
. Enjoy the simplicity.
Add a ~/projects/www/home
directory to use as a home or default page; then, if you accidentally browse to http://clientq.test
(or another non-existent directory) instead, you’ll get that home page. Bonus points if you put a landing page here that links to your other projects.
If it can’t find a client1
folder, and there’s no home
folder, it will try to serve a file directly inside ~/projects/www
. You could put a simple index file here, too, if you like.
Optional: Adding a database
This should be as simple as:
brew install mysql
brew services start mysql
Perhaps you prefer postgresql:
brew install postgresql
brew services start postgresql