Skip to content

WSGI

This is the most complicated part of the whole process because it consists of three separate steps. The first is to ensure that the WSGI Apache module is installed. Then, the corresponding WSGI configuration file for our application needs to be created. Finally, a new Apache site configuration needs to be created and enabled.

Adding mod_wsgi to Apache

Apache is designed to be extensible and there are many add-on modules available. The command below will install a pre-compiled version of the module. However, it is important to know that the Apache WSGI module must be compiled with the same version of Python that you will use in your application. This command installs a pre-compiled version, and so you will need to check that it is compatible. The simplest way to do that is after installation.

1
sudo apt-get install libapache2-mod-wsgi-py3

After installing a new module, Apache needs to be restarted with

1
sudo service apache2 restart

To check the Python version used by the WSGI module, go to the Apache error log which can usually be found at /var/log/apache2/error.log and requires root privilege. Find the part of the log that shows the output messages produced as the server restarted. You should see a message like the following which reports that the WSGI module is using Python 3.6.

1
Apache/2.4.29 (Ubuntu) mod_wsgi/4.5.17 Python/3.6 configured -- resuming normal operations

If that does not match the version of Python you intend to use, you could find that when you try to run your application via the Web server, the Python virtual environment is not activated correctly. To bring the module into line with the version of Python you will be using, you need to recompile it with the appropriate options. This is not as difficult as it sounds, but there are a few steps in the process which are detailed in the next section. If the installed version of the module uses the correct version of Python already, then you can skip the recompilation section.

Recompiling mod_wsgi for a specific Python version

If you have to recompile the module, it would be a good idea to remove the earlier version with

1
sudo apt-get remove libapache2-mod-wsgi

There are two packages required to successfully compile the WSGI module which are not always installed by default. They are the Apache development tools and the header file for your particular version of Python.

The header files need to be downloaded from the DeadSnakes personal package archive (PPA). Add it to your Ubuntu configuration with the command below.

1
sudo add-apt-repository ppa:deadsnakes/ppa

Once the repository is configured, both of the required files can be installed using the command below, where you should replace X.Y with the specific version of Python you are interested in.

1
sudo apt-get install pythonX.Y-dev apache2-dev

The next part of the process is described in detail in the mod_wsgi documentation. The first step is to download the most recent version of the source code from GitHub to your home directory. Decompress it with

1
tar xvfz FILENAME

where FILENAME is the name of the downloaded file.

This will create a new subdirectory containing the source code. Change into the new subdirectory for what follows.

The next step is to configure the code for your particular environment. This basically means linking it to the correct version of Python. If the version of your system default Python is the same as the target version, then you can just use the command below.

1
./configure

However, if the target version is different, you should locate the appropriate Python executable (i.e. the one in your virtual environment) and use this version of the command replacing PATH-TO-PYTHON with the full path to the Python executable:

1
./configure --with-python=PATH-TO-PYTHON

The next step is to build the executable, and that can be done with the make command:

1
make

make is a utility command used for automating administrative processes with several potentially complex but predictable steps. Basically, it takes its input from a pre-prepared configuration file which encapsulates the complexity of the compilation process.

The make configuration file - known as a makefile - may contain several targets. The next command uses a different target to take the compiled module and install it into your Apache configuration.

1
sudo make install

Once the installation is complete, restart Apache as before.

Testing mod_wsgi

To test that the module is working correctly, create the file /var/www/html/wsgi_test_script.py with the following contents:

1
2
3
4
5
6
def application(environ,start_response):
    status = '200 OK'
    html = b'Hello from mod_wsgi'
    response_header = [('Content-type','text/html')]
    start_response(status,response_header)
    return [html]

Then, create an Apache test site by placing the content below into the file /etc/apache2/site-available/mod-wsgi.conf

1
WSGIScriptAlias /test_wsgi /var/www/html/wsgi_test_script.py

Enable the site and restart Apache using

1
2
sudo a2ensite mod-wsgi
sudo service apache2 restart

Check that the module is working by navigating to http://domain/test_wsgi

where domain is your server's fully-qualified domain name

You should see the message embedded in the WSGI test script.

Configuring WSGI for our application

According to the plan in the introduction, the WSGI file for our application is located in the application root directory, /usr/local/env/myApp. Change to that directory and create the file myapp.wsgi. Please note that the filename must be in lowercase - if you call it myApp.wsgi, it will not work. Add the content shown below to the new file.

1
2
3
4
5
import sys

sys.path.insert(0, '/usr/local/env/myApp/')

from app.myApp import application

Explanation

Line 1: Import the Python package for system commands

Line 3: Add the directory /usr/local/env/myApp to the beginning of the PATH environment variable.

Line 5: Import the Flask application defined in app/myApp.py.

Next, we need to tell Apache to direct all incoming http requests to our application. This is done by creating a site configuration file which contains a virtual host definition. Do this by creating the file /etc/apache2/sites-available/myApp.conf containing the following code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
WSGIDaemonProcess myApp  python-home=/usr/local/env/myApp/venv  threads=5
WSGIScriptAlias / /usr/local/env/myApp/myapp.wsgi
WSGIProcessGroup myApp

<VirtualHost *:80 >
    <Directory /usr/local/env/myApp>
        Require all granted
        Order allow,deny
        Allow from all
        LogLevel info
     </Directory>
</VirtualHost>

The WSGI process can be configured in embedded mode or daemon mode. Here, we are using daemon mode as defined at line 1. The python-home parameter tells the Apache compiler where to find the Python virtual environment.

The WSGIScriptAlias directive at line 2 is used to define the URL that is aliased to the WSGI file. In this case the URL is http://domain_name/ and the aliased file is /usr/local/env/myApp/myapp.wsgi.

The other directive used here is WSGIProcessGroup at line 3. This is used for running multiple WSGI processes in one daemon process group. N.B. The WSGIDaemonProcess and WSGIProcessGroup names should be identical in order to avoid server-side errors.

Now we can test whether Apache is routing http requests through to our application. First, enable the new site and restart the Apache server with

1
2
3
sudo a2ensite myApp
sudo service apache2 stop
sudo service apache2 start

Although there is a restart option, the WSGI configuration is held in memory and changes to the configuration file may not be picked up correctly. The solution is to perform the restart in two steps.

In your browser, navigate to http://domain/ and you should see the message Hello World!. This message comes from the myApp.py file in our app directory.

Environment variables

The operation of a Flask application is typically controlled by setting certain Linux environment variables. A common one is FLASK_ENV which is used to select between groups of settings for different types of environment such as development and production. Setting FLASK_ENV to development enables debug mode. For more information on Flask configuration, please refer to the [Flask documentation](https://flask.palletsprojects.com/en/1.1.x/config/.

The simplest place to set environment variables is in the WSGI file as illustrated below.

1
2
3
4
5
6
7
import sys
import os

sys.path.insert(0, '/usr/local/env/myApp/')
os.environ['FLASK_ENV'] = 'production'

from app.myApp import flask as application