Deploying Python 3 WSGI applications with mod_wsgi

A little while back I wrote a simple Kanban board using Flask. After finally publishing the code, this post is going to look at running Python 3 WSGI applications, like the Kanban board, under Apache using mod_wsgi.

Installing packages

The first thing to do is install Apache and the Python 3 version of mod_wsgi, this can be done with the following command:

apt-get install libapache2-mod-wsgi-py3 apache2

For the example in this post, the WSGI application is going to be downloaded from GitHub, and use a Python 3 virtual environment. Therefore the following packages also need to be installed:

apt-get install python3-venv git

Creating a user account

It's good practise to use a separate user account to run the WSGI application. A user account can be created using useradd:

useradd \
  --shell /sbin/nologin \
  --create-home \
  --home-dir /srv/kanban \
  kanban

Note: the shell for the account has been set to /sbin/nologin to prevent interactive login sessions.

Cloning the Application

Once the user account is ready the application code can be cloned with a command similar to the following:

sudo -u kanban \
  git clone https://github.com/FloatingOctothorpe/python-kanban.git \
  /srv/kanban/python-kanban

Note: the command above should be run from a directory the kanban user can read (e.g. /).

For the example in this post, Python modules are going to be installed to a virtual environment. The virtual environment can be created with the following commands:

sudo -u kanban python3 -m venv /srv/kanban/venv
sudo -u kanban bash -c '
  source /srv/kanban/venv/bin/activate;
  pip install -r /srv/kanban/python-kanban/requirements.txt'

Once the virtual environment has been created, it should be possible to run the Flask application directly as the kanban user with a command similar to the following:

sudo -u kanban bash -c '
  source /srv/kanban/venv/bin/activate;
  cd /srv/kanban/python-kanban;
  FLASK_APP=main.py flask run'

Configuring Apache

Assuming the application runs without a problem the last thing to do is configure Apache to run the application. This involves creating two files, a WSGI application script file (/srv/kanban/python-kanban/kanban.wsgi), and an Apache config file (/etc/apache2/conf-available/kanban.conf).

WSGI application script file

For the example in this post, the WSGI application script should contain the following:

#!/usr/bin/env python3

import os
import sys

os.chdir(os.path.dirname(__file__))
sys.path.insert(0, os.path.dirname(__file__))

from main import app as application

Note: os.chdir and sys.path.insert are used to make sure the application's current working directory is /srv/kanban/python-kanban and code from the directory can be imported.

Apache config

The Apache config should look similar to the following:

WSGIDaemonProcess kanban user=kanban group=kanban threads=5 python-home=/srv/kanban/venv
WSGIScriptAlias /kanban /srv/kanban/python-kanban/kanban.wsgi

<Directory /srv/kanban/python-kanban/>
    WSGIProcessGroup kanban
    WSGIApplicationGroup %{GLOBAL}
    Require all granted
</Directory>

The first line tells Apache to create a daemon process which will run the Kanban application using the WSGIDaemonProcess directive. The following line uses the WSGIScriptAlias directive to map a URL to the application. Finally the Directory section maps the directory to the process and application groups and grants access to the directory.

Enabling new configuration

Once the WSGI script and Apache config are in place the following commands can be used to pick up the new config:

a2enconf kanban
systemctl reload apache2

If everything goes well there should now be an apache2 process running as the kanban user, and the application should be available:

$ pgrep -u kanban -a
5474 /usr/sbin/apache2 -k start

$ curl --head http://localhost/kanban/
HTTP/1.1 200 OK
...

Note: If the http status from curl is not 200, check /var/log/apache2/error.log for details.