Puma for Rails: Full Guide from Start to Scale

Complete Puma + Rails Guide | Randomize Blog

Complete Beginner’s Guide to Puma + Rails

Learn how to deploy Ruby on Rails with Puma – from setup to advanced production tips.

πŸ“š Table of Contents

πŸ—οΈ Key Terms & Concepts

TermDescription
PumaMulti-threaded, multi-worker Ruby/Rails application server, default for new Rails apps.
RailsPopular web application framework written in Ruby.
WorkerSeparate process for handling requests; more workers = more concurrency.
ThreadLightweight process for handling multiple requests concurrently within a worker.
Reverse ProxyServer (like Apache or Nginx) that forwards requests to Puma.
SystemdLinux service manager, used for process control.
ENVEnvironment variables for secrets/configuration.
SSL/TLSEncryption for secure HTTP (HTTPS).
Cluster ModePuma mode with multiple workers and threads for high concurrency.
Preload AppPuma setting to load the app before forking workers (saves memory, faster boot).
CapistranoPopular deployment tool for Rails apps.
DockerContainerization platform for packaging and running applications in isolated environments.

πŸ” What is Puma?

Puma is a fast, concurrent web server for Ruby and Ruby on Rails applications. It is the default server for new Rails projects and is designed to handle many requests at once using threads and workers.

Simple Analogy: Puma is like a team of waiters (workers), each able to serve multiple tables (threads) at the same time, making your Rails app fast and responsive even under heavy load.

βœ… Why Use Puma?

Puma is a great choice for Rails apps because it is lightweight, easy to configure, and supports high concurrency. It works well with reverse proxies like Nginx and Apache, and is suitable for both small and large-scale deployments.

FeatureBenefit for Beginners
Multi-threadedHandles many requests at once, making apps faster
Cluster modeMultiple workers for better CPU/core usage
Default for RailsNo extra setup for new Rails apps
Works with Nginx/ApacheEasy to use behind a reverse proxy for SSL, static files, etc.
Simple configEasy to tune for your server size
Great for APIsHandles high concurrency, perfect for JSON APIs
Production readyUsed by many large Rails sites

βš–οΈ Puma vs Passenger/Unicorn

FeaturePumaPassengerUnicorn
ThreadedYes (multi-threaded, multi-worker)Optional (multi-process, can be multi-threaded)No (multi-process only)
Zero-downtime deploysYes (phased restart)Yes (built-in)Yes (USR2 signal)
Reverse proxy neededYes (Nginx/Apache)No (integrates as module)Yes (Nginx/Apache)
Ease of useEasy, default for RailsVery easy, auto-detects Rails appManual config, less beginner-friendly
Best forAPIs, high concurrency, modern RailsTraditional web apps, shared hostingLegacy Rails, simple deployments
Docker supportExcellentGoodGood
Monitoring toolspumactl, systemd, externalpassenger-status, passenger-memory-statsManual, external

Summary:
Puma is the default for new Rails apps and is best for high concurrency and APIs. Passenger is great for easy integration with Apache/Nginx and traditional web apps. Unicorn is older, process-based, and best for legacy or simple deployments.

πŸ‘ Pros & Cons

Puma
Pros
  • High concurrency (threads + workers)
  • Default for Rails, easy to use
  • Great for APIs and real-time apps
  • Lightweight and fast
  • Excellent Docker support
  • Zero-downtime deploys (phased restart)
  • Flexible config for any server size
Cons
  • Needs reverse proxy for SSL/static files
  • Manual tuning for best performance
  • Not as beginner-friendly for advanced tuning
  • No built-in monitoring UI

πŸ› οΈ How to Install Puma & Rails

Install on Ubuntu/Debian

# Install Ruby, Rails, and dependencies
sudo apt update
sudo apt install ruby-full build-essential zlib1g-dev
sudo gem install rails bundler

# Add Puma to your Rails app
cd /var/www/myapp
bundle add puma
bundle install

Install on CentOS/RHEL

# Install Ruby, Rails, and dependencies
sudo yum install ruby ruby-devel gcc make
sudo gem install rails bundler

# Add Puma to your Rails app
cd /var/www/myapp
bundle add puma
bundle install

Install on macOS

# Using Homebrew
brew install ruby
sudo gem install rails bundler

# Add Puma to your Rails app
cd /path/to/myapp
bundle add puma
bundle install

Note: Puma is the default server for new Rails apps (Rails 5+), so you may already have it in your Gemfile.

βš™οΈ Basic Configuration

puma.rb Example (Rails)

# config/puma.rb
max_threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }
min_threads_count = ENV.fetch("RAILS_MIN_THREADS") { max_threads_count }
threads min_threads_count, max_threads_count

port        ENV.fetch("PORT") { 3000 }
environment ENV.fetch("RAILS_ENV") { "development" }

# For production, use workers (cluster mode)
workers ENV.fetch("WEB_CONCURRENCY") { 2 }
preload_app!

plugin :tmp_restart
  • threads: Sets min/max threads per worker (concurrency)
  • port: Port Puma will listen on (default 3000)
  • environment: Rails environment (development, production, etc.)
  • workers: Number of worker processes (for cluster mode)
  • preload_app!: Loads app before forking workers (saves memory, faster boot)
  • plugin :tmp_restart: Allows rails restart to work

Tip: For production, set WEB_CONCURRENCY and RAILS_MAX_THREADS as environment variables for best performance.

βš™οΈ Rails Application Settings for Puma

  • Set RAILS_ENV=production for production deployments
  • Precompile assets: RAILS_ENV=production rails assets:precompile
  • Use config.force_ssl = true in config/environments/production.rb for HTTPS
  • Set secrets and DB credentials via environment variables or config/credentials.yml.enc
  • For large apps, tune config.eager_load = true and config.cache_store
  • Use WEB_CONCURRENCY and RAILS_MAX_THREADS for scaling
# Example: config/environments/production.rb
Rails.application.configure do
  config.cache_classes = true
  config.eager_load = true
  config.force_ssl = true
  config.log_level = :info
  # ...other production settings...
end

πŸš€ Full Implementation Example

1. Standalone Puma (for development or simple production)

# Start Puma in your Rails app directory
RAILS_ENV=production bundle exec puma -C config/puma.rb
  • App will be available at http://localhost:3000 (or port you set)
  • Not recommended to expose Puma directly to the internet in production

Troubleshooting Tips:

  • Puma not starting? Check log/production.log and config/puma.rb
  • Port in use? Change port in puma.rb or stop other services

2. Puma with Apache Reverse Proxy

# Apache VirtualHost example

    ServerName example.com
    ProxyPass / http://127.0.0.1:3000/
    ProxyPassReverse / http://127.0.0.1:3000/
    ErrorLog ${APACHE_LOG_DIR}/puma_error.log
    CustomLog ${APACHE_LOG_DIR}/puma_access.log combined

  • Start Puma on 127.0.0.1:3000
  • Apache handles SSL, static files, and proxies requests to Puma

Troubleshooting Tips:

  • Proxy errors? Ensure Puma is running and listening on the correct port
  • Permission denied? Check Apache and Rails logs

3. Puma with Nginx Reverse Proxy

# Nginx server block example
server {
    listen 80;
    server_name example.com;
    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
    error_log /var/log/nginx/puma_error.log;
    access_log /var/log/nginx/puma_access.log;
}
  • Start Puma on 127.0.0.1:3000
  • Nginx handles SSL, static files, and proxies requests to Puma

Troubleshooting Tips:

  • 502 Bad Gateway? Check Puma is running and Nginx proxy_pass is correct
  • Permission denied? Check Nginx and Rails logs

4. Puma with Docker

# Dockerfile for Rails + Puma
FROM ruby:3.2

# Install dependencies
RUN apt-get update -qq && apt-get install -y nodejs yarn

WORKDIR /app
COPY Gemfile Gemfile.lock ./
RUN bundle install
COPY . .
RUN bundle exec rake assets:precompile

EXPOSE 3000
CMD ["bundle", "exec", "puma", "-C", "config/puma.rb"]
  • Set environment variables in your Dockerfile or docker-compose.yml
  • Use a reverse proxy (Nginx/Apache) for SSL and static files in production

Troubleshooting Tips:

  • Puma not starting? Check container logs and config/puma.rb
  • Assets not loading? Ensure assets:precompile ran successfully

🏒 Best Settings for Large Scale Applications

  • Set WEB_CONCURRENCY to match CPU cores (e.g., 4 for quad-core)
  • Set RAILS_MAX_THREADS to 5-16 (tune for your workload)
  • Use preload_app! in puma.rb for memory savings
  • Enable config.cache_store with Redis or Memcached
  • Use a CDN for static assets
  • Monitor memory and CPU usage (see Monitoring section)
  • Use config.active_job.queue_adapter for background jobs (Sidekiq, Resque)
  • Consider horizontal scaling (multiple servers/load balancer) for very high traffic
# Example: config/puma.rb for production
workers ENV.fetch("WEB_CONCURRENCY") { 4 }
threads_count = ENV.fetch("RAILS_MAX_THREADS") { 8 }
threads threads_count, threads_count
preload_app!

Tip: Always test your settings under load (using tools like wrk or ab) before going to production.

🌍 Real-World Case Study: Rails + Puma Deployment

Company: High-Traffic API Platform

Stack: Rails 7 + Puma + Nginx + PostgreSQL + Redis + Sidekiq

Deployment: AWS EC2, Dockerized Rails app, Puma in cluster mode (4 workers, 8 threads), Nginx reverse proxy, SSL via AWS ACM, assets on S3/CloudFront, background jobs with Sidekiq.

Challenges: Needed to handle 1000+ concurrent API requests/sec, minimize latency, and ensure zero-downtime deploys.

Solution: Tuned WEB_CONCURRENCY and RAILS_MAX_THREADS, used preload_app!, enabled caching with Redis, and set up rolling deploys with Capistrano. Used Datadog for monitoring and alerting.

Result: 99.99% uptime, sub-100ms API response times, and seamless scaling during traffic spikes.

🚨 Common Error Messages Table

ErrorCauseSolution
Puma not startingMissing dependencies, incorrect config, port in useCheck log/production.log, config/puma.rb, and stop conflicting services.
Port in useAnother service is using the port (3000)Change port in config/puma.rb or stop the conflicting service.
Permission deniedFile permissions, user permissions, or SELinuxCheck file/directory permissions, run with sudo, or adjust SELinux.
502 Bad GatewayNginx proxy_pass incorrect, Puma not running, or SSL issueCheck Nginx config, Puma logs, and SSL settings.
No such file or directoryMissing file, incorrect path, or file permissionsCheck file existence, path, and permissions.
NoMethodErrorMissing method, incorrect config, or precompilation issueCheck config/environments/production.rb, log/production.log, and ensure assets are precompiled.
ActionView::Template::ErrorMissing template, incorrect path, or precompilation issueCheck log/production.log, config/environments/production.rb, and ensure assets are precompiled.
ActiveRecord::ConnectionErrorDatabase connection issues, missing credentials, or config errorCheck config/database.yml, config/credentials.yml.enc, and log/production.log.
ActionController::RoutingErrorIncorrect route, missing routes, or precompilation issueCheck config/routes.rb, log/production.log, and ensure assets are precompiled.
ActionView::MissingTemplateMissing template, incorrect path, or precompilation issueCheck log/production.log, config/environments/production.rb, and ensure assets are precompiled.

Tip: Always check your log/production.log for detailed error messages. Use pumactl for Puma-specific monitoring.

πŸ”— .htaccess and Nginx Equivalent

Below are examples of how to configure Apache’s .htaccess and Nginx for Puma. These are often used in conjunction with a reverse proxy.

Apache .htaccess

# Apache .htaccess for Puma
RewriteEngine On
RewriteCond %{REQUEST_URI} !^/assets/
RewriteCond %{REQUEST_URI} !^/favicon.ico
RewriteCond %{REQUEST_URI} !^/robots.txt
RewriteRule ^(.*)$ http://127.0.0.1:3000/$1 [P,L]

Nginx Server Block

# Nginx server block for Puma
server {
    listen 80;
    server_name example.com;
    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
    error_log /var/log/nginx/puma_error.log;
    access_log /var/log/nginx/puma_access.log;
}

Note: The .htaccess example assumes a reverse proxy is in place. The Nginx example is a complete server block.

πŸ”§ Troubleshooting

Common issues and how to resolve them:

Puma Not Starting

  • Check log/production.log for detailed error messages.
  • Ensure all dependencies are installed and up-to-date.
  • Verify config/puma.rb is correct and has no syntax errors.
  • Check if the port is in use and stop conflicting services.
  • Run RAILS_ENV=production bundle exec puma -C config/puma.rb directly to test.

Port in Use

  • Use lsof -i :3000 to find which process is using port 3000.
  • Stop the process using kill -9 PID (replace PID with the actual process ID).
  • Change the port in config/puma.rb to a different one.

Permission Denied

  • Check file/directory permissions: chmod 755 /path/to/your_app
  • Run with sudo: sudo RAILS_ENV=production bundle exec puma -C config/puma.rb
  • Adjust SELinux: chcon -R -t httpd_sys_content_t /path/to/your_app

502 Bad Gateway

  • Check log/production.log for Nginx errors.
  • Verify config/puma.rb is running on 127.0.0.1:3000 or correct port.
  • Check SSL settings in Nginx if using HTTPS.

No Such File or Directory

  • Ensure the file/directory exists: ls -la /path/to/your_file
  • Check file permissions: chmod 644 /path/to/your_file
  • Check path: pwd and cd /path/to/your_file

NoMethodError

  • Check log/production.log for the specific error.
  • Ensure config/environments/production.rb is correct.
  • Check if the method exists in the class: Rails.application.routes.named_routes.helper_names

ActionView::Template::Error

  • Check log/production.log for the specific error.
  • Ensure config/environments/production.rb is correct.
  • Check if the template file exists: ls -la /path/to/your_app/app/views/your_controller/your_action.html.erb

ActiveRecord::ConnectionError

  • Check config/database.yml and config/credentials.yml.enc.
  • Ensure database credentials are correct and accessible.
  • Check log/production.log for connection errors.

ActionController::RoutingError

  • Check config/routes.rb and log/production.log.
  • Ensure routes are defined and accessible.
  • Check if the route name is correct: Rails.application.routes.named_routes.helper_names

ActionView::MissingTemplate

  • Check log/production.log for the specific error.
  • Ensure config/environments/production.rb is correct.
  • Check if the template file exists: ls -la /path/to/your_app/app/views/your_controller/your_action.html.erb

Tip: Always check your log/production.log for detailed error messages. Use pumactl for Puma-specific monitoring.

⚑ Performance Tuning

Optimize Puma for better performance:

Threads and Workers

  • Set RAILS_MAX_THREADS and RAILS_MIN_THREADS based on your server’s CPU cores.
  • Use WEB_CONCURRENCY for scaling.
  • For a 4-core server, set RAILS_MAX_THREADS=4, RAILS_MIN_THREADS=2, WEB_CONCURRENCY=2.
  • For a 16-core server, set RAILS_MAX_THREADS=16, RAILS_MIN_THREADS=8, WEB_CONCURRENCY=4.

Preloading and Caching

  • Enable config.eager_load = true for production.
  • Configure config.cache_store for better performance.
  • Precompile assets: RAILS_ENV=production rails assets:precompile.

SSL/TLS

  • Configure SSL/TLS for secure connections.
  • Use config.force_ssl = true in config/environments/production.rb.

Monitoring

  • Use pumactl for Puma-specific monitoring.
  • Use external monitoring tools like New Relic, Datadog, or Sentry.

Error Handling

  • Implement robust error logging and monitoring.
  • Use config.log_level = :info in config/environments/production.rb.

Tip: Always monitor your application’s performance and adjust settings as needed. Use tools like pumactl and external monitoring services.

πŸ”’ Security Best Practices

Ensure your Rails app is secure:

SSL/TLS

  • Configure SSL/TLS for secure HTTP (HTTPS).
  • Use config.force_ssl = true in config/environments/production.rb.

Environment Variables

  • Never commit sensitive credentials to version control.
  • Set secrets via config/credentials.yml.enc or environment variables.
  • Use RAILS_ENV for different environments.

File Permissions

  • Set appropriate file permissions for your application files.
  • Use chmod 644 for configuration files.
  • Use chmod 755 for application directories.

Authentication

  • Use strong authentication mechanisms (e.g., Devise, Auth0).
  • Implement secure session management.
  • Use HTTPS for all requests.

Input Validation

  • Sanitize all user inputs.
  • Use strong parameters.
  • Validate all inputs.

Error Messages

  • Do not expose sensitive error messages to users.
  • Log all errors to log/production.log.

Tip: Regularly update your dependencies and Ruby version to ensure security patches are applied.

πŸ€– Automation

Use tools to automate your deployment and management:

Capistrano

  • Popular deployment tool for Rails apps.
  • Handles zero-downtime deploys.
  • Configures environment variables, SSL, and reverse proxies.

Docker

  • Containerization platform for packaging and running applications.
  • Easy to replicate environments.
  • Handles SSL, static files, and reverse proxies.

Systemd

  • Linux service manager for managing Puma as a system service.
  • Handles process restarts, monitoring, and logging.

pumactl

  • Command-line tool for managing Puma processes.
  • Provides status, restart, and other management functions.

Tip: Use a combination of tools for optimal automation. For example, Capistrano for deployment, Docker for containerization, and Systemd for service management.

πŸ‘οΈ Monitoring

Monitor your application’s performance and health:

pumactl

  • Command-line tool for Puma monitoring.
  • Provides status, stats, and management functions.

Passenger Status

  • For Passenger users, use passenger-status and passenger-memory-stats.

External Monitoring

  • Use New Relic, Datadog, Sentry, or other external monitoring services.
  • Track performance metrics, error rates, and user behavior.

Log Analysis

  • Regularly analyze log/production.log for errors and performance issues.
  • Use tools like tail -f or grep for quick checks.

Tip: Combine multiple monitoring tools for a comprehensive view of your application’s health.

πŸ“ Cheat Sheet

Quick reference for common commands and configurations:

Basic Puma Commands

# Start Puma in development
RAILS_ENV=development bundle exec puma -C config/puma.rb

# Start Puma in production
RAILS_ENV=production bundle exec puma -C config/puma.rb

# Restart Puma
RAILS_ENV=production bundle exec puma -C config/puma.rb --restart

# Check Puma status
pumactl status

# Check Puma stats
pumactl stats

# Check Puma logs
tail -f log/puma.log

Common Configurations

  • Threads: max_threads_count = ENV.fetch(“RAILS_MAX_THREADS”) { 5 }
  • Port: port ENV.fetch(“PORT”) { 3000 }
  • Environment: environment ENV.fetch(“RAILS_ENV”) { “development” }
  • Workers: workers ENV.fetch(“WEB_CONCURRENCY”) { 2 }
  • Preload App: preload_app!
  • Plugin: plugin :tmp_restart

Tip: Always refer to the official Puma documentation for the most up-to-date information.

🌐 External Resources

🏁 Conclusion

Puma is a powerful, production-ready application server for Rails, offering high concurrency, performance, and flexibility. With the right configuration, automation, and monitoring, you can deploy robust Rails applications that scale to meet any demand. Use this guide as your reference for deploying, tuning, and securing Puma in real-world scenarios. Happy deploying!

Learn more aboutΒ Rails
Learn more aboutΒ DevOps

47 thoughts on “Puma for Rails: Full Guide from Start to Scale”

Comments are closed.

Scroll to Top