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 .dev
suffixes
# 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[0] : 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 8.8.8.8
;; 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.
Edit /etc/apache2/extra/httpd-vhosts.conf
(your virtual host configuration could be somewhere else) and add these lines below.
Every .dev
domain will point to a folder inside ~/Sites
, with the same name, but without the extension.
For example, myapp.dev
will point to ~/Sites/myapp
.
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
.
Leave a Reply