Performance Tuning for Phusion Passenger (an Introduction)

Phusion Passenger (aka mod_rails) allows for easy and scalable deployment of Ruby on Rails applications on Apache or Nginx servers. Part of what makes it so easy is that it comes with suitable default settings right out of the box, so that you don’t need to concern yourself with any of the details when deploying your application to production.

However, once you’ve launched your application and people start actually using it, you may find server experiencing excessive swapping once the traffic begins to pick up. Before getting too deep into scaling, caching, upgrading your server, etc., there are a few Passenger settings you can tune to wring out the best performance your existing stack. I’ll address these settings with a few rules of thumb we’ve gathered from the Passenger documentation and simple trial-and-error.

Before getting started, we highly recommend employing Munin (instructions for Munin with Passenger here) and NewRelic Performance Monitoring for your Passenger/Rails application, so that you can fine-tune your settings for the best performance.

Note: the following settings are specific for Apache, you will need to modify the syntax/file-names for Nginx.

Per-server Settings

These settings are set per-server, meaning if you have multiple applications running on the server, these should be set only once throughout all of your .conf files. If you have only one Rails/Passenger app on the server, then you may set these in that application’s .conf file. On Ubuntu, this may be found at /etc/apache2/sites-available/yourapp.conf.

A more appropriate place for these however would be in Apache’s .conf file.

PassengerMaxPoolSize and PassengerPoolIdleTime

/etc/apache2/apache2.conf:

# PassengerMaxPoolSize
# Default: 6
# For 2gb RAM: 30
# For 256 slice with MySQL running: 2
PassengerMaxPoolSize 2

# PassengerPoolIdleTime
# Recommended to be avg time per page * 2
# In Google Analytics... (Avg time on site / Avg page views) * 2
# Default: 300
PassengerPoolIdleTime 150

You can essentially copy and paste this into your apache conf file. I’ve added some notes in there to help you remember how they should be tuned.

PassengerMaxPoolSize: This is the number of Passenger instances that can be spawned at once for your site. This basically relates directly to the amount of RAM that is available to your application. However, it is not a linear relationship. It’s more of an exponential relationship. Numbers that we and others have found to work well are:

RAM (MB) PassengerMaxPoolSize
256 2
512 6 (default)
1024 (1GB) 15
2048 (2GB) 30

Since this depends on available RAM, this will vary depending on what other processes are running on your server, such as MySQL, etc. If you have Munin installed with the Passenger plugins, you can monitor the Passenger Memory Stats and Passenger Status graphs to tweak this as necessary.

PassengerPoolIdleTime: This is essentially the amount of time (in seconds) that each Passenger instance continues to run after it becomes idle (i.e. someone stops clicking around the site). According to the documentation, this should be set to the average time a visitor views a page multiplied by 2. This of course prevents Passenger instances from shutting down (and thus having to re-spawn) while someone is viewing a page.

If your application has been in production long enough to have a few hundred viewers and you have Google Analytics installed, an easy way to get the ideal setting for your application is to take the Average Time on Site, divide by the Average Page Views per Visit, and multiply by 2.

( Average Time on Site / Average Page Views per Visit ) x 2

PassengerMaxInstancesPerApp (optional)

If you do have more than one Rails/Passenger application on the server, you may optionally set PassengerMaxInstancesPerApp.

PassengerMaxInstancesPerApp: This is the maximum number of instances each application may use of the total PassengerMaxPoolSize. Therefore, PassengerMaxInstancesPerApp must be smaller than PassengerMaxPoolSize.

For example, if you have 2 applications running on one 256MB server, you may want to set PassengerMaxInstancesPerApp to 1, so that one app may never hog all of the rails instances at the expense of the other app. If you have 2 applications running on a 512MB server (PassengerMaxPoolSize 6), then you may want to set cci]PassengerMaxInstancesPerApp[/cci] to 4 or 5 to allow each app to better handle higher load (hopefully while the other app is under less load).

RailsAutoDetect

To get a little more performance out of your server, you can also turn the RailsAutoDetect setting of Passenger off. This will make it so that each request through Apache does not check to see if it is a Rails request that requires Passenger. However, this will now require that you explicitly tell Apache which requests are Rails and do require Passenger, using RailsBaseURI. This is easy though.

Again, in your apache2.conf file:

# Must be used with RailsBaseURI in Vhost
RailsAutoDetect off

Now, within your the Vhost block in your site’s .conf file, you will need to set the RailsBaseURI:

<VirtualHost *:80>
  ...
  RailsBaseURI /
</VirtualHost>

Here is a complete example of the Passenger block in your apache2.conf file:

# PassengerMaxPoolSize
# Default: 6
# For 2gb RAM: 30
# For 256 slice with MySQL running: 2
PassengerMaxPoolSize 2

# PassengerPoolIdleTime
# Recommended to be avg time per page * 2
# In Google Analytics... (Avg time on site / Avg page views) * 2
# Default: 300
PassengerPoolIdleTime 144

# PassengerMaxInstancesPerApp < PassengerMaxPoolSize
PassengerMaxInstancesPerApp 1

# Must be used with RailsBaseURI in Vhost
RailsAutoDetect off

And a simple sites-available/yourapp.conf may look like this:

<VirtualHost *:80>
  ServerName www.yourapp.com
  DocumentRoot /home/deploy/yourapp/current/public
  <Directory "/home/deploy/yourapp/current/public">
    Options FollowSymLinks
    AllowOverride None
    Order allow,deny
    Allow from all
  </Directory>
  RailsBaseURI /
</VirtualHost>

More Passenger Configuration Settings

These settings so far only scratch the surface of Passenger configuration. Give the Passenger documentation a good read-through for more information on configuring Passenger.

Further Reading: Apache Settings

At this point, if you still need to squeeze more performance out of your server (and you will eventually), you’ll want to configure Apache to utilize its resources most efficiently with Passenger. I recommend reading through this thread on the Slicehost forums to get a good idea just how much trial-and-error this will take. Keep a close eye on your server when you tweak these settings, you may end up making it slower several times before finding that sweet spot.

You may read the documentation for each setting in the Apache docs (note that these are the Apache 1.3 docs, but they still explain purpose of each setting well). For more information using these settings to reduce the RAM consumed by Apache, see this other Slicehost forum and this blog post on saving RAM with Rails and Passenger.

If you’re impatient and just want to get something better than default, here are some numbers that worked well for us on a recent application running on a 256MB VPS on Slicehost. These settings can be found in your apache2.conf file.

# prefork MPM
# StartServers: number of server processes to start
# MinSpareServers: minimum number of server processes which are kept spare
# MaxSpareServers: maximum number of server processes which are kept spare
# MaxClients: maximum number of server processes allowed to start
# MaxRequestsPerChild: maximum number of requests a server process serves
<IfModule mpm_prefork_module>
  StartServers          2
  MinSpareServers       1
  MaxSpareServers       5
  MaxClients           10
  MaxRequestsPerChild   100
</IfModule>
# worker MPM
# StartServers: initial number of server processes to start
# MaxClients: maximum number of simultaneous client connections
# MinSpareThreads: minimum number of worker threads which are kept spare
# MaxSpareThreads: maximum number of worker threads which are kept spare
# ThreadsPerChild: constant number of worker threads in each server process
# MaxRequestsPerChild: maximum number of requests a server process serves
<IfModule mpm_worker_module>
  StartServers          2
  MaxClients           10
  MinSpareThreads      25
  MaxSpareThreads      75
  ThreadsPerChild      25
  MaxRequestsPerChild   100
</IfModule>

Tags: , , , , , ,

15 Responses to “Performance Tuning for Phusion Passenger (an Introduction)”

  1. Tuningbox Says:

    Great info! I will use this on my apache server. Thank you for sharing.

  2. Caffeine Driven Development » Blog Archive » L33t Links #84 Says:

    [...] Performance Tuning for Phusion Passenger [...]

  3. Chris Gunther Says:

    Th Phusion Passenger docs state that PassengerMaxPoolSize and PassengerPoolIdleTime are to appear once, in the global server configuration, not on a per application basis. Are the docs outdated or are your examples incorrect?

  4. steve Says:

    Hey Chris, yes you are correct that those are per-server settings instead of per-application settings. I had written this based on having one application per server, but was trying to write it to be more universally applicable. I guess I got a little ahead of myself with the title and intro for that section. Anyway, I’ve updated that section. And since we’re talking about having multiple apps per server, I added a section about PassengerMaxInstancesPerApp. Thanks for the catch!

  5. Make Sure Your Rails Application is Actually Caching (and not just pretending) - Alfa Jango Blog » Blog Archive Says:

    [...] Alfa Jango Blog Engineering, Software, and Entrepreneurship « Performance Tuning for Phusion Passenger (an Introduction) [...]

  6. Chris Gunther Says:

    Thanks for the clarification Steve.

    I’d love it if some of those settings could be on an application basis. I wish I could limit my staging site to 1 instance so it never gets out of hand, but allow the production environment to use any remaining instances. It’d be nice to change the PoolIdleTime on a per application basis as well so the staging instance could spin down quickly freeing up the instance whereas production could stay open longer.

  7. Suzette Says:

    Great clarification. I have learned a lot as well. Railsautodetect is a great innovation.

  8. steve Says:

    Yes, I agree that’d definitely be nice. Perhaps in a future version. At any rate, if you have multiple sites, sometimes it’s wise to move all of your staging environments to a separate staging server, provided your environments are all identical.

  9. chiptuning Says:

    I tested it on my server, working good.Thanks for the sharing.

  10. Domonique Dieckmann Says:

    http://counterpulse.org/performing-diaspora/artists/

  11. Vinay Says:

    Hi Steve,

    A very nice read. I wish I had come across this post a little sooner, it would’ve been a good reference for a post I just made myself. It would be nice to get your comments on it though – http://tenmiles.com/blog/2010/07/understanding-optimizing-your-server-part-2-apache-passenger-directives/ . The next part of this series is metrics and tuning so im definitely gonna check out Munin for that :) .

  12. Understanding & Optimizing your Server (Part 2) – Apache & Passenger Directives « Tenmiles Blog Says:

    [...] A similar, very useful blog post here [...]

  13. Épületgépészet Says:

    Thx the good ideas..
    Respect…

  14. Szonyegtisztitas Says:

    This is a good idea!
    There is some good advice?

  15. Masszőr Says:

    This will make it so that each request through Apache does not check to see if it is a Rails request that requires Passenger.

Leave a Reply

You may include code snippets in your comment using this syntax.




Entries (RSS) and Comments (RSS)