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
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
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.
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
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
http://localhost:5000 much as you would check
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.
# Gemfile gem 'puma'
bundle install and get it into your
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
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
.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 the
standard-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 startwill 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 the
padrino startroute that is handled within padrino from the
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.