Skip Navigation
Show nav
Dev Center
  • Get Started
  • Documentation
  • Changelog
  • Search
  • Get Started
    • Node.js
    • Ruby on Rails
    • Ruby
    • Python
    • Java
    • PHP
    • Go
    • Scala
    • Clojure
    • .NET
  • Documentation
  • Changelog
  • More
    Additional Resources
    • Home
    • Elements
    • Products
    • Pricing
    • Careers
    • Help
    • Status
    • Events
    • Podcasts
    • Compliance Center
    Heroku Blog

    Heroku Blog

    Find out what's new with Heroku on our blog.

    Visit Blog
  • Log inorSign up
Hide categories

Categories

  • Heroku Architecture
    • Compute (Dynos)
      • Dyno Management
      • Dyno Concepts
      • Dyno Behavior
      • Dyno Reference
      • Dyno Troubleshooting
    • Stacks (operating system images)
    • Networking & DNS
    • Platform Policies
    • Platform Principles
  • Developer Tools
    • Command Line
    • Heroku VS Code Extension
  • Deployment
    • Deploying with Git
    • Deploying with Docker
    • Deployment Integrations
  • Continuous Delivery & Integration (Heroku Flow)
    • Continuous Integration
  • Language Support
    • Node.js
      • Working with Node.js
      • Troubleshooting Node.js Apps
      • Node.js Behavior in Heroku
    • Ruby
      • Rails Support
      • Working with Bundler
      • Working with Ruby
      • Ruby Behavior in Heroku
      • Troubleshooting Ruby Apps
    • Python
      • Working with Python
      • Background Jobs in Python
      • Python Behavior in Heroku
      • Working with Django
    • Java
      • Java Behavior in Heroku
      • Working with Java
      • Working with Maven
      • Working with Spring Boot
      • Troubleshooting Java Apps
    • PHP
      • PHP Behavior in Heroku
      • Working with PHP
    • Go
      • Go Dependency Management
    • Scala
    • Clojure
    • .NET
      • Working with .NET
  • Databases & Data Management
    • Heroku Postgres
      • Postgres Basics
      • Postgres Getting Started
      • Postgres Performance
      • Postgres Data Transfer & Preservation
      • Postgres Availability
      • Postgres Special Topics
      • Migrating to Heroku Postgres
    • Heroku Key-Value Store
    • Apache Kafka on Heroku
    • Other Data Stores
  • AI
    • Model Context Protocol
    • Vector Database
    • Working with AI
    • Heroku Inference
      • Quick Start Guides
      • Inference API
      • Inference Essentials
      • AI Models
  • Monitoring & Metrics
    • Logging
  • App Performance
  • Add-ons
    • All Add-ons
  • Collaboration
  • Security
    • App Security
    • Identities & Authentication
      • Single Sign-on (SSO)
    • Private Spaces
      • Infrastructure Networking
    • Compliance
  • Heroku Enterprise
    • Enterprise Accounts
    • Enterprise Teams
    • Heroku Connect (Salesforce sync)
      • Heroku Connect Administration
      • Heroku Connect Reference
      • Heroku Connect Troubleshooting
  • Patterns & Best Practices
  • Extending Heroku
    • Platform API
    • App Webhooks
    • Heroku Labs
    • Building Add-ons
      • Add-on Development Tasks
      • Add-on APIs
      • Add-on Guidelines & Requirements
    • Building CLI Plugins
    • Developing Buildpacks
    • Dev Center
  • Accounts & Billing
  • Troubleshooting & Support
  • Integrating with Salesforce
  • Language Support
  • Ruby
  • Rails Support
  • Deploying Rails Applications with Unicorn

Deploying Rails Applications with Unicorn

English — 日本語に切り替える

Last updated October 16, 2024

Table of Contents

  • The Unicorn server
  • Adding Unicorn to your application
  • Caveats

Heroku recommends using the Puma web server instead of Unicorn. If you are using Unicorn, your application is not protected against a slow client attack.

Web applications that process requests concurrently make much more efficient use of dyno resources than web applications that only process one request at a time. Therefore it is recommended to use concurrent request processing whenever developing and running production services.

The Rails framework was originally designed to process one request at a time. The framework is gradually moving away from this design towards a thread safe implementation that allows for concurrent processing of requests in a single Ruby process. But most Ruby applications don’t support this today.

The Unicorn web server lets you run any Rails application concurrently by running multiple Ruby processes in a single dyno.

This guide will walk you through deploying a new Rails application to Heroku using the Unicorn web server. For basic Rails setup, see Getting Started with Rails.

Always test your new deployments in a staging environment before you deploy to your production application. See Managing Multiple Environments for an App for more info.

The Unicorn server

Unicorn is a Rack HTTP server that uses forked processes to handle multiple incoming requests concurrently.

Adding Unicorn to your application

Gemfile

First, add Unicorn to your app’s Gemfile:

 gem 'unicorn'

Run bundle install to set up your bundle locally.

Config

Create a configuration file for Unicorn at config/unicorn.rb, or at a path of your choosing. For a simple Rails application, we recommend the following basic configuration:

# config/unicorn.rb
worker_processes Integer(ENV["WEB_CONCURRENCY"] || 3)
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 above assumes a standard Rails app with ActiveRecord and New Relic for monitoring. For information on other available configuration operations, see Unicorn’s documentation.

To manually configure the concurrency value use heroku config:set WEB_CONCURRENCY. Load test your application to find the right value for your app.

Unicorn worker processes

worker_processes Integer(ENV["WEB_CONCURRENCY"] || 3)

Unicorn forks multiple OS processes within each dyno to allow a Rails app to support multiple concurrent requests without requiring them to be thread-safe. In Unicorn terminology these are referred to as worker processes not to be confused with Heroku worker processes which run in their own dynos.

Each forked OS process consumes additional memory. This limits how many processes you can run in a single dyno. With a typical Rails memory footprint, you can expect to run 2-4 Unicorn worker processes. Your application may allow for more or less processes depending on your specific memory footprint, and we recommend specifying this number in an config var to allow for faster application tuning. Monitor your application logs for R14 errors (memory quota exceeded) via one of our logging addons or heroku logs.

Preload app

preload_app true
# ...
before_fork do |server, worker|
  # ...
  defined?(ActiveRecord::Base) and
    ActiveRecord::Base.connection.disconnect!
end

after_fork do |server, worker|
  # ...
  defined?(ActiveRecord::Base) and
    ActiveRecord::Base.establish_connection
end

Preloading your application reduces the startup time of individual Unicorn worker_processes and allows you to manage the external connections of each individual worker using the before_fork and after_fork calls. In the config above, these calls are used to correctly establish postgres connections for each worker process.

New Relic also recommends preload_app true for more accurate data collection with Unicorn apps. For information on using New Relic with preload_app true, see their documentation.

Signal handling

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
  # ...
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
  # ...
end

POSIX Signals are a form of interprocess communication to indicate a certain event or state change. Traditionally, QUIT is used to signal a process to exit immediately and produce a core dump. TERM is used to tell a process to terminate, but allows the process to clean up after itself.

Unicorn uses the QUIT signal to indicate graceful shutdown. When the master process receives this signal it sends a QUIT signal to all workers who will then gracefully shut down after completing any in-flight requests. After the worker processes have shut down, the master process will exit.

Heroku uses the TERM signal to indicate to all processes in a dyno that the dyno is being shut down. The configuration above ensures that this TERM signal is translated correctly to the Unicorn model: the workers trap and ignore the signal. The master traps and sends a QUIT signal to itself, thereby starting the graceful shutdown process.

Heroku gives processes 10 seconds to shut down gracefully after which a KILL signal is sent to all processes to force a shutdown. If an individual request takes longer than 10 seconds, it might be interrupted. Keep an eye out for entries in your application logs that indicate failure to shut down gracefully.

Timeouts

Heroku’s router enforces a 30 second window before there is a request timeout. After a request is delivered to a dyno via the router it has 30 seconds to return a response or the router will return a customizable error page. This is done to prevent hanging requests from tying up resources. While the router will return a response to the client, the unicorn worker will continue to process the request even though a client has received a response. This means that the worker is being tied up, perhaps indefinitely due to a hung request. To ensure your application’s requests do not tie up your dyno past the request timeout, we recommend using both the Rack::Timeout gem and Unicorn’s timeout configuration setting.

Unicorn timeout

Unicorn has a configurable timeout setting. The timeout countdown for unicorn will begin once the request is being processed by your application and end when it returns a response. If the request takes longer than the specified time, the master will SIGKILL the worker working on the request.

timeout 15

We recommend a timeout of 15 seconds. With a 15 second timeout, the master process will send a SIGKILL to the worker process if processing a request takes longer than 15 seconds. This will generate a H13 error code and you’ll see it in your logs. Note, this will not generate any stacktraces to assist in debugging.

Rack::Timeout

When the Rack::Timeout limit is hit, it closes the requests and generates a stacktrace in the logs that can be used for future debugging of long running code. First, the gem will need to be installed.

# Gemfile
gem 'rack-timeout'
$ bundle install

Then the timeout can be set.

# config/initializers/timeout.rb
Rack::Timeout.timeout = 10  # seconds

On Ruby 1.9/2.0, Rack::Timeout uses Ruby’s stdlib Timeout library which can be unreliable. Heroku recommends using Rack::Timeout and setting the unicorn timeout. If using both timeout systems, the Rack::Timeout value should be lower than the unicorn timeout if you plan on using the stack trace produced by Rack::Timeout for debugging.

Procfile

Set Unicorn as the server for the web process in your Procfile, pointing to your config file:

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

Caveats

Preloading and other external services

Take care with other external connections to make sure they work properly with Unicorn’s forking model. As you can see in the sample configuration above, the app drops its ActiveRecord connection in the before_fork block, and reconnects in the worker process in after_fork. Other services will follow a similar pattern. For example, here’s the configuration block for using Resque with a Unicorn app:

before_fork do |server, worker|
  # ...

  # If you are using Redis but not Resque, change this
  if defined?(Resque)
    Resque.redis.quit
    Rails.logger.info('Disconnected from Redis')
  end
end

after_fork do |server, worker|
  # ...

  # If you are using Redis but not Resque, change this
  if defined?(Resque)
    Resque.redis = ENV['<REDIS_URL>']
    Rails.logger.info('Connected to Redis')
  end
end

Modify the REDIS_URL config var to correspond with that from your Redis provider.

Many popular gems, such as the dalli memcache client, discuss compatibility with Unicorn’s worker process model in their documentation. If you are experiencing issues, check your gem’s documentation for more information.

Assets

For optimal performance, host your assets behind a CDN to free up your web dynos to serve only dynamic content.

Database connections

Running a concurrent web server in production means that each dyno will require more than one database connection. To run a high volume Rails app with a concurrent web server you will need to understand how Active Record creates and manages these connections in the connection pool, and the connection limit on development databases. For an in depth look at these topics please read the devcenter article Concurrency and Database Connections.

Slow clients

Unicorn may not be the best choice for your particular combination of application and workload. In particular, if your application receives requests with large body payloads from slow clients, you may be better off using a different web server. An example would be a an app that receives images uploaded by users from mobile phones that are not on wifi, 4G or other fast networks.

The problem is caused by Unicorn workers becoming tied up receiving requests that are sent slowly by clients. If all Unicorn workers are tied up, new requests are queued and your app will likely experience greater-than-normal request-queue times or even H12 errors.

Puma, Thin or Rainbows! are alternative web servers that may work better under load generated by slow clients. To change the web server running your app on Heroku, simply specify a different command for the web process type in your Procfile.

Keep reading

  • Rails Support

Feedback

Log in to submit feedback.

Using Rack::Cache with Memcached in Rails 3.1+ (Including Rails 4) Deploying to a Custom Rails Environment

Information & Support

  • Getting Started
  • Documentation
  • Changelog
  • Compliance Center
  • Training & Education
  • Blog
  • Support Channels
  • Status

Language Reference

  • Node.js
  • Ruby
  • Java
  • PHP
  • Python
  • Go
  • Scala
  • Clojure
  • .NET

Other Resources

  • Careers
  • Elements
  • Products
  • Pricing
  • RSS
    • Dev Center Articles
    • Dev Center Changelog
    • Heroku Blog
    • Heroku News Blog
    • Heroku Engineering Blog
  • Twitter
    • Dev Center Articles
    • Dev Center Changelog
    • Heroku
    • Heroku Status
  • Github
  • LinkedIn
  • © 2025 Salesforce, Inc. All rights reserved. Various trademarks held by their respective owners. Salesforce Tower, 415 Mission Street, 3rd Floor, San Francisco, CA 94105, United States
  • heroku.com
  • Legal
  • Terms of Service
  • Privacy Information
  • Responsible Disclosure
  • Trust
  • Contact
  • Cookie Preferences
  • Your Privacy Choices