Padrino with Foreman and Puma on Heroku
Padrino
works great on heroku with just a simple git push heroku master
, but you can get a surprising bump in performance simply
from using Puma
in production rather than the default heroku
web server.
That also goes for using it on your local development machine, but heroku uses Procfile
based spinning up of apps, so you should also test locally on your dev box, using heroku’s handy foreman
before pushing it live to heroku.
It doesn’t take anything other than a tiny bit of configuration and change
from using padrino start
(or padrino s
) locally to foreman start
while you’re developing.
There wasn’t explicit documentation on how to get padrino, puma and heroku working together, and while not too tricky to figure out on your own, I felt it made sense to write it down here as a nice easy recipe to save some time for those who follow (waves at inbound google searcher), and to add some help for the awesome folks in the padrino community.
Pre-requisites
This post assumes you have a working padrino app you can boot up via padrino start
and is focused on the use case of using ActiveRecord as
your ORM and Postgres as your DB (as Heroku likes these things). It further
assumes you have the very handy heroku-cli
set up on your machine (If you
can type heroku apps
and get a response of all the apps running in your heroku
account, you’re good to go. If not, homebrew
is your friend and if you do a brew install heroku
, you should have everything you need.). A properly set up heroku-cli means foreman
is installed in running.
Foreman
is a simple manager for
Procfile based applications, allowing you to kick up apps via rackup rather
than in the case of padrino, via the padrino start
script. As heroku uses
Procfile based startups itself, using it in dev better emulates how your app
will (or will not) run on heroku. If you have a Procfile already in place for
deploying to heroku, you should be able to do a foreman start
and have the app
boot up on port 5000. You can check your padrino app is running there with a
simple http://localhost:5000
much as you would check http://localhost:3000
with padrino start
.
This post is working with padrino 0.14.0.1, puma 3.8.2, foreman 0.84.0, and the heroku cli tools 5.8.11-0e9b5ce (darwin-amd64) go1.7.5.
Preparing Pumas
# Gemfile
gem 'puma'
Do a bundle install
and get it into your Gemfile.lock
.
In your Profile, write the following a la the guide for Rails on Heroku:
# Procfile
web: bundle exec puma -C config/puma.rb
While it’s most likely obvious, this starts up puma by referring to a config
file located at config/puma.rb
.
So, now we need to create a config file for puma. Riffing off what heroku has in place for it Rails and Puma documentation, I created the following:
# config/puma.rb
workers Integer(ENV['WEB_CONCURRENCY'] || 2)
threads_count = Integer(ENV['PADRINO_MAX_THREADS'] || 5)
threads threads_count, threads_count
preload_app!
rackup DefaultRackup
port ENV['PORT'] || 3000
environment ENV['RACK_ENV'] || 'development'
on_worker_boot do
ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations[Padrino.env])
end
So, to quickly explain…
Basically, Puma has workers and threads (do not confuse puma workers with heroku workers you tend to use for async tasks, they are completely different beasts). Loosely speaking, workers consume more RAM and threads consume more CPU. Both provide more concurrency for your app and throughput. And so, a faster experience for your users.
Note a few important things here:
-
On your dev box, if you haven’t set the envs for
WEB_CONCURRENCY
orPADRINO_MAX_THREADS
in your.bash_profile
, these will default to 2 workers and 5 threads on your box. This will both increase the CPU and RAM dedicated to throughput on the web server. This will make your local app seem significantly faster than it would be on heroku, so you may want to adjust those numbers to what you’ll have on heroku. -
On heroku production and staging (you do run a staging instance, right? Ahem… ), you need to set these with config_vars via the excellent Heroku CLI. While you can set web concurrency to 2, it is very very likely your app is not thread-safe unless you really know what you’re doing. Most likely you should set this to
heroku config:set PADINO_MAX_THREADS=1
. If you are absolutely convinced you are thread safe in your padrino app, you can most likely set this number to somewhere between 3 and 5 for a padrino app on a single dyne depending on whether it is thefree
,hobby
orstandard-1x dyno
. (note these are only guidelines and you should check the memory on your app and logs to make sure you’re not topping out memory if you’re playing with squeezing everything you can out of this.). -
The tricky bit in the config is the line regarding establishing the connection to the database via ActiveRecord on worker boot. For those of you who have trawled around your padrino config directory, you’ll notice there is an exact similar line in
config/database.rb
. While I do not have a definitive answer on this, it appears it’s safe to leave this uncommented (and in fact if you do comment it out,padrino console
andpadrino start
will no longer work). As near as I can tell, if you Procfile rackup the application, the puma workers handle the database connection and if you go via thepadrino start
route that is handled within padrino from theconfig/database.rb
.
And Go
From here, you should be able to foreman start
the padrino application and
have it spin up on http://localhost:5000
with it running via puma.
After that, and git committing everything, it’s a simple matter to push the app
as you normally would to heroku. Simply git push heroku master
and watch your
new, speedier, and puma-powered app come up.
nb: Do remember to set your heroku config:set PADRINO_MAX_THREADS=1
to
make sure you don’t have any major issues, but so far I’ve been running this in
production for a while and it is just giving me a noticeably faster, snappier
experience that users have commented on.
Wanna know more about the internals? You can read up on heroku and puma here, Deploying Rails Applications with the Puma Web Server | Heroku Dev Center though its targeted specifically to Rails.