Setting up a new local host is a familiar task to most web developers, updating
/etc/hosts to include yet another local domain, and potentially updating Apache config to recognise the new server. This process can be made much easier in several ways.
The easiest way to not have to deal with basic Apache configuration again is to put all your local domains in one basket; in my case
~/Sites. We can then add the following to our Apache config…
NameVirtualHost *:80 <VirtualHost *:80> VirtualDocumentRoot /Users/username/Sites/%0 ServerName localhost <Directory /Users/username/sites/> Order deny,allow Allow from all Options Indexes MultiViews FollowSymLinks </Directory> </VirtualHost>
This tells Apache to look for a directory in
/Users/username/Sites matching the incoming domain name. So, for example, you would put the document root for
/Users/username/Sites/myproject.dev, and serve all your files from there (though personally I tend to symlink them to a separate local repository instead).
I put this in
/extras/httpd-sites.conf (along with various other configurations for slightly more advanced local development —
wsgi etc), and
include it at the bottom of
httpd.conf, but your setup may be different, of course.
With that done, Apache now knows where to find our document roots, but our browsers don’t know where to find Apache. There’s nothing in the system that actually says “this server is actually on my machine”. If you wanted to go full-on you could configure your DNS to direct all
local.* traffic to
127.0.0.1 with a tool like Dnsmasq, but most developers seem to be much happier editing their
Knowing where all of our document roots live makes this process relatively easy to automate.
launchd is a service-management framework used to start, stop and manage daemon, applications, processes, and scripts in Apple OS X environments, and it’s exactly what we need to keep an our on our
The two important bits we need to know about
launchctl configuration are
Program key maps to the name of the program to run whenever the job is started. The
QueueDirectories key provides a list of directories to watch, and causes the job to be started if any one of the listed paths are modified. Applying these properties gives us something like this.
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Label</key> <string>net.andrewhayward.generate-hosts</string> <key>Program</key> <string>/path/to/script</string> <key>QueueDirectories</key> <array> <string>/Users/username/Sites</string> </array> </dict> </plist>
This essentially says that whenever
/Users/username/Sites is modified, run the script at
/path/to/script. Because this requires administrative privileges, and (theoretically at least) to run without a user being logged in, we need save this file in
/Library/LaunchDaemons. Having done that, we just load it into
> sudo launchctl load /Library/LaunchDaemons/net.andrewhayward.generate-hosts.plist
Of course, that’s all well and good, but we need something to actually put at
/path/to/script. You can either write your own
hosts processor, or you can save time and use mine instead. It’s relatively self-explanatory — it generates a
hosts configuration file — and it’s probably riddled with bugs, but it seems to do the job. I won’t go over every option
generate-hosts.py offers (you can read them yourself!), but in brief:
-usays that we should update hosts that already exist
-ssays that we should stay silent
-wsays that we should write the file back to where we found it
-dsays that we should read folders from the specified target directory
If you do use mine, we’ll need to modify our property list a little. Rather than use the
Program key, we instead use
ProgramArguments, which is essentially a slightly more advanced
Program that takes arguments.
<key>ProgramArguments</key> <array> <string>/path/to/generate-hosts.py</string> <string>-u</string> <string>-s</string> <string>-w</string> <string>-d /Users/andrew/Sites</string> </array>
So that’s it, really. We set up Apache to check for anything thrown at it in a single location, we set up the operating system to keep an eye on that location and to let us know when it changes, and we wrote a script that reads a directory for folder names and generates a
hosts file from them.
All of this means that you need never think about setting up local hosts again. Whenever you make a new folder in
~/Sites, your computer will automatically map it to a local host, and know to use it as a document root for that new server.
Even if you only set up a new local host once a year, you can easily afford the time it will take you to set all of this up!