Passenger

From DreamHost
Jump to: navigation, search

Overview

Passenger is an open source web and application server that greatly simplifies the deployment of Ruby applications, Python, and Node.js. Passenger is the preferred way to deploy and host Ruby on Rails applications across all DreamHost servers and is free on every DreamHost hosting plan.

Enabling Passenger on a domain

  1. Navigate to the (Panel > 'Domains' > 'Manage Domains') page.
  2. Click the Edit button to the right of your domain under the 'Web Hosting' column.
    The 'Manage Domains' settings page opens:
    01 RVM nodejs.fw.png
  3. Scroll down to the 'Web Options' section and check the 'Passenger (Ruby/NodeJS/Python apps only):' check box.
    A WARNING dialog box then appears:
    01 Passenger.png
  4. Click the OK button. The panel adds the /public subdirectory for you.
  5. Click the Change settings button to save your changes.
Important icon.png Important:
  • 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 to trigger Passenger to reinitialize the application. Passenger caches many resources so changes are not recognized unless the modification date of "tmp/restart.txt" is changed.
  • The most common method to make this change is to run "touch tmp/restart.txt" via SSH. (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.


The "public" subdirectory

  • Passenger maps the directory named "public" to be the document root for your domain/subdomain.
  • If a static HTML file named "public/index.html" exists, it's then used as the response for requests for the root document (i.e., "/").
  • If you want your application to handle requests for the root document, then you must first remove "public/index.html" (if it exists).
  • By default, Ruby on Rails creates 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.)

Basic operation

The following are the basic actions that take place once a file is requested from a domain running Passenger and Ruby on Rails:

  1. When a request is made to a domain/subdomain, the Apache HTTP Server passes the request to Passenger.
  2. Passenger first looks for an appropriately-named HTML or CGI file in the domain/subdomain's /public subdirectory.
  3. If no matching file is found, the request is passed to Passenger's Rack interface.
  4. Note that this use of the /public subdirectory meshes precisely with the way that Ruby on Rails makes use of the same subdirectory.
  5. In order to generate a response, Rack looks for a file named "config.ru" in the domain/subdomain's root directory (i.e., the parent of the domain's /public subdirectory).
  6. 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.

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 subdomain. Passenger directs 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 only enable Passenger if you want to access your application via the following three URLs:

  • myapp.example.com
  • www.example.com
  • www.myapp.example.com

If you want to access your application via www.example.com/path-to-myapp then use FastCGI instead of passenger.

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 April 2015, 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 VPS and install RVM.
  • Passenger and Mongrel fulfill very much the same roles so you most likely do NOT want to use 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, try using the FastCGI method.
  • 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 use the following in the config/environment.rb file:
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
end

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

The same path should be set in your shell's environment variables GEM_HOME and GEM_PATH so you can use the gem program to install/upgrade your own gems. You can reload the config file by running "touch tmp/restart.txt" in your base directory.

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.


Passenger is best known for being used with Ruby on Rails applications, however 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.

Passenger's WSGI support works reasonably well, however another available option is 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 using the steps mentioned above. Note that the document root must end in "/public" for a Passenger application as 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 which 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). The following link may offer further assistance:

Pyramid-on-DreamHost

500 Errors with Passenger WSGI workaround

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

One solution is to use Python Paste as a WSGI middleware between passenger and your application:

  1. Grab Paste from here: http://pypi.python.org/pypi/Paste.
  2. Unzip the files. All you need is the "paste" directory.
  3. Put it into your application directory (for example, /home/username/sitename/myapp/paste)
  4. Edit your passenger_wsgi.py file to include that directory in the python path, and then load Paste.
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

A 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

This solution is only available on a VPS machine.

Another solution is to start a development server by executing:

./manage.py runserver

Next, open another SSH shell and run:

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.

Note2 icon.png Note: 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 which may point out any python errors.

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.

Troubleshooting

For common troubleshooting solutions, please visit the Passenger Troubleshooting article.

See also

Passenger

Ruby

Python

Node.js