Running Rails with Puma on Heroku

02 July 2013 Puma 2 Updates:

I’ve been playing around with Heroku a lot lately and noticed they recommend using Thin for your production server when running Rails. In recent months I’ve grown attached to Puma (for no qualified reasons) and wanted to see if I can get a Rails app running on Heroku’s cedar stack with Puma instead. It turned out to be really, really simple.

It's important to keep in mind that Puma is threaded; if your code is not threadsafe, or your not using config.threadsafe!, you may have a bad time (Rails 4 is threadsafe by default).

Create a new Rails 3.x app

First create a new rails app, heroku app, and initialize a git repo:

$ rails new heroku-puma
  create 
  ...
$ cd heroku-puma
$ heroku create
  Creating high-day-4093... done, stack is cedar
  http://high-day-4093.herokuapp.com/ | git@heroku.com:high-day-4093.git
$ git init
$ git remote add heroku git@heroku.com:high-day-4093.git

Launch your favorite editor and make some standard adjustments in your

1
Gemfile
, just as a demonstration:

...
gem 'sqlite3'
...

becomes

...
gem 'thin'
...

Deploy with Thin, just for fun

Back in the terminal, add your files to the git repo and make your first commit, then push it up to Heroku:

$ git add .
$ git commit -m "Initial import, using Thin server"
$ git push heroku master
-----> Heroku receiving push
-----> Ruby/Rails app detected
-----> Installing dependencies using Bundler version 1.2.0.pre
Running: bundle install --without development:test --path vendor/bundle --binstubs bin/ --deployment
Fetching gem metadata from https://rubygems.org/.......
...
Installing thin (1.4.1) with native extensions
...
-----> Launching... done, v4
http://high-day-4093.herokuapp.com deployed to Heroku
$

Now that your app is launched, do a quick

1
curl
call and confirm it’s on Thin:

$ curl -I http://high-day-4093.herokuapp.com
HTTP/1.1 200 OK
[...]
Server: thin 1.4.1 codename Chromeo
[..]
$ 

Hooray, we’re running Thin! You can see this in the Server header that Thin sets. Except, we want to run Puma…

Procfile

Cedar introduced a new way to think about scaling your app; “the process model”. It’s a generalized approach to managing processes across Heroku’s distributed environment, allowing you to tweak how and what gets run via a Procfile. Here we’ll create a simple

1
Procfile
and tell our app to run with Puma instead of Thin.

First, swap out Thin for Puma in your

1
Gemfile
:

...
gem 'puma'
...

[^proc]Then add a file named

1
Procfile
to the root of your project and add the following:

1
web: bundle exec puma -p $PORT -e $RACK_ENV -t 0:5

Those are the instructions Heroku will use to run your

1
web
process.

Here $PORT and $RACK_ENV reference environment variables provided by Heroku, port being the listen port assigned by the Heroku router, and $RACK_ENV being the mode to run your app, typically 'production'. The -t flag lets you tune the threads Puma will use; here we set it to minimum 0, maximum 5, to line up with the default connection pool count from ActiveRecord.

Making sure to add the new

1
Procfile
to your repo, commit your changes and push to Heroku again:

$ git add Procfile
$ git add Gemfile
$ git commit -m "Use Puma via a Procfile"
$ git push heroku master
  -----> Heroku receiving push
  -----> Ruby/Rails app detected
  -----> Installing dependencies using Bundler version 1.2.0.pre
  Running: bundle install --without development:test --path vendor/bundle --binstubs bin/ --deployment
  Fetching gem metadata from https://rubygems.org/.......
  ...
  Installing puma (1.4.0) with native extensions
  ...
  -----> Launching... done, v5
  http://high-day-4093.herokuapp.com deployed to Heroku

Now we run our

1
curl
command again:

$ curl -I http://high-day-4093.herokuapp.com
HTTP/1.1 200 OK
[...]
$

Hooray, we’re not running Thin! Except it doesn’t say we’re running Puma either… I suppose Puma doesn’t report itself like Thin and WEBrick do.

Now if you run

1
heroku ps
you should see you have 1 web dyno running, and the statement used to run it:

$ heroku ps
=== web: bundle exec puma -p $PORT -e $RACK_ENV -t 0:5
web.1: up for 2m

Congratulations, you’re running Puma! Note that Puma is said to really shine with Rubinius and JRuby where it can utilize multiple threads. Still, it should give you some benefits on MRI as well.

Configuration

As you continue to fine tune your Puma setup, you may find yourself with more than a few flags and switches in your invocation (the

1
web:
part of the
1
Procfile
in this case). Puma lets you specify a configuration file for all this, using the
1
-C
flag:

web: bundle exec puma -p $PORT -C ./config/puma.rb 

And matching

1
./config/puma.rb
file:

# config/puma.rb
environment ENV['RACK_ENV']
threads 0,5

Done! Checkout the sample configuration or configuration.rb in the repository to see all available options.

Note: you can't configure the port in your configuration file. Not sure why, maybe a bug. ¯\_(ツ)_/¯

Clustered Mode

With it’s 2.0 release, Puma learned some new tricks, key being the addition of Clustered Mode. Puma gains a level of concurrency by spawning workers to handle requests, each worker with it’s own set of threads. You enable clustered mode with the

1
-w
or
1
--workers
flag, providing the number of workers you’d like to spawn. To enable this on Heroku, simply update your
1
Procfile
:

1
web: bundle exec puma -p $PORT -e $RACK_ENV -w 3 -t 0:5

Or add

1
workers
to your
1
./config/puma.rb
file:

# config/puma.rb
environment ENV['RACK_ENV']
threads 0,5

workers 3

You can optionally choose to preload your application before spinning up your workers. Use the

1
--preload
flag or call
1
preload_app!
in your config file.

Finally, you can hook into your workers before they boot with the

1
on_worker_boot
method in your config file. An example of something to do here is if you’re preloading your application, you’ll want to establish your ActiveRecord connections here:

# config/puma.rb
environment ENV['RACK_ENV']
threads 0,5

workers 3
preload_app!

on_worker_boot do
  ActiveSupport.on_load(:active_record) do
    ActiveRecord::Base.establish_connection
  end
end

Done!

You’ve ran the gamut and setup Puma on Heroku, and threw in lots of optional things a long the way. Checkout puma.io and github.com/puma/puma for more info.

Now get hacking.

@ctshryock

About

My name is Clint Shryock. I develop things in Go and Ruby. I live in central Missouri, where the weather is beautiful 4 months of the year.
+-----------------+
|                       |
|      (ノ^_^)ノ      |
|                       |
|   ☜(゚ヮ゚☜)    |
|                       |
|     ౿(ఠ_ఠఎ)    |
|                       |
|        ಠ_ಠ         |
x                      x
  xxx           xxx
       xx    xx
           xx