Using pyvenv (venv) With Django on WebFaction.

silly walk
Silly.

Setting up Python virtual environments has never been easier.

When I returned to Django and began to rebuild my site I didn’t find any information that mentioned this option.

A number of the how-tos I read involved editing the wsgi.py file that is installed when you create a mod_wsgi app on WebFaction, so the wsgi.py file would activate the virtualenv. This really isn’t necessary. Interestingly, WebFaction’s own instructions are simple, current, and really all you need to get things rolling pretty easily. Go figure. This article adds a few specifics (particulary for the Apache config file), but is really about using Python3’s built in venv command.

As of Python3.3, the ability to create virtual environments is built right in to the language in the form of the pyvenv command (which will be deprecated in favor of simply venv in Python3.6.) This makes it even easier to setup Python virtualenvs on WebFaction.

One last bit before we get to it. Most of what I did to set up Django in a virtualenv is exactly what is described in the this excellent how-to by Michał Karzyński, the only real difference being the use of the built in venv instead of doing a separate install of virtualenv. I recommend you read Michał’s article as it goes a bit beyond setup and includes instructions on serving static media and separating your development and production environments.

Ok. Here’s what I did:

We’re going to skip the Django App installer script that WebFaction provides and just create a generic mod_wsgi application.

In the WebFaction control panel,

  1. Create a new app of type mod_wsgi 4.5.9/Python 3.5 (if you read this in the future the version numbers may have increased.) I’m going to call this app, ‘yourapp.’
  2. Create a website using one of your available domains and connect it to yourapp.
  3. Make sure it’s working by visiting the site in your browser. You should see this:
Welcome to your mod_wsgi website! It uses:
 Python 3.5.2 (default, Sep 4 2016, 00:18:27)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-4)]
WSGI version: (4, 5, 8)

Now, if you visit /home/username/webapps/yourapp via ssh or your FTP client, you will see the directories: apache2 and htdocs.
You aren’t going to need htdocs so you can delete that now.

Ok. Now we just have to create the virtual environment for our Django app and set up a virtual host for our site in the Apache config file.

Let’s create the virtual environment first.

I prefer to create the virtual environment directory in the same location as the app itself. This puts the directory next to the apache2 directory (and in a moment next to our Django project directory.)

Do an ssh login to your WebFaction account, and run these commands: (You can use any name for your virtual environment. I’ve used the app name with ‘env’ appended.)

cd /home/username/webapps/yourapp
python3 -m venv yourappenv

Or just:

python3 -m venv /home/username/webapps/yourapp/yourappenv

Now we have a Python virtual environment installed. Easy! (See the directory tree below.)

/yourapp
├── apache2
│   ├── bin
│   ├── conf <== #location of http.conf file
│   ├── lib
│   ├── logs
│   └── modules
└── yourappenv
    ├── bin <== #location of activate command
    ├── include
    ├── lib
    └── lib64 -> lib

Activating your virtual environment

When you are managing your app from an ssh session you will need to activate your virtual environment. The command is located in yourappenv/bin. From the yourapp directory, run source yourappenv/bin/activate
(I’d suggest creating an alias or login hook that activates your virtual environment so you don’t have to type in file paths everytime.)

Pip!

Another nice thing about the built in venv command is that it installs pip automatically. Let’s get pip updated and ready to load your requirements.txt file.

Run:
pip install --upgrade pip

(Optional)
I like to install yolk so I can easily update all of my installed packages.
pip install yolk3K

Running,
yolk --upgrade
will check all of your installed packages for available updates and install them if there are any.

Now you can install the latest version of Django with:
pip install Django

If you have a requirements.txt file ready you can install all of your packages with:
pip install -r requirements.txt
(If Django is included in your requirements file, you can skip the separate pip install for it.)

Create a Django project

At this point I recommend you go ahead and set up a Django project. You will need to know the name (the path) of this project when we configure Apache to serve your site.

With your virtual environment activated run:
django-admin.py startproject yourproject

You may need to make sure that your django-admin.py and manage.py files are executable.
cd /home/username/webapps/yourapp/yourappenv/bin/
chmod +x django-admin.py

Once you create your project, from the project directory, run: chmod +x manage.py
This allows you to run:
./manage.py 'your command'
whenever you are managing your project

Great! Only one thing left to do!

Set up a virtual host for your site.

This will ensure that Apache is serving your site using the correct environment.

You are going to make some changes to your app’s httpd.conf file. The file is located here: /home/username/webapps/yourapp/apache2/conf/httpd.conf You can make a copy and alter the original if you like. Change the original to look like this:

ServerRoot "/home/username/webapps/yourapp/apache2" 

LoadModule authz_core_module modules/mod_authz_core.so 
LoadModule dir_module modules/mod_dir.so 
LoadModule env_module modules/mod_env.so 
LoadModule log_config_module modules/mod_log_config.so 
LoadModule mime_module modules/mod_mime.so 
LoadModule rewrite_module modules/mod_rewrite.so 
LoadModule setenvif_module modules/mod_setenvif.so 
LoadModule wsgi_module modules/mod_wsgi.so 
LoadModule unixd_module modules/mod_unixd.so 

Listen 27140 #this should be whatever WebFaction set it to. 
KeepAlive Off 
SetEnvIf X-Forwarded-SSL on HTTPS=1 
ServerLimit 1 
StartServers 1 
MaxRequestWorkers 5 
MinSpareThreads 1 MaxSpareThreads 3 
ThreadsPerChild 5 

WSGIRestrictEmbedded On 
WSGILazyInitialization On 

<VirtualHost *> 
    ServerName yourdomain.net # Logging config 
    LogFormat "%{X-Forwarded-For}i %l %u %t "%r" %>s %b "%{Referer}i" "%{User-Agent}i"" combined CustomLog /home/username/logs/user/access_yourapp.log combined 
    ErrorLog /home/username/logs/user/error_yourapp.log 
    
    #Django WSGI settings 
    WSGIDaemonProcess yourapp processes=2 threads=12 python-home=/home/username/webapps/yourapp/yourappenv python-path=/home/username/webapps/yourapp/yourproject 
    WSGIProcessGroup yourapp 
    WSGIScriptAlias / /home/username/webapps/yourapp/yourproject/yourproject/wsgi.py 
</VirtualHost>
  1. Make the necessary substitutions in the path names using your own user, application, and project names.
  2. Set the Listen value to whatever WebFaction set it to in the original config file (meaning you shouldn’t have to change it.)
  3. Change ServerName to the appropriate domain.
  4. Note that due to Django’s somewhat frustrating naming conventions, the WSGIScriptAlias path will seem like it contains a typo. It doesn’t. This path needs to go all the way to your project directory, which is where the wsgi.py file lives. (When you create a Django project you get a directory named as you asked, with a subdirectory of the same name.)
  5. Save the httpd.conf file (to its original location) and restart Apache.

From the ‘yourapp’ directory run:
./apache2/bin/restart

And that should do it! Have fun and build something cool!