Performance

Rails Out of Memory Fix: Phusion Passenger Tuning

BLUF (Bottom Line Up Front): If your legacy Rails application randomly goes offline under traffic spikes, the Linux OOM Killer is likely terminating your web server. This happens because Phusion Passenger spawns new worker processes dynamically. If PassengerMaxPoolSize is unconfigured, it will spawn processes until the server runs out of RAM. The fix is strictly limiting the pool size based on a mathematical calculation of your available memory.

Phase 1: The Silent Server Crash

Legacy Rails monoliths are heavy. A single Ruby process can consume 300MB to 500MB of RAM.

Synthetic Engineering Context: The OOM Killer

You receive an alert that the server is down. When you check the system logs, you see the kernel intervened to save the operating system.

# Terminal Output: dmesg logs
$ dmesg -T | grep -i oom
[Tue Apr 23 10:15:22 2026] ruby invoked oom-killer: gfp_mask=0x100cca(GFP_HIGHUSER_MOVABLE), order=0, oom_score_adj=0
[Tue Apr 23 10:15:23 2026] Out of memory: Killed process 14532 (ruby) total-vm:854320kB, anon-rss:512000kB, file-rss:0kB, shmem-rss:0kB

Passenger saw increased web traffic and spawned a 10th Ruby worker, pushing the server’s 4GB RAM capacity over the edge.

Phase 2: Tuning the Passenger Config

You must define hard boundaries in your Nginx or Apache configuration to prevent Passenger from destroying the host.

Execution: Calculating the Pool Size

First, find out exactly how much RAM one worker uses. Run passenger-memory-stats.

$ sudo passenger-memory-stats
# Assume the "Private" memory of a Ruby process averages 400MB.

If your server has 4096MB of RAM, reserve 1024MB for the OS and background tasks. That leaves 3072MB for Passenger. 3072MB / 400MB = 7.6 Your absolute maximum pool size is 7.

Execution: The Nginx Configuration

Update your Nginx config to lock in these limits.

# /etc/nginx/nginx.conf or your site block
http {
    # ...
    passenger_root /usr/lib/ruby/vendor_ruby/phusion_passenger/locations.ini;
    passenger_ruby /usr/local/rvm/gems/ruby-2.7.6/wrappers/ruby;

    # The maximum number of total worker processes
    passenger_max_pool_size 7;
    
    # Pre-start workers to avoid slow first-requests
    passenger_min_instances 3;
    
    # Terminate workers that balloon in memory (e.g., above 600MB)
    passenger_memory_limit 600;
}

Phase 3: Next Steps & Risk Mitigation

Capping the PassengerMaxPoolSize stops the crashes, but introduces a new problem: if traffic exceeds your 7 workers, requests will queue up and users will experience slow load times (504 Gateway Timeouts). You must monitor the Passenger queue length and scale horizontally (add more servers) if the queue grows consistently.

Need Help Stabilizing Your Legacy App? Server tuning is a delicate balance of memory, CPU, and response times. Our DevOps team at USEO stabilizes legacy Rails infrastructure, eliminating OOM crashes and configuring precise auto-scaling.

Contact us for a Technical Debt Audit