Passenger

From DreamHost
(Redirected from Passenger WSGI)
Jump to: navigation, search

DreamHost supports Passenger, free on every hosting plan!

Enterprise logo.png

Overview

Passenger [1] (informally also known as mod_rails and mod_rack) is a module for the Apache HTTP Server that greatly simplifies the deployment of Ruby applications, in particular those built using the Ruby on Rails framework. The software is developed by a small company called Phusion. Passenger is the preferred way to deploy and host Ruby on Rails applications across all DreamHost servers.

Use of Passenger vs. FastCGI

Passenger should only be enabled if you intend to run a Ruby on Rails (RoR) or other Ruby/Python-based program as the sole application for the entire domain or sub-domain. Passenger directs essentially all requests for the designated domain/subdomain to the associated Rack-compliant application. So it's best to leave Passenger disabled if you do not actually need it. In other words, you should enable Passenger if you want to access your application via myapp.mydomain.com or www.myapp.mydomain.com or www.mydomain.com. If you want to access your application via www.mydomain.com/path-to-myapp then use FastCGI instead of passenger.

Configuration Steps

  • To enable Passenger, edit any of your fully hosted domains or add a new domain (or sub-domain) from the Manage Domains area of our web panel, and check the Passenger (Ruby/Python apps only) option. You must also specify the location of the 'public' subdirectory of the Ruby on Rails (or other Rack-compliant) application at the same time.
  • Place the code for your application in the directory specified in the previous step.
  • There is no next step! It's that easy.

IMPORTANT NOTE: Whenever the code or configuration files for your application are modified, you must create or update the modification date of the file "tmp/restart.txt" in the application's root directory tree in order trigger Passenger to reinitialize the application. Passenger caches many resources and so changes will generally not be recognized unless the modification date of "tmp/restart.txt" is changed. The most common method to make this change is to invoke "touch tmp/restart.txt" from the shell. (Ruby on Rails automatically creates a directory named "tmp". If you are creating non-RoR application, you may need to create the "tmp" directory manually.)

Basic Operation

When a request is made to a domain (or subdomain) with Passenger enabled, the Apache HTTP Server passes the request to Passenger. Passenger first looks for an appropriately-named HTML or CGI file in the domain's (or subdomain's) "public" subdirectory. If no matching file is found in "public", the request is passed to Passenger's Rack interface. Note that this use of the "public" subdirectory meshes precisely with the way that Ruby on Rails makes use of the same subdirectory.

In order to generate a response, Rack looks for a file named "config.ru" in the domain(or subdomain) root directory (i.e. the parent of the domain's "public" directory.) Rack requires that you place the appropriate Ruby code into "config.ru" to invoke your desired web framework or application to handle the request. (See Rack for more information about how information is passed through the Rack interface.)

Under normal circumstances, Ruby on Rails (RoR) will automatically create and initialize all of the files and directories needed to interface with Passenger/Rack. When running a RoR application, the only Rack-related files you are likely to modify are possibly adding GEM_PATH information to "config.ru" and "touching" the "tmp/restart.txt" file.

The "public" Subdirectory

Passenger maps the directory named "public" to be the document root for your domain/subdomain. In particular, if a static HTML file named "public/index.html" exists, then it will be used as the response for requests for the root document (i.e. "/"). Thus, if you want your application to handle requests for the root document, then you must first remove "public/index.html" if it exists. Please note that, by default, Ruby on Rails creates just such a static "public/index.html" file.

Likewise, a file inside the "public" subdirectory that is named with one of the suffixes recognized by Apache (e.g. "public/foo.cgi" or "public/foo.pl") will be treated as an executable CGI script in the usual Apache fashion. (See CGI for more information.)

Use with Other Web Frameworks

Passenger is most often used in conjunction with Ruby on Rails. However, it has been known to work with a variety of other Rack-compliant web frameworks including (but not limited to) Camping, Padrino, Sinatra, and Ramaze. However, little or no support is provided by Dreamhost for these other frameworks.

Troubleshooting

Common Problems

Below are some of the most common Passenger-related problems people run into.

Session Errors Relating to Rack

This is perhaps the most common problem. The problem is that Rails 2.3.x is incompatible with versions of Passenger prior to version 2.1.2. Many of DreamHost's older Sarge boxes are running Passenger 2.0.6. You can find out what version of Passenger the apache service your site is being hosted on is using by running the following command via SSH:

dpkg -l | grep passenger

The output should look something like this:

ii  ndn-passenger  2.2.4-1        Phusion Passenger (mod_rails)

If it displays earlier versions than 2.2.4 than this might be the problem. Sometimes dpkg reports an incorrect version though, but tech support can verify the version running for you. Symptoms of this problem are also identifiable by exceptions mentioning "rewind".

Application Not Restarting

This generally isn't actually a problem -- it's only a perceived problem. In versions of Passenger prior to 2.1, you would restart your application by touching the restart.txt file in the tmp directory. The next request your application processed would restart it and remove that file. Thus, the common practice to see if the application restarted was to see if the restart.txt file was still there or not. In Passenger 2.1 the behavior of the restart mechanism changed. Now Passenger simply checks the modification timestamp on the restart.txt file. If it's more recent than the last time it checked, it restarts. The file is not removed. This was done in large part so that restarting applications would work seamlessly on NFS shares. On lower traffic sites, you can usually still visually see the application restarting by "priming" a page (loading it a couple times in your browser), touching the restart.txt file and then loading that page again. It should take a noticeably longer time to load than it had the previous couple of times. This won't really work on live sites that are getting traffic though as it's entirely possible another person's request restarted the application by the time you test it yourself.

Broken Pipe Error

Passenger error broken pipe.jpg
If you get an error message mentioning a broken pipe with the exception class PhusionPassenger::Railz::ApplicationSpawner::Error as depicted in the image to the right, then you're most likely running into memory limit problems. Basically, what's happening is the request being processed by Passenger caused your user memory limit on the server to be exceeded and the Rails process that was handling that request was killed. If you're seeing this error, then it's usually a dead giveaway that this is happening. Unfortunately, there isn't a whole lot that can be done about this since even a very basic standard Rails application requires a lot of memory up front. In fact, enough memory to hit your memory limit almost immediately -- especially if you have other websites being hosted on your account. This will happen even more frequently if you have more than one Rails application being hosted from the same account. The best way to ensure that you don't run into this problem is to move your Rails sites over to a DreamHost PS. That will give you guaranteed, scalable memory that you can adjust until your memory needs are met.

Production Log Isn't Being Written To

This problem can be a bit tricky to figure out until you know what causes it. It's almost always caused by a misconfigured database.yml file. If your database.yml file isn't configured correctly Passenger will die a horrible death before your application is even loaded. As such, you get no helpful error messages from Passenger and your production.log file isn't touched.

Missing connection settings

Some versions of Passenger really don't like it if your database.yml file doesn't at least include settings (they can be blank) for all environments. The symptom of this happening is your application endlessly loading with no output to the production.log file. So, make sure you leave the connection information for all environments in your database.yml file -- even if you're only using the production settings.

Using a socket connection rather than a host

This mistake is simply not changing from using a socket connection to a host. The default MySQL connection information looks like this:

production:
  adapter: mysql
  encoding: utf8
  reconnect: false
  database: bar_production
  pool: 5
  username: root
  password: 
  socket: /tmp/mysql.sock

As you can see, it's configured to use a socket connection by default. You need to make sure you replace that line with correct host, username, and password information for a database created in the Goodies -> Manage MySQL area of your web panel. A correct configuration might look like this:

production:
  adapter: mysql
  encoding: utf8
  reconnect: false
  database: bar_production
  pool: 5
  username: exampleuser
  password: jWr873dnaiu12
  host: mysql.example.com

Misnaming parameters

Another common mistake is simply having a necessary parameter misnamed. This can happen easier than it sounds because when you generate a Rails application it defaults to generating a sqlite3 database.yml configuration file. You have to manually specify that you want a MySQL file (rails --database mysql <projectname>) or you'll have to change it manually. Since a lot of people don't realize this, they end up changing it after the fact and sqlite3 config files don't have username or host parameters! Here's what a default sqlite3 section looks like:

production:
  adapter: sqlite3
  database: db/production.sqlite3
  pool: 5
  timeout: 5000

So, it would be easy enough to modify that to look something like this:

production:
  adapter: mysql
  database: bar_production
  pool: 5
  user: exampleuser
  password: jWr873dnaiu12
  host: mysql.example.com

See the problem? In that configuration the "username" parameter was called "user" instead. This is a very common error and easy to miss. It should actually look like this:

production:
  adapter: mysql
  database: bar_production
  pool: 5
  username: exampleuser
  password: jWr873dnaiu12
  host: mysql.example.com

Passenger Can't Find Rails

I had a problem trying to get the latest (0.9.10) Fat Free CRM to run on Dreamhost. I kept getting an error that passenger couldn't find rails. Fat Free CRM included a config.ru. Once I removed that file, Fat Free CRM started up fine. Something to keep in mind if you have issues.

Uncommon Problems

Below are oddball problems you may encounter when running under Passenger.

ERB comments cause the beginning of your view to vanish

You may be tempted to begin a view with a comment to help you remember which file you're editing. This can be helpful if, say, you have "index.html.erb" under many different controllers. You may be tempted to do this:

This view is broken:

 <% # Help controller index: %>
 <p> blah blah blah...
 <%= link_to "help topic 1", help_topic_one %>
 more blah blah blah</p>

What will happen is that the erb processor will summarily treat as a comment everything up to the next erb directive. It will literally not appear in the output stream sent to the browser. In the above example, the view will start with the link, eliminating everything prior to that:

 <a href="/">help topic 1</a> more blah blah blah</p>

As you can see by the dangling </p> tag, this can easily break your DOM structure, causing all sorts of problems both for your CSS directives and any Prototype / JQuery / AJAX stuff you've got going on.

You will be surprised by this bug, because the above view renders exactly as it should under mongrel in a development environment.

The solution, obviously, is to eliminate the comment or replace it with an old-school HTML comment (the <!-- kind).

A couple of technical notes

  • Output to STDERR for processes run through the Rack interface is directed to the master Apache error log file rather than the domain/subdomain specific log file. You do not have direct access to the master log file. This limitation can make debugging initialization errors (in particular syntax errors and gem resolution issues) tricky. Passenger will often produce an error output webpage including a stack traceback. However, in some cases it does not. If you have a persistent problem and Passenger is not producing sufficiently useful error output, you can try contacting the Dreamhost support staff and ask them to examine the master log file for you. Once a framework (such as RoR) is up and running, its error output is typically handled by the framework's own error logging mechanism. For example, RoR records its error output in a file named "log/production.log".
  • As of this writing, Aug 18, 2011, Passenger on all shared Dreamhost servers uses Ruby 1.8.7. To use a different version of Ruby (and to otherwise gain full control of the operation of your system) you must use a Virtual Private Server.
  • Passenger and Mongrel fulfill very much the same roles so you most likely do NOT want to be using both of them on the same domain or website.
  • Activating Passenger on a domain will break the phpMyAdmin on any subdomain under the domain. To use phpMyAdmin and Passenger, you must have a non-Passenger-enabled domain with an active phpMyAdmin.
  • In the interest of ease of use and 'Upload and Go' functionality, Passenger disables some mod_rewrite functionality. That means it will automatically override an existing 'dispatch.fcgi' setup you have in place. This is not a problem for your Rails application but it may have other side effects (such as breaking other mod_rewrite rules you have set up). If this causes a problem for your website, contact our support team and we can probably get things working like they did previously for you.
  • Passenger automatically launches applications and leaves them running as long as they are not idle. It also caches the code for Ruby on Rails itself to speed up application launching.
  • You can use your local gem repository if you:
if ENV['RAILS_ENV'] == 'production'  # don't bother on dev
  ENV['GEM_PATH'] = '/home/USERNAME/.gems' #+ ':/usr/lib/ruby/gems/1.8'  # Need this or Passenger fails to start
  require '/home/USERNAME/.gems/gems/RedCloth-4.1.9/lib/redcloth.rb'  # Need this for EACH LOCAL gem you want to use, otherwise it uses the ones in /usr/lib
end

in the config/environment.rb file after the require boot.rb line. The same path should be set in shell's environment variables GEM_HOME and GEM_PATH so you can use the gem program to install/upgrade your own gems. It seems you also need to clear the existing gem paths after setting the path, or it (inconsistently) ignores whatever you've set, according to [the Phusion Blog]. You can reload the config file by typing "touch tmp/restart.txt" in your base directory.

  • User:Bobstiles Passenger doesn't delete the tmp/restart.txt file, but it looks at the timestamp instead and leaves the file.
  • As of 4 Aug 2008, Passenger only supports Rails 2.0.2. I have set up a couple of sites (October 2008) running with Rails 1.2.6, and they seem to behave nicely.
  • User:Bobstiles As of 12 Dec 2009, Passenger seems to only need to know where your local gems are. You don't have to add any special require statements. The above config doesn't work. Use this.
if ENV['RAILS_ENV'] == 'production'  # don't bother on dev
  ENV['GEM_PATH'] = '/home/USERNAME/.gems' + ':/usr/lib/ruby/gems/1.8'
end
  • User:davidonlaptop For Rails 2.3.8, Bobstiles's above code needs to be BEFORE the boot line :
RAILS_GEM_VERSION = '2.3.8' unless defined? RAILS_GEM_VERSION

if ENV['RAILS_ENV'] == 'production'  # don't bother on dev
  ENV['GEM_PATH'] = '/home/USERNAME/.gems' + ':/usr/lib/ruby/gems/1.8'
end

require File.join(File.dirname(__FILE__), 'boot')

#...
  • For some reason, Passenger will not use the 'rails' installation in your local gems repository. However, it will be able to load all other gems. To run Passenger with Rails 2.1.0, freeze rails into your vendor/ directory. To do that, run the following command on the server. Make sure the current directory is the Rails app directory.
rake rails:freeze:edge TAG=rel_2-1-0

This will work, but having Rails in your local gem directory will not. The same method applies to all Rails versions above 2.1.0 .

Capistrano

Using Capistrano with DH RoR on Passenger works well. Use the Manage Domains area of our web panel to set the domain's web directory to:

# access app 'foo' via  www.foo.mydomain.com  or via foo.mydomain.com
# Set web directory in the DH panel to:
foo.mydomain.com/apps/foo/current/public
# In your Capistrano deploy/production.rb file:
set :deploy_to, "/home/DH_user_name/foo.mydomain.com/apps/#{application}"
# Where DH_user_name is your CGI-runs-as user field in the domain edit screen from the DH Panel

If you need to use a different rails environment than 'production', take a look at this.

Passenger and Python/WSGI

Important icon.png Warning: The instructions provided in the following section are considered advanced. You are expected to be knowledgeable in the UNIX shell. Support for these instructions is not available from DreamHost tech support. Server changes may cause this to break. Be prepared to troubleshoot this yourself if this happens. We seriously aren't kidding about this.


Passenger provides "proof of concept" support for WSGI-compliant Python applications. See the following for more information.

Passenger is best known for being used with Ruby on Rails applications. However, it's got another trick up its sleeve... it can also serve up Python web applications which use the WSGI interface, including any application which uses the Django framework! Since Passenger allows your application to temporarily reside in memory while it is being actively used, it will allow your site to respond significantly faster than is otherwise possible.

Note that Passenger's WSGI support is currently considered a "proof of concept"[2]. It currently seems to work reasonably well; nevertheless, you should probably have a backup plan ready (such as Python FastCGI) in case you run into problems.

Setting up Passenger WSGI

To start an example Python site using Passenger WSGI, your first step should be to configure the domain to use Passenger in the Manage Domains section of the web panel. Note that the document root must end in "/public" for a Passenger application - this directory will be used to serve static media.

Once you have set the domain to use Passenger, create a file called passenger_wsgi.py in the folder above the document root (i.e, if you set your document root to /home/username/example.com/public, you'd put this file at /home/username/example.com/passenger_wsgi.py). This file must export a WSGI server with the name application. Here's a minimal example:

def application(environ, start_response):
    start_response('200 OK', [('Content-type', 'text/plain')])
    return ["Hello, world!"]

This application will return a text file with the content "Hello, world!" for any request.

Passenger WSGI and Django

See Django for instructions on how to configure Passenger to run Django.

Passenger WSGI and virtualenv

As Passenger loads your passenger_wsgi.py into a special wrapper (currently /dh/passenger/lib/phusion_passenger/wsgi/request_handler.py, although this may change), you cannot directly select what Python interpreter is used to run your application. However, you can switch interpreters at runtime by adding the following code to the beginning of your passenger_wsgi.py:

import sys, os
INTERP = "/home/<username>/local/bin/python"
#INTERP is present twice so that the new python interpreter knows the actual executable path
if sys.executable != INTERP: os.execl(INTERP, INTERP, *sys.argv)

Set INTERP to the Python interpreter which you wish to use instead of the default.

Passenger WSGI and Pylons/Pyramid

If you're using a Pyramid-framework supported site, the following should work for your passenger_wsgi.py, assuming you've setup the python virtual environment at INTERP:

import sys, os
INTERP = "/home/<username>/local/bin/python"
#INTERP is present twice so that the new python interpreter knows the actual executable path
if sys.executable != INTERP: os.execl(INTERP, INTERP, *sys.argv)

from paste.deploy import loadapp
application = loadapp('config:/home/path/to/site/production.ini')

Note that if you're using a site created from one of the Pyramid starter templates, the development.ini config file wraps your site in the ErrorMiddleware layer, similar to what's done in the next section. However, ErrorMiddleware does not support environments where wsgi.multiprocess is True, so you must use the production config, or modify environ to set wsgi.multiprocess to False. (Note: This may cause problems if you manually override the settings)

500 Errors with Passenger WSGI Workaround

Passenger WSGI at the moment has trouble dealing with errors. Namely, if your WSGI application (for example - but not limited to - Django) raises an uncaught exception Passenger will die, a 500 page will be displayed in the browser, and the error message will not be recorded in the /home/username/logs/sitename/http/error.log file. This makes debugging really tricky.

One solution is to use Python Paste as a WSGI middleware between passenger and your application (django for me). Here is how I did it:

  1. Grab Paste from here: http://pypi.python.org/pypi/Paste . Unzip, all you need is the "paste" directory; put it into your application directory (for example, /home/username/sitename/myapp/paste)
  2. Edit your passenger_wsgi.py file to include that directory in the python path, and load paste up. Here is what your passenger_wsgi.py file might look like:

 import sys, os
 cwd = os.getcwd()
 myapp_directory = cwd + '/myapp'
 sys.path.insert(0,myapp_directory)
 sys.path.append(os.getcwd())
 os.environ['DJANGO_SETTINGS_MODULE'] = "myapp.settings"
 from paste.exceptions.errormiddleware import ErrorMiddleware
 import django.core.handlers.wsgi
 application = django.core.handlers.wsgi.WSGIHandler()
 # To cut django out of the loop, comment the above application = ... line ,
 # and remove "test" from the below function definition.
 def testapplication(environ, start_response):
   status = '200 OK'
   output = 'Hello World! Running Python version ' + sys.version + '\n\n'
   response_headers = [('Content-type', 'text/plain'),
                       ('Content-Length', str(len(output)))]
   # to test paste's error catching prowess, uncomment the following line
   # while this function is the "application"
   #raise("error")
   start_response(status, response_headers)    
   return [output]
 application = ErrorMiddleware(application, debug=True)

Local Logging Alternative

import os, sys

log = file('/home/<username>/passengerwsgi.log', 'a')
print >>log, "Running %s" % (sys.executable)

INTERP = "/home/moatra/local/bin/python"
if sys.executable != INTERP: 
    print >>log, "Detected wrong interpreter location, swapping to %s" % (INTERP)
    #swapping interpreters will not flush any files
    log.flush()
    log.close()
    os.execl(INTERP, INTERP, *sys.argv)
    #Should resume execution from the top of the file

from paste.deploy import loadapp

def application(environ, start_response):
    print >>log, "Application called:"
    print >>log, "environ: %s" % str(environ)
    results = []
    try:
        app = loadapp('config:/home/path/to/site/production.ini')
        print >>log, "App loaded, attempting to run"
        log.flush()
        results = app(environ, start_response)
        print >>log, "App executed successfully"
    except Exception, inst:
        print >>log, "Error: %s" % str(type(inst))
        print >>log, inst.args
        log.flush()
    finally:
        log.close()
    return results

Slightly More Robust Local Logging Alternative

This example uses Python's logging module. It does not contain the alternative interpreter bit or loading the ini file; it does show how to load local modules using getcwd().

Apart from the name myappmodule and myappmodule.application, this doesn't make any assumptions about your application.

import os
import sys
import logging
# append current dir to module path
cwd = os.getcwd()
sys.path.append(cwd)
# assuming this module is in the same dir as passenger_wsgi, this now works!
import myappmodule

# create a logfile in the current directory
logfilename = os.path.join(cwd, 'passenger_wsgi.log')
# configure the logging
logging.basicConfig(filename=logfilename, level=logging.DEBUG)
logging.info("Running %s", sys.executable)

def application(environ, start_response):
    logging.info("Application called:")
    logging.info("environ: %s", str(environ))
    results = []
    try:
        results = myappmodule.application(environ, start_response)
        logging.info("App executed successfully")
    except Exception, inst:
        logging.exception("Error: %s", str(type(inst)))
    logging.info("Application call done")
    return results

Another solution

*NOTE* This is not allowed on shared servers, only PS.

Another solution that worked for me was to first start a development server by executing
./manage.py runserver
Next, I opened another ssh shell and executed
lynx localhost:8000

This opens your application in the lynx web browser, bypassing Passenger by using the Django development server. If you're lucky, it will return some helpful feedback. Note that this solution won't help if the problem is with your Passenger configuration, since this method bypasses Passenger entirely.

Tips and Tricks

Try running your passenger_wsgi.py from your command line. That should help point out any normal python errors that you'd swear weren't there

Passenger seems to use a persistent python session. After updating passenger_wsgi.py, make sure to run pkill python to reset the session and force the server to use your new changes.

See also

External Links

  • The Passenger Documentation on Application Deployment - At Dreamhost, Ruby on Rails applications are reached through the Rack interface. So the relevant Passenger documentation is in the "Rack Deployment" section not the "Ruby on Rails Deployment" section. Also, you do not need to concern yourself with the Apache configuration files. Dreamhost automatically configures those for you.