Mind Chasers Inc.

Quick Install of Apache, WSGI, and Django on Ubuntu 16.04

A Step-by-Step cookbook to install Apache and Django using WSGI on Ubuntu Linux

We're starting with a clean x86 64-bit Ubuntu 16.04 system with python3.5 but with neither Apache nor Django installed:

$ lsb_release -a
No LSB modules are available.
Distributor ID:	Ubuntu
Description:	Ubuntu 16.04.2 LTS
Release:	16.04
Codename:	xenial

Use Ubuntu's apt (Advanced Packaging Tool) to install our base apache2 installation


$ sudo apt install apache2
Reading package lists... Done
Building dependency tree       
Reading state information... Done
The following additional packages will be installed:
  apache2-bin apache2-data apache2-utils libaprutil1-dbd-sqlite3 libaprutil1-ldap liblua5.1-0
Suggested packages:
  apache2-doc apache2-suexec-pristine | apache2-suexec-custom
The following NEW packages will be installed:
  apache2 apache2-bin apache2-data apache2-utils libaprutil1-dbd-sqlite3 libaprutil1-ldap liblua5.1-0
0 upgraded, 7 newly installed, 0 to remove and 4 not upgraded.
Need to get 1,374 kB of archives.
After this operation, 5,865 kB of additional disk space will be used.

We want mod-wsgi, which is an Apache module for the implmenentation of python WSGI (Web Server Gateway Interface). This will enable us to run our Django application using Apache and also do cool things like SSE server push via WSGI, and this will be the subject of a future article.


Determine whether it's already installed:

$ dpkg -S mod_wsgi
dpkg-query: no path found matching pattern *mod_wsgi*

Install it for python 3 making sure to use the Apache library for python3. Information on various Ubuntu HTTP server packages can be found on the Ubuntu package site.

$ sudo apt install libapache2-mod-wsgi-py3
Reading package lists... Done
Building dependency tree       
Reading state information... Done
The following NEW packages will be installed:
0 upgraded, 1 newly installed, 0 to remove and 4 not upgraded.
Need to get 78.6 kB of archives.
After this operation, 249 kB of additional disk space will be used.
Get:1 http://us.archive.ubuntu.com/ubuntu xenial/universe amd64 libapache2-mod-wsgi-py3 amd64 4.3.0-1.1build1 [78.6 kB]
Fetched 78.6 kB in 0s (681 kB/s)                 
Selecting previously unselected package libapache2-mod-wsgi-py3.
(Reading database ... 259242 files and directories currently installed.)
Preparing to unpack .../libapache2-mod-wsgi-py3_4.3.0-1.1build1_amd64.deb ...
Unpacking libapache2-mod-wsgi-py3 (4.3.0-1.1build1) ...
Setting up libapache2-mod-wsgi-py3 (4.3.0-1.1build1) ...
apache2_invoke: Enable module wsgi

Note the last line above that states the module was enabled. Although it's not necessary, let's check that it is indeed installed and where it's located:

$ dpkg -S mod_wsgi
libapache2-mod-wsgi-py3: /usr/lib/apache2/modules/mod_wsgi.so
libapache2-mod-wsgi-py3: /usr/lib/apache2/modules/mod_wsgi.so-3.5

Check if our Apache Web Server is running

$ ps -e | grep apache
 3231 ?        00:00:00 apache2
 3234 ?        00:00:00 apache2
 3235 ?        00:00:00 apache2

Install Django from source

It's so easy to work with Django from source using git. We'll install it under our /build tree:

$ cd /build

$ git clone https://github.com/django/django.git
Cloning into 'django'...
remote: Counting objects: 386325, done.
remote: Compressing objects: 100% (22/22), done.
remote: Total 386325 (delta 8), reused 0 (delta 0), pack-reused 386303
Receiving objects: 100% (386325/386325), 157.03 MiB | 3.38 MiB/s, done.
Resolving deltas: 100% (280454/280454), done.
Checking connectivity... done.

Checkout the latest production branch

$ cd django

$ git tag -l | grep 1.10.6

$ git checkout 1.10.6

Note: checking out '1.10.6'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:

  git checkout -b <new-branch-name>

HEAD is now at 320ec4e... [1.10.x] Bumped version for 1.10.6 release.

Create our own development branch so we can make changes to the source, commit them, create patches, etc.

$ git checkout -b dev_1.10

Determine where to configure the django library:

$ python3.5
Python 3.5.2 (default, Nov 17 2016, 17:05:23) 
[GCC 5.4.0 20160609] on linux
Type "help", "copyright", "credits" or "license" for more information.

>>> import site

>>> site.getsitepackages()
['/usr/local/lib/python3.5/dist-packages', '/usr/lib/python3/dist-packages', '/usr/lib/python3.5/dist-packages']

>>> quit()

$ cd /usr/local/lib/python3.5/dist-packages

$ sudo sh -c "echo '/build/django' > django.pth"

Note that the last line above creates a path configuration file so python can find our Django library.

Verify it's installed in our python3 environment:

$ python3
Python 3.5.2 (default, Nov 17 2016, 17:05:23) 
[GCC 5.4.0 20160609] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import django
>>> django.VERSION
(1, 10, 6, 'final', 0)

Now we have Apache running and Django installed so python3.5 can import it and use it.

Create an example Django test project: tstdj under /build

$ python3.5 /build/django/django/bin/django-admin.py \
	startproject tstdj

Find your settings.py file under tstdj and modify your ALLOWED_HOSTS so it includes your interface (e.g., If you don't do this, you'll see an error when you visit your website explaining what you need to fix (courtesy of the Django developers).

If you want to take a break and verify your Django test project is OK, you can use:

$ cd /build/tstdj

$ python3 manage.py runserver

You'll get a warning that you haven't applied your migrations yet. If you unfamiliar with runserver and migrations, head over to djangoproject.com and walk through the tutorial.

Configure the Apache server to serve Django

Now it's time to tie it all together and serve up your Django project using Apache and mod-wsgi.

Open up two terminals side-by-side to modify Apache conf files and view Apache logs:

First terminal:

$ cd /etc/apache2

$ grep APACHE_LOG_DIR envvars 
export APACHE_LOG_DIR=/var/log/apache2$SUFFIX 

Second terminal, making use of the path determined for APACHE_LOG_DIR above:

$ cd /var/log/apache2

This second terminal will let you view errors in error.log and accesses in access.log.

For completeness sake, make sure mod-wsgi is enabled:

$ sudo a2enmod wsgi
Module wsgi already enabled

In the first terminal open up /etc/apache2/apache2.conf with your favorite editor after making a copy of it:

$ sudo cp apache2.conf apache2.conf.orig

Add the following lines to the end of apache2.conf:

WSGIDaemonProcess <username> processes=2 threads=12 python-path=/build/tstdj
WSGIProcessGroup <username>
WSGIRestrictEmbedded On
WSGILazyInitialization On
WSGIScriptAlias / /build/tstdj/tstdj/wsgi.py

<Directory /build/tstdj/tstdj>
    Require all granted

Note that there are many options for configuring, tuning, and tweaking both Apache and mod-wsgi. These just happen to be set up for our testing but are similar to the ones we use on a production site. Refer to the documentation of both projects for further information.

Now, restart Apache and give it a try. If you have errors, review error.log in /var/log/apache2 or submit a comment below and we'll try our best to help you.

$ sudo service apache2 restart

Date: Jan. 12, 2018

Author: Terry Sankar


Followed your directions : Have : Internal Server Error The server encountered an internal error or misconfiguration and was unable to complete your request. error.log : [Fri Jan 12 19:44:15.361543 2018] [mpm_event:notice] [pid 30864:tid 140116263982976] AH00491: caught SIGTERM, shutting down [Fri Jan 12 19:44:15.621039 2018] [wsgi:warn] [pid 31010:tid 140105568868224] mod_wsgi: Compiled for Python/3.5.1+. [Fri Jan 12 19:44:15.621101 2018] [wsgi:warn] [pid 31010:tid 140105568868224] mod_wsgi: Runtime using Python/3.5.2. [Fri Jan 12 19:44:15.622229 2018] [mpm_event:notice] [pid 31010:tid 140105568868224] AH00489: Apache/2.4.18 (Ubuntu) mod_wsgi/4.3.0 Python/3.5.2 configured $ [Fri Jan 12 19:44:15.622259 2018] [core:notice] [pid 31010:tid 140105568868224] AH00094: Command line: '/usr/sbin/apache2' So close ! Thanks for you help.

Date: Jan. 19, 2018

Author: Kevin O'Gorman


The instructions seemed clear enough. I created /build/tstdj as myself, but ensured that directories were at least mode 755 and files at least 644. I changed the configuration slightly because I already had apache2 running: so I made WSGIScriptAlias /tstdj/ /build/tstdj/tstdj/wsgi.py so that the WSGI stuff should live side-by-side with the static pages I already have. I supplied my username as <username> in the WSGIDaemonProcess and the WSGIProcessGroup The end result is a permissions error: - - [19/Jan/2018:11:57:46 -0800] "GET /tstdj/ HTTP/1.1" 403 509 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:57.0) Gecko/20100101 Firefox/57.0" But my existing pages are okay

Date: Jan. 21, 2018

Author: Kevin O'Gorman


I got a message like the one above, but it was not fatal. What was fatal the first time was that this method of installing Django does not update the timezone information properly. If instead, you use pip to install django==1.11 for instance, it notices the problem and installs package pytz, which solves it. I like this better for other reasons too, such as not needing full pathnames to get to django. The warning in the previous message is actually no problem. wsgi was compiled for 3.5.1+ where the "+" means later versions are okay. So the runtime using the next point release is compatible.

Add a new comment here or reply to one above:

For enhanced features and capabilities, please authenticate using a popular third party

your email address will be kept private
previous month
next month