As Web developers, one of the first things we do when starting a new project, is creating a development environment as similar as possible to the final destination of our code, AKA Production.
One of the most common requirements is running your apps in their own private domain and one of the most common solution is editing the
/etc/hosts file, every time you need to add a new domain.
It’s no secret that this process can quickly become very tedious.
If you work on OS X, you probably have heard of Pow, but if you only need the domain resolution or don’t work with Ruby, it is probaly overkill to install a full featured application server just to create some dev domain.
MAMP Pro offers domain resolution too, but it’s not free.
Luckily, there is another solution: running your own instance of a DNS server.
As over-the-top as it may sound, DNS is generally a very lightweight service, you’ll be making just a few thousands queries a day, not a big deal at all, and it can have a beneficial effect on latency, speeding up requests by caching them locally, without hitting the network.
What you need it’s a copy of dnsmasq and stop worrying and love the shell.
First of all install dnsmasq and put it in autostart
brew install dnsmasq sudo cp -v $(brew --prefix dnsmasq)/*.plist /Library/LaunchDaemons
Configure the dns intance
cat <<- EOF > $(brew --prefix)/etc/dnsmasq.conf # IPV4 address=/dev/127.0.0.1 # IPV6 or virtual hosts in Maverick won't work address=/dev/::1 listen-address=127.0.0.1 EOF
Then configure the resolvers for all domains and create the brand new one for the
# it might already exists sudo mkdir -p /etc/resolver # .dev domains sudo bash -c 'echo "nameserver 127.0.0.1" > /etc/resolver/dev' # universal catcher sudo bash -c 'echo "nameserver 127.0.0.1" > /etc/resolver/catchall' sudo bash -c 'echo "domain ." >> /etc/resolver/catchall'
You need to set
localhost as the main DNS server, unfortunately you can’t automatically prepend
localhost to the list of DNS your DHCP assigned to you.
You have to change it manually and put it on top.
networksetup -setdnsservers Ethernet 127.0.0.1 networksetup -setdnsservers Wi-Fi 127.0.0.1
If everything’s ok, running
scutil --dns should return something like this
resolver #8 domain : dev nameserver : 127.0.0.1 flags : Request A records, Request AAAA records reach : Reachable,Local Address
Now you can start dnsmasq
# start dnsmasq sudo launchctl -w load /Library/LaunchDaemons/homebrew.mxcl.dnsmasq.plist # make some tests $ ping -c 1 anyhostname.dev PING anyhostname.dev (127.0.0.1): 56 data bytes 64 bytes from 127.0.0.1: icmp_seq=0 ttl=64 time=0.058 ms $ dig anyhostname.dev ;; ANSWER SECTION: anyhostname.dev. 0 IN A 127.0.0.1 ;; Query time: 3 msec ;; SERVER: 127.0.0.1#53(127.0.0.1) $ dig google-public-dns-a.google.com @127.0.0.1 ;; ANSWER SECTION: google-public-dns-a.google.com. 25451 IN A 18.104.22.168 ;; Query time: 30 msec ;; SERVER: 127.0.0.1#53(127.0.0.1) # cache will speedup subsequent DNS queries $ dig google-public-dns-a.google.com @127.0.0.1 ;; Query time: 0 msec
Bonus: if you run Apache on your dev machine, you can easily setup the one virtual host to rule them all through mass virtual hosting.
/etc/apache2/extra/httpd-vhosts.conf (your virtual host configuration could be somewhere else) and add these lines below.
.dev domain will point to a folder inside
~/Sites, with the same name, but without the extension.
myapp.dev will point to
You can of course customize your own environment: you could, for example, create a folder to hold all the virtual hosts created with this tecnique, just like Pow does, or if you are a Pow user already, reuse
~/.pow folder as a container.
<VirtualHost *:80> ServerAdmin you@localhost ServerName anyhostname.dev ServerAlias *.dev UseCanonicalName Off # %1.0 is the domain without the extension, in this case # everything before .dev # more info: http://httpd.apache.org/docs/2.4/mod/mod_vhost_alias.html VirtualDocumentRoot /Users/<your_login>/Sites/%1.0 ErrorLog "/var/log/apache2/dev_hosts_error_log" CustomLog "/var/log/apache2/dev_hosts_access_log" common <Directory /Users/<your_login>/Sites/*> AllowOverride All </Directory> </VirtualHost>
You can finally reload Apache configuration with
sudo apachectl graceful.