Write Python 3 Web Apps with Apache2 mod_wsgi – Install Ubuntu 16.04 xenial – Every Tiny Part Tested Separately

Python is a popular language for web applications, used by Youtube, DropBox, Facebook, Pintrest, Reddit, Instagram, Spotify, Washington Post and many others. Apache is the most popular web server in the world. Mod_wsgi is the recommended way to use Python with Apache.
This article shows you how to write “Hello WSGI”. This is the first step to start creating your own web apps with Python. This is done step by step, testing every single part in the smallest possible step.

Popular Python web frameworks Django and Flask require mod_wsgi for their production installations.
This article show how to install mod_wsgi step by step. We install the smallest testable part at one time.
Prerequisites: to follow this article, you should be familiar with Linux command line basics, installing Linux, sudo and file system hierarchy.

Install Linux

We start with Ubuntu 16.04 LTS amd64. You could perform these steps on a live USB, locally installed system or a remote virtual private server.
This article was prepared with vagrant. You don’t need vagrant to follow this article, but I’ll list here how the test environment setup. With VirtualBox and Vagrant set up: ‘vagrant init bento/ubuntu-16.04; vagrant up; vagrant ssh’

Install Apache

Install Apache

$ sudo apt-get update
$ sudo apt-get -y install apache2

Test that it works. You can use Firefox or just command line curl:

$ curl -s localhost|grep title
 <title>Apache2 Ubuntu Default Page: It works</title>

Test page appears. Let’s remove the test page

$ echo "Hello static" > /var/www/html/index.html

And verify it’s removed

$ curl -s localhost
Hello static

Now we have Apache web server installed, and we have replaced the static test page with a page that says “Hello static”.

Install Python 3 mod_wsgi

Install the Python 3 version of the Apache WSGI module. Note the ‘-py3’ ending in the packet name.

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

Restart Apache to use the new module

$ sudo service apache2 restart

Check that your module is enabled

$ apache2ctl -M|grep -i wsgi
 wsgi_module (shared)

Create Apache VirtualHost for WSGI

With Apache, VirtualHost means a new website in the Apache installation. For example, terokarvinen.com and botbook.com are VirtualHosts in the same Apache installation.
Here, we create and enable a new VirtualHost for use with WSGI Python programming.
With Apache, we first create a VirtualHost in sites-available/, then enable it by linking from sites-enabled and restarting Apache daemon.
Create the new site. The name must end with “.conf”.

$ sudoedit /etc/apache2/sites-available/terowsgi.conf

Write the contents.

<VirtualHost *:80>
    ServerName terowsgi.example.com
    WSGIScriptAlias / /home/terowsgi/public_wsgi/tero.wsgi
    <Directory /home/terowsgi/public_wsgi/>
        Require all granted
    </Directory>
</VirtualHost>

Only WSGIScriptAlias is specific to WSGI. Otherwise, everything looks just like any VirtualHost setup. In fact, the only change for a static web site would be using DocumentRoot directive in place of WSGIScripAlias.
WSGIScriptAlias tells the ULR path “/” should be handled by a Python programĀ  “/home/terowsgi/public_wsgi/tero.wsgi”. So when a users Firefox address bar shows “http://terowsgi.example.com/”, Apache mod_wsgi will call tero.wsgi Python program.
The sites (VirtualHosts) in sites-available/ are not used until they are linked from sites-enabled/. There is a inconvenience command to do this

$ sudo a2ensite terowsgi.conf

Apache has a nice program for checking configuration before trying it for real.

$ apache2ctl configtest
Syntax OK

In many cases, “apache2ctl configtest” warns you that your computer is not sure about it’s public name. If you also get “Syntax OK”, that’s nothing to worry about.
We just modified some configuration under /etc/. As always, we must restart the daemon for the changes to take effect.

$ sudo service apache2 restart

Let’s try it out.

$ curl -sH 'Host: terowsgi.example.com' localhost|grep title
<title>403 Forbidden</title>

The error messages on the pages, shown to general public, never tell the details. So let’s check the logs!

$ tail -1 /var/log/apache2/error.log
[Sun Feb 12 14:48:21.586533 2017] [authz_core:error] [pid 3431:tid 140391693096704] [client ::1:54482] AH01630: client denied by server configuration: /home/terowsgi

Notice that we have not created the user terowsgi yet, so obviously his home directory “/home/terowsgi” or the wsgi script “tero.wsgi” don’t exist yet. But Apache is looking for them.

Create a Project User

Create a new project user. A project user can’t log in, but you other users can be added to his group to be able to edit some of the files in the project user’s home directory. Because project user is used by many humans, it should not be able to log in.
Create the user normally. The easy-to-use user manipulation commands ‘adduser’ and ‘deluser’ commands all start with a verb. Always use a good password, even if we lock it in a moment. Put your own name in the full name field, so that other sudo users know who created this new user.

$ sudo adduser terowsgi
Enter new UNIX password:
Retype new UNIX password:
 Full Name []: Tero Karvinen WSGI test User

The new project user terowsgi is not a human, so he is not allowed to log in.

$ sudo usermod --lock terowsgi

To make it possible for other users to be able to edit files in “/home/terowsgi/public_wsgi/”, we create the folder and modify the permissions.

$ sudo chmod u=rwx,g=x,o=x /home/terowsgi
$ sudo mkdir /home/terowsgi/public_wsgi
$ sudo chown terowsgi.terowsgi /home/terowsgi/public_wsgi

Magic happens here. Obviously, the regular permissions (user, group, others) should make sense. SetGid bit for the group means that newly created files inherit the same group. This way, multiple users can edit the same folder without continuously fiddling with permissions. Don’t give group permissions directly to the home directory “/home/terowsgi/”, only create subdirectories and give permissions there.

$ sudo chmod u=rwx,g=srwx,o=x /home/terowsgi/public_wsgi

Any user we add to the project user’s group will be able to edit the files.

$ sudo adduser $(whoami) terowsgi

New group membership is valid once you have logged out and back. Alternatively, you can use the ‘newgrp’ command.
Let’s test our new powers in terowsgi’s public_wsgi directory.

$ newgrp terowsgi
$ groups|grep terowsgi
terowsgi adm [...]
$ touch /home/terowsgi/public_wsgi/foo
$ lsĀ  /home/terowsgi/public_wsgi/foo
/home/terowsgi/public_wsgi/foo

So we can indeed edit files udner public_wsgi.

$ rm /home/terowsgi/public_wsgi/foo

Write Python WSGI “Hello World”

$ nano /home/terowsgi/tero.wsgi

Write the simplest possible WSGI program.

def application(env, startResponse):
        startResponse("200 OK", [("Content-type", "text/plain")])
        return [b"See you at TeroKarvinen.com\n"]

The name of the function must be “application”. WSGI will automatically call it with two parameters, environment and startResponse function for callback. We can call our parameters whatever we want.
For response, we perform a callback for the given function startResponse. It takes two parameters, a string for http status, and a list of tuples for response header key-value pairs.
Finally, we return a single item list of bytestrings. This bytestring is the body of the reponse.
And try it out. Remember, we have already set up Apache VirtualHost for this site.

$ curl -sH 'Host: terowsgi.example.com' localhost
See you at TeroKarvinen.com

Did you see the text “See you at…”? Congratulations, that was your tiny Python program talking. You now have WSGI running.
For actual development with Python WSGI, consider some popular Python web frameworks, such as Django or Flask.

Posted in Uncategorized | Tagged , , , , , , , , | 3 Comments

3 Responses to Write Python 3 Web Apps with Apache2 mod_wsgi – Install Ubuntu 16.04 xenial – Every Tiny Part Tested Separately


  1. WSGIDaemonProcess baloon user=xubuntu group=xubuntu threads=5 python-path="/home/xubuntu/wsgi/examplecom/"
    WSGIScriptAlias / /home/xubuntu/wsgi/examplecom/examplecom/wsgi.py
    WSGIProcessGroup baloon
    WSGIApplicationGroup %{GLOBAL}
    WSGIScriptReloading On
    Require all granted

  2. _VirtualHost *:80_
    WSGIDaemonProcess baloon user=xubuntu group=xubuntu threads=5 python-path=”/home/xubuntu/wsgi/examplecom/”
    WSGIScriptAlias / /home/xubuntu/wsgi/examplecom/examplecom/wsgi.py
    _Directory /home/xubuntu/wsgi/examplecom/_
    WSGIProcessGroup baloon
    WSGIApplicationGroup %{GLOBAL}
    WSGIScriptReloading On
    Require all granted
    _/Directory_
    _/VirtualHost_

  3. Peter Raeth says:

    A very good tutorial. No problem following it. Such clarity is rare on the internet.
    Would have liked to see one more component at the end, how to address the website externally. Here is what I did:
    1. Created an account on DigitalOcean (https://www.digitalocean.com). This is very inexpensive. You get an assigned web address .
    2. Followed the tutorial exactly.
    3. Copied /etc/apache2/sites-available/000-default.conf to /etc/apache2/sites-available/000-default.conf.hold.
    4. Overwrote /etc/apache2/sites-available/000-default.conf with /etc/apache2/sites-available/terowsgi.conf
    5. Opened my browser and went to http://.
    There may be more sophisticated things one might do but, at a basic level, this approach works fine.
    Thanks again for a great tutorial