- 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.
In the following examples, username would be your Shell user and example.com your website.
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 as shown in the Passenger article. 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
View the following articles 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
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
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
True, so you must use the production config, or modify environ to set
False. (Note: This may cause problems if you manually override the settings). The following link may offer further assistance:
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 error.log file. This makes debugging tricky.
One solution is to use Python Paste as a WSGI middleware between passenger and your application:
- Grab Paste from here: pypi.python.org/pypi/Paste.
- Unzip the files. All you need is the paste directory.
- Put it into your application directory (for example, /home/username/sitename/myapp/paste)
- 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 def _get_log(): return file('/home/username/passengerwsgi.log', 'a') log = _get_log() print >>log, "Running %s" % (sys.executable) INTERP = "/home/username/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 log.flush() log.close() from paste.deploy import loadapp def application(environ, start_response): log = _get_log() 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
Apart from the name
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
This solution is only available on a VPS machine.
Another solution is to start a development server by executing:
[server]$ ./manage.py runserver
Next, open another SSH shell and run:
[server]$ 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.
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.