Problem

Build a vagrant development environment that will serve an rails application that works on multiple subdomains. Moreover that application consists of multiple smaller apps that all are running with a single domain.

Let’s assume we’re using .dev domains, because we’re used to pow. Meaning those routing should work within vagrant machine and same subdomains should work exactly same way on our local machine.

Solution - nginx & dnsmasq

(I assume you’re running some kind of ubuntu-based distro, paths may vary between distros)

Install nginx & dnsmasq. Edit file /etc/dnsmasq.d/dev-tld as follows:

# /etc/dnsmasq.d/dev-tld
local=/dev/
address=/dev/127.0.0.1

Restart dnsmasq service. This rule will forward traffic from .dev domains to your localhost - and this is exactly what we want.

Now let’s configure nginx, following the scenario above we will need configuration for three apps. We will need some kind of server (thin, puma, unicorn, whatever) that will listen on unix socket. In our nginx config / sites-enabled we need:

upstream app1 {
  server unix:/apps/app1/tmp/some_rack_server.sock;
}

server {
  listen 80;
  server_name example.dev;
  root /apps/app1/public;

  location / {
    proxy_pass http://app1;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  }
}

upstream app2 {
  server unix:/apps/app2/tmp/some_rack_server.sock;
}

server {
  listen 80;
  server_name app.example.dev;
  root /apps/app2/public;

  location / {
    proxy_pass http://app2;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  }
}

upstream app3 {
  server unix:/apps/app3/tmp/some_rack_server.sock;
}

server {
  listen 80;
  server_name *.example.dev;
  root /apps/app3/public;

  location / {
    proxy_pass http://app3;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  }
}

Nging will listen on port 80 and forward request according to our needs. Traffic from example.dev will be served by our first app (app1), app.example.dev will be served by app2 and any other traffic (*.example.dev) will go through app3.

So this works, nice & easy. We have 3 rack servers listing on UNIX sockets backed by nginx that routes everything. From vagrant box you can access all .dev domains, so far - very cool.

Next step - accessing .dev subdomains from local machine

It’s nice, but it’s not very useful at this point really - we have no way for accessing example.dev (or any of it’s subdomains) from our local machine. But there is already solution for that - some smart folks already created tools for us that solves this problem.

First we need to route traffic from ninx in vagrant (port 80) to our machine, in Vagrantfile you need:

Vagrant.configure("2") do |config|
  # ...
  config.vm.network :forwarded_port, guest: 80, host: 8080 # or any other port available on your machine

Note: don’t forget to restart your vagrant vm after making changes to Vagrantfile.

Now, depending on what system you are using:

pow/prax will now forward traffic from *.example.dev on your local machine to port 8080 - so basically this will be handled by nginx in vagrant and it just works with our subdomains setup. Magic.

References: Port Proxying (pow), Per-app port forwarding (prax), Vagrant - port forwarding, Rails App With Puma and NGINX