article blog.rubyonrails.ba
Rails Deployment Memory Showdown: Docker vs Traditional 07/11/2025 ~ Views: 1940
When deploying multiple Ruby on Rails applications, choosing between containerized (Docker/Kamal) and traditional (Nginx/Passenger) deployment strategies can significantly impact your server resource requirements. Let's break down the real memory footprint of each approach.

The Contenders

We'll compare three deployment strategies for hosting multiple Rails apps with SQLite3 on a single server:

  1. Docker + Kamal - Modern containerized deployment
  2. Traditional (Capistrano + Nginx + Puma) - Separate processes per app
  3. Traditional (Nginx + Passenger) - Copy-on-Write optimization

Memory Breakdown: Traditional Deployment

Typical Stack: Nginx + Puma + SQLite3

Component RAM Usage Notes
| Nginx  | 10-30 MB  | Shared across all apps
| Ruby process  | 80-150 MB  | Base Ruby VM
| Rails app (loaded)  | +100-200 MB  | Per app
| Puma workers (4)  | +400-800 MB  | 4 × (Ruby + app)
| SQLite3  | 0-50 MB  | In-process, minimal overhead
| OS overhead  | 200-400 MB  | Shared services

Single Rails App Memory

Nginx (10 MB) + Puma master (150 MB) + 4 workers (4 × 150 MB) = ~760 MB


The Passenger Advantage: Copy-on-Write

This is where traditional deployment shines. Passenger uses Copy-on-Write (CoW) to share memory pages between application instances:

Nginx (10 MB) + Passenger core (50 MB) + 4 app instances with CoW
First instance:  150 MB (full)
Instances 2-4:   +50 MB each (shared memory pages)
Total: ~360 MB instead of 760 MB

Head-to-Head: 3 Rails Apps on One Server

Memory Comparison

Deployment Type RAM per App Total RAM (3 apps) Disk Space
| Docker + Kamal  | 500-650 MB  | ~1.8 GB  | ~1 GB (images)
| Traditional (separate Puma)  | 600-800 MB  | ~2.1 GB  | ~300 MB (code)
| Passenger (CoW)  | 300-400 MB  | ~1 GB  | ~300 MB (code)
Detailed Memory Breakdown

Option A: Docker + Kamal

App A container: 550 MB
App B container: 600 MB  
App C container: 500 MB
Traefik:          60 MB
─────────────────────────
Total:         ~1.7 GB


Option B: Traditional (Capistrano + Nginx + separate Puma per app)

Nginx:            20 MB (shared)
App A (Puma):    700 MB
App B (Puma):    750 MB
App C (Puma):    680 MB
─────────────────────────
Total:         ~2.15 GB


Option C: Traditional (Nginx + Passenger with CoW)

Nginx:            20 MB
Passenger:        50 MB
App A (4 workers): 350 MB (CoW shared)
App B (4 workers): 320 MB (CoW shared)
App C (4 workers): 300 MB (CoW shared)
─────────────────────────
Total:          ~1.04 GB  ⭐ WINNER


Key Insights

1. Passenger is Most Memory-Efficient

Copy-on-Write sharing means the Ruby VM and loaded gems are shared across workers, saving 50-70% RAM compared to isolated processes. This makes it ideal for hosting many small applications on a single server.

2. Docker Overhead: ~50-100 MB per Container

Each container includes runtime overhead for network namespacing and isolation. While not enormous, it adds up when running multiple applications.

3. SQLite3 is Nearly Free

Since SQLite3 runs in-process with no separate database server, memory overhead is minimal—typically just in-memory caching for active datasets.

4. Disk Space Advantage: Traditional Deployment

Traditional:  300 MB (Ruby + gems in system paths)
Docker:      ~1 GB (3 images with layer sharing)


Capacity Planning

On a 2 GB RAM Server

Deployment Max Apps Why
| Passenger  | 4-5 apps  | CoW memory sharing
| Docker/Kamal  | 2-3 apps  | Isolation overhead
| Separate Puma  | 2 apps  | No memory sharing
On a 4 GB RAM Server


Deployment Max Apps Why
| Passenger  | 8-10 apps  | Still most efficient
| Docker/Kamal  | 5-6 apps  | Comfortable headroom
| Separate Puma  | 4-5 apps  | Works but wasteful
The Verdict

Memory Champion: Passenger with Copy-on-Write uses 40-50% less RAM than Docker-based deployments.

Choose Docker/Kamal When:

  • You need strong isolation between apps (different Ruby versions, conflicting dependencies)
  • You want zero-downtime deploys and instant rollbacks
  • You have adequate RAM (4+ GB for multiple apps)
  • You value deployment consistency across environments
  • You're deploying to multiple servers or cloud platforms


Choose Traditional (Passenger) When:

  • Running many small applications on limited RAM
  • Apps are stable and don't require frequent rollbacks
  • All apps can share the same Ruby version
  • Working with a tight budget (e.g., $4-6/month VPS with 2 GB RAM)
  • Maximum resource efficiency is the priority

Real-World Recommendation

For Rails applications using SQLite3 where memory is constrained, Passenger delivers unmatched efficiency. However, if you need the operational benefits of containerization and have sufficient resources, Docker with Kamal provides excellent developer experience and deployment reliability.

The best choice depends on your specific priorities: resource efficiency versus operational convenience. Both are valid approaches—choose based on your constraints and requirements.

Have you compared these deployment strategies in your own projects? What memory usage patterns did you observe? Share your experiences in the comments below.

NOTE: Article update/revised partially generated by latest AP services Grok/Claude
(note for primitive assholes who have issues with using AI in writing)

Tags: rails deployment kamal capistrano docker

Back

OPEN TO HIRE
yes blog.rubyonrails.ba
Nezir Zahirovic
Ruby On Rails Full stack last 10 years.
C#, ASP.NET, JavaScript, SQL, CSS, Bootstrap 11 years.

Top articles

Best Apps for Staying in Contact With Family and Friends >>>
10 Simple Ways to Stay in Touch With Family Who Live Far ... >>>
Why Checking on Friends Regularly Is More Important Than ... >>>
Hey I Am Alive – The Easiest Way to Stay in Touch With Pe... >>>
🚨 LinkedIn → GitHub → Web3: How a “Senior Fullstack” Offe... >>>