|The instructions provided in this article or section are considered advanced.|
You are expected to be knowledgeable in the UNIX shell.
Note that Passenger's WSGI support is currently considered a "proof of concept". 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
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)
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:
- 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)
- 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.stdout = sys.stderr 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
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
*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 runserverNext, I opened another ssh shell and executed
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.