May 16, 2015 by Daniel P. Clark

Switching From Unicorn to Puma on Heroku is Easy!

I’ve had my project running on Heroku with Unicorn for some time now.  Not that long ago Heroku put out a notice advising people to switch over to Puma as Unicorn doesn’t do well with low latency connections.  And you know that with the internet you’re always going to have people with slow connections accessing your service.  Also upon looking into it further Puma it looks like it performs better than all the other server control environments.  Puma’s key advantage being it does things with threads really well.

First I’ll show what I had for my Unicorn server setup.

Unicorn

The configuration file in config/unicorn.rb

# config/unicorn.rb
worker_processes Integer(ENV["WEB_CONCURRENCY"] || 2)
timeout 15
preload_app true

before_fork do |server, worker|
  Signal.trap 'TERM' do
    puts 'Unicorn master intercepting TERM and sending myself QUIT instead'
    Process.kill 'QUIT', Process.pid
  end

  defined?(ActiveRecord::Base) and
    ActiveRecord::Base.connection.disconnect!
end

after_fork do |server, worker|
  Signal.trap 'TERM' do
    puts 'Unicorn worker intercepting TERM and doing nothing. Wait for master to send QUIT'
  end

  defined?(ActiveRecord::Base) and
    ActiveRecord::Base.establish_connection
end

The line in Procfile

# Procfile
web: bundle exec unicorn -p $PORT -c ./config/unicorn.rb

And the Gemfile

# Gemfile
group :production do
  gem "unicorn"
end

From hear it was as easy as switching the Profile and Gemfile and adding a Puma config file.

Puma

The configuration file config/puma.rb

# config/puma.rb
workers Integer(ENV['WEB_CONCURRENCY'] || 2)
threads_count = Integer(ENV['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
  # Worker specific setup for Rails 4.1+
  # See: https://devcenter.heroku.com/articles/deploying-rails-applications-with-the-puma-web-server#on-worker-boot
  ActiveRecord::Base.establish_connection
  if defined?(Resque)
    Resque.redis = ENV["REDISCLOUD_URL"] || "redis://127.0.0.1:6379"
  end
end

The only thing I had to change from the default was the environment variable used for the Redis server.

Changed the Profile to

# Procfile
web: bundle exec puma -C config/puma.rb

And the Gemfile to

# Gemfile
group :production do
  gem "puma"
end

After that it’s the normal push to the Heroku server with git and everything works!

Summary

It’s easy, it’s better, so you might as well just go ahead and do it.  Feel free to look at Heroku’s howto if you’d like.  Deploying Rails Applications with the Puma Web Server  I’ve pretty much covered it here.

Please feel free to comment, share, subscribe to my RSS Feed, and follow me on twitter @6ftdan!

God Bless!
-Daniel P. Clark

Image by Tambako The Jaguar via the Creative Commons Attribution-NoDerivs 2.0 Generic License.

#change#easy#heroku#puma#rails#ruby on rails#switch#unicorn

Comments

  1. Swapnesh Khare
    February 24, 2016 - 2:13 am

    I have a Rails app currently running on Unicorn. I want to shift to Puma in such a way that the heavy requests are directed to the Puma server and the rest run like before on Unicorn. I’m using nginx. Is it possible?

    • Daniel P. Clark
      February 26, 2016 - 7:09 pm

      I don’t think that will work. The Procfile determines which process is used for “web” and your Rails instance handles requests there.

Leave a Reply

Your email address will not be published / Required fields are marked *