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 `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 `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][5]. Here we'll create a simple `Procfile` and tell our app to run with Puma instead of Thin. First, swap out Thin for Puma in your `Gemfile`:
...
gem 'puma'
...
[^proc]Then add a file named `Procfile` to the root of your project and add the following: web: bundle exec puma -p $PORT -e $RACK_ENV -t 0:5 Those are the instructions Heroku will use to run your `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 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 `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 `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 `web:` part of the `Procfile` in this case). Puma lets you specify a configuration file for all this, using the `-C` flag:
web: bundle exec puma -p $PORT -C ./config/puma.rb
And matching `./config/puma.rb` file:
# config/puma.rb
environment ENV['RACK_ENV']
threads 0,5
**Done!** Checkout the [sample configuration][7] or [configuration.rb][8] 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 -w or --workers flag, providing the number of workers you'd like
to spawn. To enable this on Heroku, simply update your Procfile:

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

Or add workers to your ./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 --preload flag or call preload_app! in your
config file.

Finally, you can hook into your workers before they boot with the
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