You are a good little Python developer and you use virtualenv in your Python development.  If you aren’t using virtualenv you’re doing it wrong. You have one of the follwoing problems/desires.

  • You want/need to use a Python virtual environment in your production deployment to encapsulate your configuration
  • You have multiple Apache virtual hosts on the same machine and/or you are running serveral Python/Django applications with different or conflicting versions of Python packages.


I will demonstrate the solution by installing a Pinax application.  Pinax is a website framework buit on top of Django.  Pinax has many dependencies (including a specific version of Django). We will use Pinax version 0.7.3 whcih requires Django 1.0. I found the the official Pinax deployment documentation very weak and it did not work for me out of the box.  Have no fear. The following steps should get you going.  You can also use this blog post as a general tutorial on production deployment of Django applications.  This solution was tested on Ubuntu 10.10 64-bit server edition.

Step 1: Install system pre-requisites:

$ sudo apt-get update
$ sudo apt-get install git-core build-essential python-setuptools python2.6-dev

Step 2: Install `pip`, `virtualenv`, and ‘virtualencwrapper':

 $ sudo easy_install pip

Step 3: Install ‘virtualenv’ and ‘virtualenvwrapper’ with pip:

 $ sudo pip install virtualenv virtualenvwrapper

Step 5: Setup virtualenv wrapper to make dealing with Python virtual environments trivial.

This will let us use the command “workon” and say “workon ve” to switch to the “ve” virtual environment.  All wee need to do is add a couple items in our .baschrc file. You can use any text editor to do this but I will give the commands using “nano”.  Nano is a terminal based text editor pre-installed with Ubuntu.  “vi” and “emacs” are also options.

$ cd ~ 
$ nano .bashrc

Add the following lines to the bottom of the file and then save it by typing CTL-x and then hit ‘y’ to confirm the save.

export WORKON_HOME=$HOME/.virtualenvs

source /usr/local/bin/

Now activate virtualenv wrapper by re-running the .bashrc

$ source .bashrc

Step 6: Download and install Pinax

Before we download Pinax, here is a little background. The Pinax installer will use pip to fetch other prerequisites and create the virtual environment.  This is special to Pinax. Under a non-Pinax configuration, you would download your app, create a virgin virtalenv, activate it, and then run pip to fetch prerequisites and install them to the new virtualenv.  For clarity and completeness I’m going to illustrate the these basic steps here. I’ll use an imaginary project name “foobar” (You don’t need to do this for Pinax):

$ wget http:/somewebsite/foobar.tar.gz
$ tar zxvf foobar.tar.gz
$ mkvirtualenv foobar-env --no-site-packages
$ workon foobar-env
(foobar-env)$ cd foobar
(foobar-env)$ pip install -r requirements.txt

This will install the prerequisites in the file “requirements.txt” to the virtual environment named “foobar-env”.

Okay back to the task at hand.  Let’s install Pinax. Download and untar/unzip Pinax:

$ wget
$ tar zxvf Pinax-0.7.3-bundle.tar.gz
$ cd Pinax-0.7.3-bundle/

Create the virtualenv and install the prerequisites.  I’m calling my virualenv “pinax-env”.   This step could take a while.

$ python scripts/ $WORKON_HOME/pinax-env

Activate (i.e. switch to) the virtualenv.

$ workon pinax-env

Next let’s create a directory called projects to hold our actual project

(pinax-env)$ cd ~
(pinax-env)$ mkdir projects
(pinax-env)$ cd projects

Now lets clone a new Pinax project based on the Pinax intranet_project base. The first line will display a list of all project types.

(pinax-env)$ pinax-admin clone_project -l
(pinax-env)$ pinax-admin clone_project intranet_project myintranet

Install Python Imaging Library (PIL) which seems to be missing from Pinax installer

(pinax-env)$ pip install PIL

Step 7 : Fire Up the Pinax Project

(pinax-env)$ cd myintranet/

(pinax-env)$ python syncdb

(pinax-env)$ python runserver

Point your browser at http://localhost:8000/ and you should see the Pinax default homepage.  If you are hosting remotely you may want to do the following:

(pinax-env)$ python runserver

This will make Django serve over the Internet.  Note this is only development server.   We are just verifying things are working up to this point.  Now here is where it gets insteresting.  This blog post is about deployment on Apache!  Now we want to make install Apache 2, mod_wsgi, and tell Apache to serve the Pinax/Django project located at “~/projects/myintranet” using our virtualenv called “pinax-env.  I’m using “ubuntu” as the deault username.  So our home directory is “/home/ubuntu/”, our project directory is “/home/ubuntu/projects/myintranet” and the root of our virtalenvs is “/home/ubuntu/.virtualenvs/”.  Change this as needed to suit your system.

Step 8: Install Apache2 and mod_wsgi.

Hint don’t install mod_python too.  This can mess things up.

$ sudo apt-get install apache2 libapache2-mod-wsgi

Step 9: Configure Apache to serve our project

We need to tell Apache to server our project.  We do this by pointing Apache to a special  python-based WSGI configuration file for our project. For Pinax, a sample file is located at “deploy/pinax.wsgi” . For Apache, the main file you need to edit is “/etc/apache2/sites-available/default”.  Add the following lines in blue  to “/etc/apache2/sites-available/default”:

<VirtualHost *:80>
ServerAdmin webmaster@localhost
DocumentRoot /var/www
Deny from all
Allow from ::1/128
<VirtualHost *:80> ServerAdmin webmaster@localhost DocumentRoot /var/www <Directory /> Options FollowSymLinks AllowOverride None </Directory> <Directory /var/www/> Options Indexes FollowSymLinks MultiViews AllowOverride None Order allow,deny allow from all </Directory> WSGIScriptAlias / /home/ubuntu/projects/myintranet/deploy/pinax.wsgi <Directory /home/ubuntu/projects/myintranet/deploy> Order deny,allow Allow from all </Directory> ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/ <Directory "/usr/lib/cgi-bin"> AllowOverride None Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch Order allow,deny Allow from all </Directory> ErrorLog ${APACHE_LOG_DIR}/error.log # Possible values include: debug, info, notice, warn, error, crit, # alert, emerg. LogLevel warn CustomLog ${APACHE_LOG_DIR}/access.log combined Alias /doc/ "/usr/share/doc/" <Directory "/usr/share/doc/"> Options Indexes MultiViews FollowSymLinks AllowOverride None Order deny,allow Deny from all Allow from ::1/128 </Directory>

Now its time to complete our configuration by editing “home/ubuntu/projects/myintranet/deploy/pinax.wsgi”.  Pinax.wsgi file didnt work for me but the configuration I gleamed from mod_wsgi’s wiki on Virtual Environments will work.  Completely erase the default pinax.wsgi file and replace it with the following:

ALLDIRS = ['/home/ubuntu/.virtualenvs/pinax-env/lib/python2.6/site-packages',

import os
import sys
import site

# redirect sys.stdout to sys.stderr for bad libraries like geopy that uses
# print statements for optional import exceptions.
sys.stdout = sys.stderr
prev_sys_path = list(sys.path)

for directory in ALLDIRS:

# Reorder sys.path so new directories at the front.
new_sys_path = []
for item in list(sys.path):
    if item not in prev_sys_path:
sys.path[:0] = new_sys_path 

activate_this = '/home/ubuntu/.virtualenvs/pinax-env/bin/'
execfile(activate_this, dict(__file__=activate_this))
from os.path import abspath, dirname, join
from site import addsitedir

sys.path.insert(0, abspath(join(dirname(__file__), "../../")))

from django.conf import settings
os.environ["DJANGO_SETTINGS_MODULE"] = "myintranet.settings"

sys.path.insert(0, join(settings.PINAX_ROOT, "apps"))
sys.path.insert(0, join(settings.PROJECT_ROOT, "apps"))

from django.core.handlers.wsgi import WSGIHandler
application = WSGIHandler()

The key things that may change if the project path and/or virtual environment change are highlighted in blue.  Note you want to remove the following lines if you are setting up a regular Django project (one that is not based on Pinax).

sys.path.insert(0, join(settings.PINAX_ROOT, "apps"))
sys.path.insert(0, join(settings.PROJECT_ROOT, "apps"))

Step 10: Restart Apache and Cross your fingers

$ sudo apache2ctl restart

If the god s are smiling on you today you should be in business.  check by going to http://localhost/.  If not, you might want to check Apache’s error log.

$ cat /var/log/apache2/error.log

There are many other Django and Pinax specific setting you will likely want to change.  Please see the Django Project and the Pinax Project for more information. Good luck. :-)