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.
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
, just as a demonstration:1
Gemfile
...
gem 'sqlite3'
...
becomes
...
gem 'thin'
...
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
call and confirm it’s
on Thin:1
curl
$ 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…
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
and tell our app to run with Puma instead of Thin.1
Procfile
First, swap out Thin for Puma in your
:1
Gemfile
...
gem 'puma'
...
[^proc]Then add a file named
to the root of your project and add the following:1
Procfile
1
web: bundle exec puma -p $PORT -e $RACK_ENV -t 0:5
Those are the instructions Heroku will use to run your
process.1
web
Making sure to add the new
to your repo, commit your changes
and push to Heroku again:1
Procfile
$ 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
command again:1
curl
$ 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
you should see you have 1 web dyno running,
and the statement used to run it:1
heroku ps
$ 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.
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
part of
the 1
web:
in this case). Puma lets you specify a configuration file
for all this, using the 1
Procfile
flag:1
-C
web: bundle exec puma -p $PORT -C ./config/puma.rb
And matching
file:1
./config/puma.rb
# 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.
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
or 1
-w
flag, providing the number of workers you’d like
to spawn. To enable this on Heroku, simply update your 1
--workers
:1
Procfile
1
web: bundle exec puma -p $PORT -e $RACK_ENV -w 3 -t 0:5
Or add
to your 1
workers
file:1
./config/puma.rb
# 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
flag or call 1
--preload
in your
config file.1
preload_app!
Finally, you can hook into your workers before they boot with the
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:1
on_worker_boot
# 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.