Production Rails Stack
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:alpine4 container_name: rails-nginx5 restart: unless-stopped6 ports: 7 - "${NGINX_PORT:-80}:80"8 volumes: 9 - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro10 - ./public:/app/public:ro11 depends_on: 12 - web1314 web: 15 build: 16 context: .17 dockerfile: Dockerfile18 container_name: rails-web19 restart: unless-stopped20 command: bundle exec puma -C config/puma.rb21 volumes: 22 - .:/app23 - bundle_cache:/usr/local/bundle24 environment: 25 - RAILS_ENV=production26 - RAILS_MASTER_KEY=${RAILS_MASTER_KEY}27 - DATABASE_URL=postgres://${DB_USER}:${DB_PASSWORD}@db:5432/${DB_NAME}28 - REDIS_URL=redis://redis:6379/029 depends_on: 30 - db31 - redis3233 sidekiq: 34 build: 35 context: .36 dockerfile: Dockerfile37 container_name: rails-sidekiq38 restart: unless-stopped39 command: bundle exec sidekiq40 volumes: 41 - .:/app42 - bundle_cache:/usr/local/bundle43 environment: 44 - RAILS_ENV=production45 - RAILS_MASTER_KEY=${RAILS_MASTER_KEY}46 - DATABASE_URL=postgres://${DB_USER}:${DB_PASSWORD}@db:5432/${DB_NAME}47 - REDIS_URL=redis://redis:6379/048 depends_on: 49 - db50 - redis5152 db: 53 image: postgres:16-alpine54 container_name: rails-db55 restart: unless-stopped56 environment: 57 - POSTGRES_USER=${DB_USER}58 - POSTGRES_PASSWORD=${DB_PASSWORD}59 - POSTGRES_DB=${DB_NAME}60 volumes: 61 - postgres_data:/var/lib/postgresql/data6263 redis: 64 image: redis:7-alpine65 container_name: rails-redis66 restart: unless-stopped67 volumes: 68 - redis_data:/data6970volumes: 71 postgres_data: 72 redis_data: 73 bundle_cache: .env Template
.env
1# Rails Production Stack2NGINX_PORT=8034# Rails master key (from config/master.key)5RAILS_MASTER_KEY=your-rails-master-key67# Database8DB_USER=rails9DB_PASSWORD=rails_password10DB_NAME=rails_productionUsage Notes
- 1Create Dockerfile with Ruby and Rails dependencies
- 2Configure puma.rb for production workers
- 3Run migrations: docker compose exec web rails db:migrate
- 4Precompile assets: docker compose exec web rails assets:precompile
- 5Sidekiq processes background jobs from Redis queues
- 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 file2cat > docker-compose.yml << 'EOF'3services:4 nginx:5 image: nginx:alpine6 container_name: rails-nginx7 restart: unless-stopped8 ports:9 - "${NGINX_PORT:-80}:80"10 volumes:11 - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro12 - ./public:/app/public:ro13 depends_on:14 - web1516 web:17 build:18 context: .19 dockerfile: Dockerfile20 container_name: rails-web21 restart: unless-stopped22 command: bundle exec puma -C config/puma.rb23 volumes:24 - .:/app25 - bundle_cache:/usr/local/bundle26 environment:27 - RAILS_ENV=production28 - RAILS_MASTER_KEY=${RAILS_MASTER_KEY}29 - DATABASE_URL=postgres://${DB_USER}:${DB_PASSWORD}@db:5432/${DB_NAME}30 - REDIS_URL=redis://redis:6379/031 depends_on:32 - db33 - redis3435 sidekiq:36 build:37 context: .38 dockerfile: Dockerfile39 container_name: rails-sidekiq40 restart: unless-stopped41 command: bundle exec sidekiq42 volumes:43 - .:/app44 - bundle_cache:/usr/local/bundle45 environment:46 - RAILS_ENV=production47 - RAILS_MASTER_KEY=${RAILS_MASTER_KEY}48 - DATABASE_URL=postgres://${DB_USER}:${DB_PASSWORD}@db:5432/${DB_NAME}49 - REDIS_URL=redis://redis:6379/050 depends_on:51 - db52 - redis5354 db:55 image: postgres:16-alpine56 container_name: rails-db57 restart: unless-stopped58 environment:59 - POSTGRES_USER=${DB_USER}60 - POSTGRES_PASSWORD=${DB_PASSWORD}61 - POSTGRES_DB=${DB_NAME}62 volumes:63 - postgres_data:/var/lib/postgresql/data6465 redis:66 image: redis:7-alpine67 container_name: rails-redis68 restart: unless-stopped69 volumes:70 - redis_data:/data7172volumes:73 postgres_data:74 redis_data:75 bundle_cache:76EOF7778# 2. Create the .env file79cat > .env << 'EOF'80# Rails Production Stack81NGINX_PORT=808283# Rails master key (from config/master.key)84RAILS_MASTER_KEY=your-rails-master-key8586# Database87DB_USER=rails88DB_PASSWORD=rails_password89DB_NAME=rails_production90EOF9192# 3. Start the services93docker compose up -d9495# 4. View logs96docker compose logs -fOne-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 | bashTroubleshooting
- 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
Components
nginxrailspumapostgresredissidekiq
Tags
#rails#ruby#production#sidekiq#nginx#postgres#redis
Category
Full Web StacksAd Space
Shortcuts: C CopyF FavoriteD Download