Separate your nginx web & puma application servers

Author: Stuart Maynes

Generally speaking, if you don’t have the luxury of using a PaaS such as Heroku and you’re trying to deploy a Rails app, there’s a certain amount of manual configuration with nginx using reverse proxying you have to do. Every single tutorial I’ve found online about how to do this suggest that you install nginx on to the same server as your application code and use a unix socket to reverse proxy.

There’s nothing inherently wrong with this process. It works. But for a recent project, one of our regulatory constraints was that there had to be a server between our database and the web facing server. It made sense for this to be our application server but for a full separation between the web server and application server.

Here’s what we have right now:

original_puma_rails (2)

As you can see, both the web server (nginx) and the application server (puma) reside on the same logical VM. The proxying from nginx to puma (and back again) is done using a unix socket using the nginx upstream feature.

So as long as your virtual machine has access to all of the other resources required without needing to traverse private networks, you’ve got no issues. The issue occurs when you have a restriction that says no single server can face both the public internet and a certain restrictive network. In the above example, the single virtual machine is facing both the public internet and the external network and this is not allowed. Let’s add a new virtual machine to house our puma workers and act as a proxy. Our network topology now looks a bit like this.

original_puma_rails (3)

Now our web server and our multi-threaded application server are located on physically separated virtual machines with restrictive firewall rules between them. This means that no single user request has access to the private network except via our API. Also, this means that the potential routes in to the database are much reduced. We can change our nginx config to point to a puma worker on a totally different instance rather than a unix socket on the same box.

This separates our concerns nicely, keeps our regulatory issues in check and makes horizontal scaling (adding more puma workers) in the future, much easier.