Web Development | Ruby on Rails | React.js

Optimizing Phusion Passenger Settings

Proper configuration of Phusion Passenger is an aspect of optimization that frequently gets missed on production deployments of Rails / Sinatra applications. With all of the legitimate emphasis on preventing N+1 queries, not instantiating objects where a pluck will do and other Rails-level performance improvements, server level fine-tuning is typically ignored and inefficient default settings end up getting used.

On a recent project we noticed a threshold of traffic during load testing at which the server would start returning 503’s. After a lot of investigation, it eventually came down to the application server layer since everything else was barely strained or never solicited when the 503 responses starting pouring in.

By default, Phusion Passenger has a setting to control the max number of application processes that may simultaneously exist, and this PassengerMaxPoolSize is set to 6. This is fine when starting out, but once you start dealing with actual traffic you should definitely set it correctly.

The documentation provides a rule of thumb to estimate what this value should be based on your average application instance’s memory footprint and the total amount of memory you have.

max app processes = (total memory x 0.75) / application memory size

You can assess your application’s memory footprint with passenger-status:

$ passenger-status
Version : 5.0.21
Date    : 2016-12-07 09:54:11 -0500

----------- General information -----------
Max pool size : 6
App groups    : 2
Processes     : 2
Requests in top-level queue : 0

----------- Application groups -----------
/var/www/prod/app1/current (production):
  App root: /var/www/prod/app1/current
  Requests in queue: 0
  * PID: 15920   Sessions: 0       Processed: 3255    Uptime: 3d 3h 26m 46s
    CPU: 0%      Memory  : 98M     Last used: 6m 4s

/var/www/prod/app2/current (production):
  App root: /var/www/prod/app2/current
  Requests in queue: 0
  * PID: 18842   Sessions: 0       Processed: 31      Uptime: 3d 1h 55m 0s
    CPU: 0%      Memory  : 64M     Last used: 1h 17m

For a small Rails project running on a Linode instance shared with another Rails application (and where I’m running a bunch of other things, hence the more conservative coefficient of 0.5), this works out to this estimate:

max app processes  = (4096 x 0.5) / 100 = 20.48

You can alter this setting in wherever you’re configuring Phusion Passenger (in my case /etc/apache2/apache2.conf):

PassengerMaxPoolSize 20

Make sure you restart Apache (which I’m using here to run Passenger).

If you’re using the standalone flavor of Passenger 5, you can commit a Passengerfile.json to the root folder of your Rails app:

  "max_pool_size": 20

for 4.x, the file needs to be named passenger-standalone.json.

In this case, I’m running two applications on the same server so I could potentially adjust PassengerMaxInstancesPerApp to control the maximum number of processes per application based on their importance.

With this simple change, you can serve more concurrent users without any additional hardware upgrades or complex refactoring.