Pow is a dead-simple Rack server for OS X. Easy setup, easy restarting, easy development.

Nginx is a lightweight HTTP proxy server. Easy on resources, and great at what it does.

Suppose you want to run Nginx to serve some static assets off your development machine, for demonstration purposes. But you also want to use Pow for your Rails and Rack work. If you have already tried to run both, you may have noticed that Pow takes port 80 using a ipfw rule, and no requests make it to Nginx. There’s a fairly easy solution.

What we are looking to have is:

  • http://localhost/ to Nginx
  • http://myapp.dev/ to Pow
  • http://dev.example.com/ to development machine to Nginx
  • http://myapp.dev.example.com/ to development machine to Pow

Those last two are only if you want external access to these services. They will require you to have your DNS setup properly to redirect dev.example.com and *.dev.example.com to your development machine (substituting your own domain of course).

From the Top

I’m going to assume you’re running Snow Leopard (10.6) or newer — Pow doesn’t run on older OS X versions. You should also have Nginx and Pow installed. I’m using Pow 0.3.2 and Nginx 1.0.14 on Mac OS 10.7.3.

Pow uses a resolver and an ipfw rule to redirect requests from *.dev to your Rack app. Take a look:

  $ cat /etc/resolver/dev
  # Lovingly generated by Pow
  nameserver 127.0.0.1
  port 20560

  $ sudo ipfw list
  00100 0 0 fwd 127.0.0.1,20559 tcp from any to me dst-port 80
  65535 allow ip from any to any

That first rule, 00100, redirects any requests to port 80 to Pow at 127.0.0.1:20559. We can’t just remove the rule, as Pow will re-add it with a Launch Daemon on startup. And we can’t just remove the Launch Daemon, as pow needs it to run. (I tried). Instead, let’s let Pow take over a port we don’t care about.

  $ echo "export POW_DST_PORT=19999" >> ~/.powconfig

And reinstall pow to pick up the new configuration:

  $ curl get.pow.cx | sh

With that gone, if you try to load http://localhost/ you should get Nginx’s welcome page (if it is running)! One caveat: we can’t access our Rack servers from the http://*.dev/ domain anymore. This is where Nginx’s config comes into play.

Step into Nginx

Open Nginx’s config (/usr/local/etc/nginx/nginx.conf if you’re using Homebrew) in your favourite editor, and we will add some new server blocks.

Look for the existing server block, and adjust it’s server_name to include your external address.

server {
  listen       80;
  server_name  localhost dev.example.com;

  #charset koi8-r;
  ...

If you save it and reload Nginx now (sudo nginx -s reload), you should see the Nginx welcome page at your domain. Nice!

Back to the conf file, add two new server blocks above the existing server directive.

server {
  listen 80;
  server_name *.dev;
  location / {
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_redirect off;
    proxy_pass http://localhost:20559;
  }
}

server {
  listen 80;
  server_name myapp.dev.example.com;
  location / {
    proxy_set_header Host "myapp.dev"; # Host for Pow
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_redirect off;
    proxy_pass http://localhost:20559;
  }
}

server {
    listen       80;
    server_name  localhost dev.example.com;
    ...

Save and reload Nginx. localhost now goes to Nginx. myapp.dev now goes to Pow. And externally, dev.example.com goes to Nginx, and myapp.dev.example.com goes to Pow.