Fork me on GitHub

Setting up Wildcard Subdomains on OS X 10.6

The new trend in web applications is to have your customers name or account code in the URL as a subdomain. Like in 37 Signals Campfire web application the URLs look like: myaccount.campfirenow.com. This is pretty cool because is gives your customers a little bit of customization and it is one less thing that you have to maintain in your session state store. It makes development a real beating though because most of the time you have to maintain a set of accounts in your /etc/hosts file. I’m going to walk you through getting all the subdomains you want with a little upfront configuration.

I know that the Passenger PrefPane and other solutions like Pow handle this for you, but I prefer to run the same server for development that I will be running in production and I currently use Unicorn on all my production apps. I won't go into setting up Unicorn, but I just set up the standard Nginx reverse proxy to Unicorn configuration.

Requirements

I’m tailoring my examples to OS X since that is the platform I’m running on. However, they should work on any *nix based system with some massaging.

Install homebrew and Nginx

Homebrew is a great package management system and you should definitely be using it. Like…right now. Don’t wait and tell yourself that you’ll get to it. You also said that you would get to that thing that you’ve been meaning to do and where did that get ya? Fine. Download and install it later, but you’re really missing out.

Once you have it installed you can run brew install nginx and that will download and install Nginx. Make sure to install the launch plist because then OS X will automatically start Nginx on boot. One thing to note is that if you decide to use Nginx then you should deactivate Apache. To deactivate Apache go to System Settings > Sharing > Uncheck “Web Sharing”.

When editing the configuration file for Nginx you have add a reference to your website:

server {
  listen 80;
  server_name *.mycoolapp.dev mycoolapp.dev;

  location / {
    root /path/to/your/app;
  }
}

The *.mycoolapp.dev will tell Nginx to accept a wildcard subdomain for that website. See your particular framework/vendor for specific instructions on setting up Nginx.

Set up BIND

BIND is the little piece of software the runs the internet. BIND is a DNS server and it works in a distributed fashion. It’s really fascinating how DNS works but that’s outside the scope of this post. There are a few steps to get BIND set up to serve domains and they are crucial.

  1. sudo -s
    This will let you run a shell as root. I suggest doing this because most of the next commands you will execute need to be on privileged files and directories.
  2. Create the rndc.conf and rndc.key
    You can easily do this by running the following commands: rndc-confgen > /etc/rndc.conf
    head -n5 /etc/rndc.conf |tail -n4 > /etc/rndc.key
  3. Add the zone to /etc/named.conf I am going to use .dev for all my examples. I have used .local on my machine but I have found that this can be troublesome with the OS X default Multicast being .local. You can make this whatever top level domain you would like (.dev, .rails, .php, etc.) In /etc/named.conf add:
     zone "dev" IN {
       type master;
       file "dev.zone";
       allow-update { none; };
     };
    
  4. Create /var/named/dev.zone
    Use this template for your zone file:
     $TTL	60
     $ORIGIN dev.
     @			1D IN SOA	localhost. root.localhost. (
               42		; serial (d. adams)
               3H		; refresh
               15M		; retry
               1W		; expiry
               1D )		; minimum
    
           1D IN NS	localhost
           1D IN A		127.0.0.1
     localhost.dev. 60 IN A 127.0.0.1
     *.dev. 60 IN A 127.0.0.1
    

    The only thing you should have to change is the serial (42) to any random number and if you decided to go with something else other than .dev: everthing that has .dev in it. Note that the dots (.) after the dev entries are intentional and required.

  5. launchctl load -w /System/Library/LaunchDaemons/org.isc.named.plist
    This will cause named to load everytime the system restarts.
  6. named
    This loads named now. It doesn’t give you any input if it fails to load so I recommend running named -g first to make sure it parses you zones and configs. -g will tell named to run in the forground and write all logging to STDOUT. Once you’ve verified that it starts then Ctrl-c and run named as normal.

Create the resolver

Next we have to create an entry in the /etc/resolver folder. This folder is checked every time a DNS entry is looked up. If a file exists here then it will use the special resolv configuration to look up DNS names. Here you can specify custom domains, search orders, and in this case nameservers. So let’s get started.

Create a file that is the same as the name of your app. I.e. if your app is mycoolapp.com like from above create the file mycoolapp.dev and inside it put:

nameserver 127.0.0.1

Now when you go to http://mycoolapp.dev you should get your app, but you can also go to http://www.mycoolapp.dev or http://theskyisblue.mycoolapp.dev and still go to your site.

Log out of the root shell and you are done.

New sites

When adding new sites you will need to create a new /etc/resolver file and add an entry in Nginx, but that only needs to be done per new app. Once you’ve done that you can call it with whatever subdomains that you wish. You can have Nginx reload it’s config files by calling nginx -s reload or killall -HUP nginx.

Essentially you have now set up your own local DNS server with your custom zone file so you can actually use your own DNS server to resolve host names. I sometimes have to VPN into work so I need to use the DNS servers that are provided when I log onto the VPN, so that is why I chose this method.

Enjoy!

Update 2011/08/28: I upgraded to Lion yesterday and I found that for some reason the DNS order is not being respected in OS X 10.7 Lion so the /etc/resolver files. The current workaround is to add 127.0.0.1 to the list of DNS servers in Network.

Comments

Post New Comment »

Nobody has commented on this yet.