Setting up a local DNS server on OSX

 Nov 26, 2014

I do a lot of development, and pretty much run everything locally using Vagrant. For basic stuff http://localhost:3000 is fine, but what happens when I need lots of sub domains or multiple VM’s running… here’s my journey to solving this problem and some of the suggestions I came across.

Hosts File

The most basic solution that is available is to edit your computers local hosts file. DNS will respect any changes in this file above remote addresses. You can use this to add custom domains each pointing to 127.0.0.1 and they will resolve locally.

Open the file for edit with super user privileges.

$ sudo vim /etc/hosts

You should get something similar to the below

##
# Host Database
#
# localhost is used to configure the loopback interface
# when the system is booting.  Do not change this entry.
##
127.0.0.1       localhost
255.255.255.255 broadcasthost
::1             localhost
fe80::1%lo0     localhost

Don’t change the default entries, but you can add as many additional items as you want. Add each entry on a new row (you can use comma separated lists, but I prefer one per row for clarity) in the format

ipaddress domain

For example:

127.0.0.1   myblog.dev

Then save the file. Now if you try to reach that domain, it will resolve to localhost and you can use it in your app.

$ ping myblog.dev
PING myblog.dev (127.0.0.1): 56 data bytes
64 bytes from 127.0.0.1: icmp_seq=0 ttl=64 time=0.041 ms
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.089 ms
64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.047 ms

The downside to this approach is that you need to edit the hosts file for each domain you want to use, including every subdomain you might want. You can’t add wildcard domains to the hosts file.

Remote DNS for 127.0.0.1

Another approach that I’ve seen and tried is to have a real DNS server point at 127.0.0.1. There are a number of domains already set up that offer this, or you can do it yourself if you have a domain/DNS provider.

You can even set up a wildcard DNS record so that all subdomains for a given domain, point to the same IP address and resolve. This will allow you to use your own domain for development.

I’m not posting instructions for this approach as it will differ massively depending on who you host with, but essentially you need to add an A record for *.yourdomain.tld pointing at 127.0.0.1

The big downside to this is that you have to be online for it to work. If you are working off-line for any reason, no DNS.

Local DNS

This is my preferred solution. Run a local domain name server on your computer that can resolve all requests for a given top level domain to the localhost, and forward everything else to the Internet as normal.

As with everything I write about this will be specifically aimed at Mac OSX, although you can do the same on Linux as dnsmasq is available. For Windows users I can’t recommend or suggest a service but the principles will be the same.

First off, you will need Homebrew the package manager for OSX. I won’t retread installation instructions for it, you can get the most current ones from the Homebrew site.

Then follow these steps, which set up dnsmasq so that all domains with a TLD of .loc will point to the localhost

$ brew install dnsmasq
$ echo 'address=/.loc/127.0.0.1' > /usr/local/etc/dnsmasq.conf
$ sudo cp -v /usr/local/homebrew.mxcl.dnsmasq.plist /Library/LaunchDaemons
$ sudo launchctl load -w /Library/LaunchDaemons/homebrew.mxcl.dnsmasq.plist

This will install dnsmasq via Homebrew, add the configure value for pointing all .loc domains to the localhost, add it to the startup process of OS X so dnsmasq is always running, and finally start the service.

You can manually start and stop the service with the following commands

$ sudo launchctl stop homebrew.mxcl.dnsmasq
$ sudo launchctl start homebrew.mxcl.dnsmasq

Generally, I will then add 127.0.0.1 as the first domain name server in my network settings. You can do this in System Preferences -> Network -> Advanced -> DNS

At this point you should be able to use ping or nslookup to check everything is working and you haven’t broken your normal DNS

$ ping google.com
PING google.com (74.125.230.105): 56 data bytes
64 bytes from 74.125.230.105: icmp_seq=0 ttl=56 time=5.460 ms
64 bytes from 74.125.230.105: icmp_seq=1 ttl=56 time=6.809 ms

$ ping this.is.a.test.loc
PING this.is.a.test.loc (127.0.0.1): 56 data bytes
64 bytes from 127.0.0.1: icmp_seq=0 ttl=64 time=0.036 ms
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.068 ms

There you go. A local DNS that will resolve all your local domain needs to 127.0.0.1.