Apache, PHP, MySQL, .dev on OS X Lion (with a minimum of pain)

Scenario: You’re a PHP developer. (Hey, someone has to write the good PHP code.) You use a mac (and, let’s assume, you’re on Lion). You want to have a lot of sites running locally, with minimum effort. Sure, you could use MAMP, but that’s a chunk of manual configuration every time you want to create a new domain. It’s not much, but it adds up!

Here’s a big pile of steps to get OS X, PHP, Apache, Percona’s MySQL server, and so forth all working smoothly.

Package Management

First, install homebrew. It makes things much easier. Then update it:

brew update

Database

You could install MySQL, or you could install Percona Server instead. It’s transparent, it’s painless, and Percona is (in my experience) somewhere around 30% faster on my MacBook Pro than bare MySQL is. So (assuming you don’t have MySQL installed already):

brew install percona-server
# Go to lunch, it takes a bit.
brew link percona-server
unset TMPDIR
mysql_install_db --verbose --user=`whoami` --basedir="$(brew --prefix percona-server)" --datadir=/usr/local/var/percona --tmpdir=/tmp
mkdir -p ~/Library/LaunchAgents
cp /usr/local/opt/percona-server/homebrew.mxcl.percona-server.plist ~/Library/LaunchAgents/
launchctl load -w ~/Library/LaunchAgents/homebrew.mxcl.percona-server.plist

Then you should be able to connect with mysql -uroot.

Apache and PHP

OS X ships with PHP by default (5.3.10, as of the time of writing), but does not enable it for Apache by default. So, here’s a patch to fix that:

Download and apply with:

cd ~/Downloads
curl -LO https://gist.github.com/raw/2931575/a9e424d70fcb71b65c788f71767824c3ec2f605d/httpd.conf.diff
cd /etc/apache2
sudo patch -p2 < ~/Downloads/httpd.conf.diff

Then create a personal configuration file, like so:

cd ~/Downloads
curl -LO https://gist.github.com/raw/2931635/1311ed82da761b1db7a2cc7127f47dcd6510fa90/YOURNAME.conf
sed -e "s/YOURNAME/$(whoami)/g" YOURNAME.conf > $(whoami).conf
sudo mv $(whoami).conf /etc/apache2/users

Generate an SSL certificate, following the directions from superuser.com. The configuration file already has the SSL directives in it; you should put the resuling server.crt and server.key files in /etc/apache2, or edit the configuration file appropriately.

(The SSL configuration, at this time, is set up only for domain.dev use, not domain.10.0.0.10.nip.io use. You can switch out the VirtualDocumentRoot line in the *:443 VirtualHost block for the one in the ServerName example.127.0.0.1.nip.io block if you need to use SSL for mobile testing.)

Finally, start up Apache – open up the Sharing preference pane and check (or uncheck and recheck) Web Sharing.

DNS

The .local domain is special in OS X, and you should avoid it. Instead, set up a .dev wildcard domain with dnsmasq:

brew install dnsmasq
cat <<EOT > /usr/local/etc/dnsmasq.conf
address=/.dev/127.0.0.1
rebind-domain-ok=/xip.io/nip.io/
EOT
sudo cp /usr/local/opt/dnsmasq/homebrew.mxcl.dnsmasq.plist /Library/LaunchDaemons
sudo launchctl load -w /Library/LaunchDaemons/homebrew.mxcl.dnsmasq.plist

dnsmasq will still let you edit /etc/hosts to make changes – in fact, it works better, since it picks up changes faster than the OS would otherwise.

Now you need to tell OS X to use dnsmasq as one of its domain nameservers. You could just go into the Network preference pane and add it to your DNS servers, but if you don’t set all of your DNS servers, you can’t resolve things properly, and if you do set all of them, then you have to reset them if you go somewhere with a captive portal wifi (many coffee shops and hospitals, for instance) – it’s a pain. A little more work on the front end removes this problem basically entirely.

You’ll need these two files:

Download and extract, then:

mv setdns.sh /usr/local/bin
chmod a+x /usr/local/bin/setdns.sh
sudo cp com.zizproductions.setdns.plist /Library/LaunchDaemons
sudo launchctl load -w /Library/LaunchDaemons/com.zizproductions.setdns.plist

This will watch for changes in your network configuration and add 127.0.0.1 (your dnsmasq server) to the list of nameservers if it needs to.

One more thing. OS X will refuse to do DNS lookups if you’re not connected to a network (under some circumstances). There’s a simple way around that, in two steps:

First, install VirtualBox. Create a new virtual machine, and give it a Host-only network adapter. (You can use the FreeDos image, if you don’t actually need a virtual machine for anything. It’s small.) Start the virtual machine at least once.

You can do this from the commandline with this snippet (after VirtualBox is installed):

(ifconfig | grep -s vboxnet) || VBoxManage hostonlyif create ipconfig vboxnet0 --ip 192.168.56.1 --netmask 255.255.255.0

This prevents OS X from disabling normal DNS resolution when you’re not connected to a network.

Second, create a ‘dev’ domain resolver entry:

sudo mkdir -p /etc/resolver
sudo tee /etc/resolver/dev <<EOT
nameserver 127.0.0.1
domain dev
search_order 1
EOT

This tells OS X that it can always ask localhost (your dnsmasq server) for domain resolution for .dev domains when it doesn’t have other DNS servers.
Now if you type ifconfig in a terminal, one of the lines should start with vboxnet0: – if it does, great! OS X will happily do DNS lookups even if you’re on an airplane, so you can keep using your .dev domains wherever you are.

Creating new .dev domains

It’s easy.

mkdir ~/Sites/testdomain.dev

Yes, that’s all. Now you can copy your html5boilerplate in, or check out WordPress, or whatever you need to do, and http://testdomain.dev/ should take you right there. (Remember, if you’re in Chrome, you’ll need to type http:// explicitly – just typing in testdomain.dev will do a google search.)

And that’s it!

Edits

Newer versions of homebrew link the current version of a package into /usr/local/opt, avoiding the need for the /usr/local/Cellar/.../version/ path.

Fixed an issue with local DNS resolution not working when disconnected from a network.

Legalese

All instructions, commands, source, and configuration files provided in this post and authored by me are available for use under the terms of the MIT license, including the disclaimer of warranty.

Further Disclaimer: These instructions are from notes and my own installation journal. Your mileage may vary.

3 Responses