Deploying Flask Application using Apache with mod_wsgi

Introduction

We have written this blog to help developers deploy flask application with apache and mod_wsgi in production. We read through a lot of articles and found out the steps we layout below are the simplest way to deploy. Also, the biggest caveat to deploying in production is version mismatches, so we are going to be building things from source rather than using apt-get to install packages. So let’s get started.

Build and install custom version of python

We used python 3.7.5 for a current project and here is how you can deploy python from source. Make sure you remove any other python version you might have on your machine. To remove any existing python installations.

sudo apt-get purge python<version>
sudo apt-get autoremove

1. Install Dependencies and packages.

sudo apt update
sudo apt install build-essential zlib1g-dev libncurses5-dev libgdbm-dev libnss3-dev libssl-dev libsqlite3-dev libreadline-dev libffi-dev wget libbz2-dev

2. Download source

Now to fetch tar file for a specific python version use.

wget https://www.python.org/ftp/python/3.7.5/Python-3.7.5.tgz  

You can check out other python versions here: Link

3. Compile python

Now to compile the downloaded tar.

tar -xf Python-3.7.5.tgz
cd Python-3.7.5
./configure --enable-shared --prefix=/usr/ 
make -j4
sudo make install 

Argument in make -jN is the number of processors if you are not sure about number of processors in the machine. You can use nproc to get it.

Build and install custom version of wsgi mod

Ok, now we move on to getting mod_wsgi installation right. Before we get into steps I would like you to read this Link. which essentially says this:

The problem in trying to force mod_wsgi to use a different Python installation than what it was compiled for, 
even where it is the same Python version, is that the Python installation may itself not have been 
compiled with the same options. This is especially a problem when it comes to issues around how Python stores 
Unicode characters in memory. 

So to avoid this problem we are going to build mod_wsgi from the source and compile it with python we just installed in the last step.

1. Compile wsgi-mod

Download source code from: Github releases

tar xvfz mod_wsgi-X.Y.tar.gz
cd mod_wsgi-X.Y
sudo apt-get install apache2-dev
./configure --with-python=/usr/bin/python
make -j4
sudo make install

Configure Apache

Ok, now we move on to building apache2.

1. Install apache2

sudo apt-get install apache2

2. Configure Wsgi module

Now we need to configure apache with the mod_wsgi we complied and installed in the last step.

Create a file: `/etc/apache2/mods-available/wsgi.load`

Add the following line to the file:

`LoadModule wsgi_module /usr/lib/apache2/modules/mod_wsgi.so`

Now we need to enable mod_wsgi and restart apache2.

sudo a2enmod wsgi
sudo systemctl restart apache2

This completes the installation of Apache2 and mod_wsgi, all complied with python3.7.5.

Deploying Flask Application

1. Create .wsgi file

Now to deploy the flask application copy your code directory to /var/www/ directory and create <your_application>.wsgi file and add the following lines.

import sys
sys.path.insert(0, '/var/www/<your_application_dir>')
from <your_application> import <flask_app> as application

2. Create virtualenv

Its generally best practice to create a virtualenv for your application and install all the dependencies, to avoid clashing package versions that other application might be using. To create a virtualenv:

cd /var/www/
virtualenv -p python3 <venv_name>
pip install -r requirements.txt

Note that you will need a requirments.txt file. To create one you can do the following:

pip freeze > requirements.txt

3. Create Configuration file in Apache

Now we need to create a configuration file to tell apache from where to load your application. In /etc/apache2/sites-available create <your_application>.conf file and add the following lines.

    <VirtualHost *:80>
       ServerName <Server_name>
       WSGIDaemonProcess <your_application> user=<user> group=<group> threads=5 \
       python-home=/var/www/<venv_name>
   
       WSGIProcessGroup <your_application>
       WSGIApplicationGroup %{GLOBAL}
       WSGIScriptAlias / /var/www/<your_application_dir>/<your_application>.wsgi
       <Directory /var/www/<your_application_dir>/<your_application>/>
           Order allow,deny
           Allow from all
           Require all granted
       </Directory>
       Alias /static /var/www/<your_application_dir>/<your_application>/static
       <Directory /var/www/<your_application_dir>/<your_application>/static/>
           Order allow,deny
           Allow from all
        </Directory>
        ErrorLog /var/www/<your_application_dir>/error.log
        LogLevel debug
        CustomLog /var/www/<your_application_dir>/access.log combined
   </VirtualHost>

You can remove that logging commands if you don’t wish to see logs.

4. Enable application

Now we need to tell apache to enable our application and load the configuration file we just created.

sudo a2ensite <your_application>
sudo systemctl restart apache2

That’s it. Your website is up and running on the public IP for the server.

Divyanshu Goyal
Divyanshu Goyal
Masters in Computer Science.

My research interests include Machine Learning, Deep Learning and Distributed systems.