Tuesday, January 31, 2012

My twelve-factor app development environment (Unicorn and Pound)

I've been hugely influenced by the twelve-factor app manifesto written by Heroku. I really like building apps on Heroku and think it's pretty great for prototyping things, because it totally abstracts the need to think about devops in the early stages of a project. I don't think Heroku is a panacea (I don't have experience running something large on it), but it's great for getting something going quickly.

That manifesto illustrates how you end up building your app if you launch it on Heroku. I've found the  principles greatly increase my productivity no matter what platforms I'm using. One thing that did take me a little while to figure out: most of my apps are SSL-only, and I always want to interact with them over SSL in my development environment because odd things can creep up in production if you never test SSL locally. (Most of the advice that Rails blogs give always point you towards shutting off SSL in development mode which seems crazy to me)

At first I couldn't quite figure out how to make SSL work while complying with factor 7, Port Binding. I was still using Phusion Passenger with SSL configured as I described in this very old article (which still gets a lot of traffic).

Now here's what I do. I run the Pound reverse proxy using this configuration:

# http://lvh.me
ListenHTTP
  Address 127.0.0.1
  Port    80

  Service
    BackEnd
      Address 127.0.0.1
      Port    8080
    End
  End
End

# https://lvh.me
ListenHTTPS
  Address 127.0.0.1
  Port    443
  Cert    "/usr/local/etc/pound.pem"
  AddHeader "X_FORWARDED_PROTO: https"

  Service
    BackEnd
      Address 127.0.0.1
      Port    8080
    End
  End
End

The /usr/local/etc/pound.pem file is a locally-generated, locally-signed SSL certificate.

When I want to view an SSL-only app in my local browser, I just start Unicorn (my app container of choice) which defaults to port 8080. Then I visit https://lvh.me which is helpfully set to the loopback address 127.0.01. That hits Pound, which terminates the SSL connection (after a warning about the self-signed certificate), and proxies the web request to Unicorn.

This is very similar to what happens when the app runs in production on Heroku's systems, except that they use a Procfile in the root directory of the app to configure Unicorn. Per factor 7, it allows Heroku to bind my Unicorn instance to any arbitrary port:

web: bundle exec unicorn -p $PORT -c config/unicorn.rb

7 comments:

Justin Wiley said...

Nice post, hope to get my app running SSL locally via this method

Hernán said...

Thanks for this post! I got it working perfectly on my machine.

I used this tutorial to create the certificates, and then I did cat server.key server.crt > pound.pem to get the PEM file used by Pound.

One question arises: How can I give other machines on my network access to the server? Opening 192.168.1.110:3000 (my dev machine running Webrick on port 3000) worked before.
After activating SSL and Pound, accessing 192.168.1.110 via http or https gives "Unable to connect" and accessing 192.168.1.110:8080 gives "Secure Connection Failed".

Any tips? Thanks!

Hernán said...

Also, I added xHTTP 1 as an option within each ListenHTTP block to allow PUT and DELETE HTTP requests.

Mike Subelsky said...

Hernán: sounds like the machine running Pound might have a firewall blocking port 80 and port 443. Good tip on xHTTP 1!

Andres Ramirez said...

How could I use a setup like this to test Cucumber features? Thanks

Hernán said...

Ok, so I solved my problem with accessing my server from other machines:
In the Address setting under ListenHTTP and ListenHTTPS, I have to put my machine's network address (ie. 192.168.1.102).

Uri Klar said...
This comment has been removed by the author.