docker.recipes

Production Rails Stack

advanced

Production Ruby on Rails with Nginx, Puma, PostgreSQL, Redis, and Sidekiq for background jobs

Overview

NGINX is a high-performance web server and reverse proxy that has become the de facto standard for production web applications due to its event-driven architecture and exceptional performance under load. Originally developed by Igor Sysoev in 2004 to solve the C10K problem, NGINX excels at handling thousands of concurrent connections with minimal memory overhead, making it ideal for serving as a front-end proxy to Ruby on Rails applications running on Puma. This production Rails stack combines NGINX's high-performance request handling with Ruby on Rails running on Puma application servers, backed by PostgreSQL for reliable ACID-compliant data storage and Redis for caching and job queuing. Sidekiq leverages Redis to process background jobs asynchronously, while NGINX handles static asset serving and load balancing to Puma workers. This architecture separates concerns effectively: NGINX handles the heavy lifting of HTTP connections and static files, Puma serves dynamic Rails content, PostgreSQL ensures data integrity, and Redis provides fast session storage and job queuing. This stack is ideal for Rails applications that have outgrown simple development setups and need production-grade performance, reliability, and scalability. Startups moving from prototype to production, established companies running customer-facing Rails applications, and development teams requiring a battle-tested deployment architecture will benefit from this configuration. The combination provides the reliability needed for business-critical applications while maintaining the flexibility to scale horizontally by adding more Puma workers or Redis instances as traffic grows.

Key Features

  • Event-driven NGINX architecture handling thousands of concurrent connections with minimal memory usage
  • Puma multi-threaded, multi-process application server optimized for Rails concurrency model
  • PostgreSQL ACID compliance with advanced transaction support and JSON/JSONB document capabilities
  • Redis sub-millisecond response times for session storage, caching, and Sidekiq job queues
  • Sidekiq reliable background job processing with retry logic and job scheduling
  • NGINX reverse proxy with load balancing across multiple Puma workers
  • PostgreSQL logical replication and streaming replication for high availability
  • Redis Pub/Sub messaging for real-time features and inter-service communication

Common Use Cases

  • 1E-commerce platforms requiring reliable transaction processing and background order fulfillment
  • 2SaaS applications with user authentication, session management, and async email processing
  • 3Content management systems serving high traffic with efficient static asset delivery
  • 4API-first applications requiring fast response times and background data processing
  • 5Multi-tenant Rails applications with complex database queries and real-time notifications
  • 6Enterprise applications requiring audit trails, data integrity, and scheduled job processing
  • 7Rails applications with image processing, email delivery, and third-party API integrations

Prerequisites

  • Minimum 2GB RAM for the full stack (PostgreSQL 1GB, Redis 512MB, Rails/Puma 512MB)
  • Valid RAILS_MASTER_KEY for Rails credentials and encrypted configuration
  • Ruby Dockerfile with bundler, Rails dependencies, and production gems installed
  • Puma configuration file (config/puma.rb) with worker and thread settings for production
  • NGINX configuration with upstream Puma servers and static asset serving rules
  • Understanding of Rails asset pipeline, database migrations, and Sidekiq job classes

For development & testing. Review security settings, change default credentials, and test thoroughly before production use. See Terms

docker-compose.yml

docker-compose.yml
1services:
2 nginx:
3 image: nginx:alpine
4 container_name: rails-nginx
5 restart: unless-stopped
6 ports:
7 - "${NGINX_PORT:-80}:80"
8 volumes:
9 - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
10 - ./public:/app/public:ro
11 depends_on:
12 - web
13
14 web:
15 build:
16 context: .
17 dockerfile: Dockerfile
18 container_name: rails-web
19 restart: unless-stopped
20 command: bundle exec puma -C config/puma.rb
21 volumes:
22 - .:/app
23 - bundle_cache:/usr/local/bundle
24 environment:
25 - RAILS_ENV=production
26 - RAILS_MASTER_KEY=${RAILS_MASTER_KEY}
27 - DATABASE_URL=postgres://${DB_USER}:${DB_PASSWORD}@db:5432/${DB_NAME}
28 - REDIS_URL=redis://redis:6379/0
29 depends_on:
30 - db
31 - redis
32
33 sidekiq:
34 build:
35 context: .
36 dockerfile: Dockerfile
37 container_name: rails-sidekiq
38 restart: unless-stopped
39 command: bundle exec sidekiq
40 volumes:
41 - .:/app
42 - bundle_cache:/usr/local/bundle
43 environment:
44 - RAILS_ENV=production
45 - RAILS_MASTER_KEY=${RAILS_MASTER_KEY}
46 - DATABASE_URL=postgres://${DB_USER}:${DB_PASSWORD}@db:5432/${DB_NAME}
47 - REDIS_URL=redis://redis:6379/0
48 depends_on:
49 - db
50 - redis
51
52 db:
53 image: postgres:16-alpine
54 container_name: rails-db
55 restart: unless-stopped
56 environment:
57 - POSTGRES_USER=${DB_USER}
58 - POSTGRES_PASSWORD=${DB_PASSWORD}
59 - POSTGRES_DB=${DB_NAME}
60 volumes:
61 - postgres_data:/var/lib/postgresql/data
62
63 redis:
64 image: redis:7-alpine
65 container_name: rails-redis
66 restart: unless-stopped
67 volumes:
68 - redis_data:/data
69
70volumes:
71 postgres_data:
72 redis_data:
73 bundle_cache:

.env Template

.env
1# Rails Production Stack
2NGINX_PORT=80
3
4# Rails master key (from config/master.key)
5RAILS_MASTER_KEY=your-rails-master-key
6
7# Database
8DB_USER=rails
9DB_PASSWORD=rails_password
10DB_NAME=rails_production

Usage Notes

  1. 1Create Dockerfile with Ruby and Rails dependencies
  2. 2Configure puma.rb for production workers
  3. 3Run migrations: docker compose exec web rails db:migrate
  4. 4Precompile assets: docker compose exec web rails assets:precompile
  5. 5Sidekiq processes background jobs from Redis queues
  6. 6Monitor Sidekiq at /sidekiq with authentication

Individual Services(5 services)

Copy individual services to mix and match with your existing compose files.

nginx
nginx:
  image: nginx:alpine
  container_name: rails-nginx
  restart: unless-stopped
  ports:
    - ${NGINX_PORT:-80}:80
  volumes:
    - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
    - ./public:/app/public:ro
  depends_on:
    - web
web
web:
  build:
    context: .
    dockerfile: Dockerfile
  container_name: rails-web
  restart: unless-stopped
  command: bundle exec puma -C config/puma.rb
  volumes:
    - .:/app
    - bundle_cache:/usr/local/bundle
  environment:
    - RAILS_ENV=production
    - RAILS_MASTER_KEY=${RAILS_MASTER_KEY}
    - DATABASE_URL=postgres://${DB_USER}:${DB_PASSWORD}@db:5432/${DB_NAME}
    - REDIS_URL=redis://redis:6379/0
  depends_on:
    - db
    - redis
sidekiq
sidekiq:
  build:
    context: .
    dockerfile: Dockerfile
  container_name: rails-sidekiq
  restart: unless-stopped
  command: bundle exec sidekiq
  volumes:
    - .:/app
    - bundle_cache:/usr/local/bundle
  environment:
    - RAILS_ENV=production
    - RAILS_MASTER_KEY=${RAILS_MASTER_KEY}
    - DATABASE_URL=postgres://${DB_USER}:${DB_PASSWORD}@db:5432/${DB_NAME}
    - REDIS_URL=redis://redis:6379/0
  depends_on:
    - db
    - redis
db
db:
  image: postgres:16-alpine
  container_name: rails-db
  restart: unless-stopped
  environment:
    - POSTGRES_USER=${DB_USER}
    - POSTGRES_PASSWORD=${DB_PASSWORD}
    - POSTGRES_DB=${DB_NAME}
  volumes:
    - postgres_data:/var/lib/postgresql/data
redis
redis:
  image: redis:7-alpine
  container_name: rails-redis
  restart: unless-stopped
  volumes:
    - redis_data:/data

Quick Start

terminal
1# 1. Create the compose file
2cat > docker-compose.yml << 'EOF'
3services:
4 nginx:
5 image: nginx:alpine
6 container_name: rails-nginx
7 restart: unless-stopped
8 ports:
9 - "${NGINX_PORT:-80}:80"
10 volumes:
11 - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
12 - ./public:/app/public:ro
13 depends_on:
14 - web
15
16 web:
17 build:
18 context: .
19 dockerfile: Dockerfile
20 container_name: rails-web
21 restart: unless-stopped
22 command: bundle exec puma -C config/puma.rb
23 volumes:
24 - .:/app
25 - bundle_cache:/usr/local/bundle
26 environment:
27 - RAILS_ENV=production
28 - RAILS_MASTER_KEY=${RAILS_MASTER_KEY}
29 - DATABASE_URL=postgres://${DB_USER}:${DB_PASSWORD}@db:5432/${DB_NAME}
30 - REDIS_URL=redis://redis:6379/0
31 depends_on:
32 - db
33 - redis
34
35 sidekiq:
36 build:
37 context: .
38 dockerfile: Dockerfile
39 container_name: rails-sidekiq
40 restart: unless-stopped
41 command: bundle exec sidekiq
42 volumes:
43 - .:/app
44 - bundle_cache:/usr/local/bundle
45 environment:
46 - RAILS_ENV=production
47 - RAILS_MASTER_KEY=${RAILS_MASTER_KEY}
48 - DATABASE_URL=postgres://${DB_USER}:${DB_PASSWORD}@db:5432/${DB_NAME}
49 - REDIS_URL=redis://redis:6379/0
50 depends_on:
51 - db
52 - redis
53
54 db:
55 image: postgres:16-alpine
56 container_name: rails-db
57 restart: unless-stopped
58 environment:
59 - POSTGRES_USER=${DB_USER}
60 - POSTGRES_PASSWORD=${DB_PASSWORD}
61 - POSTGRES_DB=${DB_NAME}
62 volumes:
63 - postgres_data:/var/lib/postgresql/data
64
65 redis:
66 image: redis:7-alpine
67 container_name: rails-redis
68 restart: unless-stopped
69 volumes:
70 - redis_data:/data
71
72volumes:
73 postgres_data:
74 redis_data:
75 bundle_cache:
76EOF
77
78# 2. Create the .env file
79cat > .env << 'EOF'
80# Rails Production Stack
81NGINX_PORT=80
82
83# Rails master key (from config/master.key)
84RAILS_MASTER_KEY=your-rails-master-key
85
86# Database
87DB_USER=rails
88DB_PASSWORD=rails_password
89DB_NAME=rails_production
90EOF
91
92# 3. Start the services
93docker compose up -d
94
95# 4. View logs
96docker compose logs -f

One-Liner

Run this command to download and set up the recipe in one step:

terminal
1curl -fsSL https://docker.recipes/api/recipes/production-rails-stack/run | bash

Troubleshooting

  • NGINX 502 Bad Gateway errors: Verify Puma is running and accessible on configured upstream port, check container networking between nginx and web services
  • Sidekiq jobs not processing: Ensure Redis is accessible from sidekiq container and REDIS_URL environment variable matches Redis service configuration
  • PostgreSQL connection refused: Check DATABASE_URL format includes correct credentials and that db service is fully initialized before Rails containers start
  • Rails assets not loading: Verify public volume mount in nginx service and run 'docker compose exec web rails assets:precompile' in production environment
  • Puma workers crashing: Increase memory allocation and check for memory leaks, review puma.rb worker and thread configuration for available resources
  • Redis memory issues: Monitor Redis memory usage with 'docker compose exec redis redis-cli info memory' and configure appropriate maxmemory policies

Community Notes

Loading...
Loading notes...

Download Recipe Kit

Get all files in a ready-to-deploy package

Includes docker-compose.yml, .env template, README, and license

Ad Space