Four Kitchens
Insights

Local development with Apache VHosts and Dnsmasq

3 Min. ReadDevelopment

There are plenty of tools for doing local development, however some of them place some inconvenient limits on what you can do. If you aren’t afraid to edit a few config files then you can have an incredibly flexible local development in just a few steps.

At the end of this tutorial you’ll be able to create a folder, following the simple ~/Sites/MYFOLDERNAME.dev pattern, and have it just work in the browser.

Setting up Apache

OSX ships with Apache pre-installed, but in it’s default configuration it’s not very useful. Additionally at some point in the near past, Apple removed the ability to toggle Apache’s state from System Preferences, we’ll be solving that as well.

These instructions assume that you’ve never made any changes to the Apache configuration. If you have, your mileage may vary.

Create a Sites directory in your home directory if you don’t already have one.

[ ! -d ~/Sites ] && mkdir ~/Sites

Create a blank httpd-vhosts.conf file, we’ll come back to this in a bit.

touch ~/Sites/httpd-vhosts.conf

Create a symbolic link for our previously created conf file.

sudo ln -s ~/Sites/httpd-vhosts.conf /etc/apache2/other

Setup the logs directory, and set it’s permissions. These are probably a little too permissive, feel free to adjust as needed.

mkdir ~/Sites/logs && chmod 0777 ~/Sites/logs

Add the following to the previously created httpd-vhosts.conf file. Make sure to replace USERNAME with your own user name.

Listen 80

# Use name-based virtual hosting.
NameVirtualHost *:80
UseCanonicalName Off

LogFormat "%V %h %l %u %t "%r" %s %b "%{Referer}i" "%{User-Agent}i"" dynamic_vhosts
CustomLog /var/log/apache2/access_log dynamic_vhosts

<VirtualHost *:80>
  ServerName localhost
  VirtualDocumentRoot /Users/USERNAME/Sites/%0

  <Directory "/Users/USERNAME/Sites">
    Options Indexes FollowSymLinks MultiViews
    AllowOverride All
    Order allow,deny
    Allow from all
  </Directory>
</VirtualHost>

Obviously feel free to change around the log format to best fit your application. The key to this working is the %0 at the end of VirtualDocumentRoot, acting as a placeholder for the domains you’ll be using locally.

At this point you should restart Apache to the previous configuration changes will be applied.

sudo apachectl stop && sudo apachectl start

Setting up Dnsmasq

You could potentially stop without executing any of the following steps and have dynamic vhosts. However, every time you created a new domain you’ll need to update your hosts file. That’s no fun. Installing Dnsmasq is relatively straightforward.

Install Dnsmasq.

brew install dnsmasq

Copy the default configuration to its new home.

cp /usr/local/opt/dnsmasq/dnsmasq.conf.example /usr/local/etc/dnsmasq.conf

We’re going to be using the .dev domain for all local domains. Add the configuration to the newly created dnsmasq.conf file. If you want the .dev domains to point to a different address (i.e. A Vagrant instance), other than your local, just change 127.0.0.1 to your preferred location.

echo 'address=/.dev/127.0.0.1' > $(brew --prefix)/etc/dnsmasq.conf

Now we need a place to let Dnsmasq know about our new resolvers. Additionally we’ll add our first resolver, .dev. Again, don’t forget to change 127.0.0.1 to the address you specified in the previous step if you intend to use something different.

sudo mkdir -v /etc/resolver && sudo bash -c 'echo "nameserver 127.0.0.1" > /etc/resolver/dev'

Dnsmasq needs to start, and we want it to start at startup.

sudo cp -fv /usr/local/opt/dnsmasq/*.plist /Library/LaunchDaemons
sudo launchctl load /Library/LaunchDaemons/homebrew.mxcl.dnsmasq.plist

If you need to restart Dnsmasq run the following.

sudo launchctl unload /Library/LaunchDaemons/homebrew.mxcl.dnsmasq.plist && sudo launchctl load /Library/LaunchDaemons/homebrew.mxcl.dnsmasq.plist

Great! You can check to see that resolver is working with scutil --dns. You should see an additional resolver at the bottom, your exact number might vary.

resolver #8
  domain   : dev
  nameserver[0] : 127.0.0.1

Additionally you can ping any domain ending in .dev and it should resolve to 127.0.0.1

~ : ping -c 1 test.dev
PING test.dev (127.0.0.1): 56 data bytes
64 bytes from 127.0.0.1: icmp_seq=0 ttl=64 time=0.042 ms

--- test.dev ping statistics ---
1 packets transmitted, 1 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 0.042/0.042/0.042/0.000 ms

Final setup

You can now create any series of folders ending in .dev in your sites directory. The contents of those folders will be served on the corresponding domain. If you have a folder called test.dev then your domain will also be test.dev

Enjoy your new automatic local development environment.