Date

After having uWSGI installed on my server but not configured for years, I have finally decided to go set it up properly. This setup was done on Ubuntu 16.04 and is mostly based off of these two articles along with a large collection of various StackOverflow and ServerFault questions that I unfortunately didn't keep track of. These notes assume that uWSGI will be used to deploy apps written in Python. You will need to adjust some of the steps if you are deploying other languages.

In general, this setup was as painless as I would reasonably expect. Most of the difficultly resolved around my somewhat-unusual requirements — namely that I wanted to have more than one WSGI application per virtual host, and I wanted the ability to add/remove WSGI applications via the git push mechanism I documented earlier. I can accept that it does seem like I am forcing uWSGI to conform to my own deployment method rather than going for a more "normal" site organization strategy. However, I believe that the setup I have here is more suitable for the organization of my websites (a huge dumping ground of small components rather than one large app), and the fact that it mostly Just Worked™ is a testament to uWSGI's flexibility.

The first step in the setup process is to install uWSGI. I opted to install the version from the Ubuntu repository rather than to install the latest stable via pip or from source. I wanted the convenience of being able to add support for a new language later by simply using apt-get install, and I didn't feel I was likely to require the latest and greatest features. To install from the repositories, install the uwsgi, uwsgi-emperor, and uwsgi-plugin-python packages. If you do not already have them, also install python-pip and python-virtualenv. (This installs the Python 2 version of the packages. Replace instances of python with python3 to get the corresponding Python 3 versions.)

Next, I chose to replace the Ubuntu-provided init scripts for uWSGI with a systemd unit file. There was no real reason for this other than because the two tutorials these notes are based on decided to use systemd, and because systemd is supposed to be The Future™.

(I will now insert a mini-rant here and say that Ubuntu's "halfway adopt systemd but keep around a bunch of older init stuff" is probably the worst of the possible choices of init systems. Some of the issues this has caused me include:

  • losing the getty on ttyS0/ttyHVC0 (it needs to be explicitly reenabled in systemctl)
  • a number of warnings mentioning Upstart jobs and runlevels whenever a large dist-upgrade is performed (I have just been ignoring these for now because nothing detrimental seems to happen as a result.)

All of these issues surfaced after an upgrade from Ubuntu 14.04 to 16.04, and IMHO a better transition would have been appreciated.)

Anyways, place the following contents into /etc/systemd/system/uwsgi-emperor-systemd.service:

[Unit]
Description=uWSGI Emperor
After=syslog.target

[Service]
ExecStartPre=/bin/bash -c 'mkdir -p /run/uwsgi; chown www-data:www-data /run/uwsgi'
ExecStart=/usr/bin/uwsgi --ini /etc/uwsgi-emperor/emperor.ini
Restart=always
KillSignal=SIGQUIT
Type=notify
StandardError=syslog
NotifyAccess=all

[Install]
WantedBy=multi-user.target

Now run the following commands to disable the Ubuntu init scripts and activate this new one:

sudo systemctl stop uwsgi.service
sudo systemctl stop uwsgi-emperor.service
sudo systemctl disable uwsgi.service
sudo systemctl disable uwsgi-emperor.service
sudo systemctl enable uwsgi-emperor-systemd.service
sudo systemctl start uwsgi-emperor-systemd.service

Note that unlike the Ubuntu-provided init scripts, this unit file will cause uWSGI to log to journalctl rather than its own log file.

The next and final "global system" configuration step is to create symlinks in /etc/uwsgi-emperor/vassals/ that point to /var/www/<site>/conf/uwsgi-master.ini (or some other filename of your choice).

$ ls -l /etc/uwsgi-emperor/vassals/
lrwxrwxrwx 1 root root  43 Aug 30 05:25 robertou.com.ini -> /var/www/robertou.com/conf/uwsgi-master.ini
...

In order to potentially contain multiple apps in one virtual host without needing to create additional symlinks, the file conf/uwsgi-master.ini in each site's git repository contains only the following two lines:

[uwsgi]
emperor = /var/www/<site>/conf/apps

(Note that the %d magic variable will not work here because uWSGI doesn't follow the symlink before computing %d .)

Although I didn't find any explicit documentation that this would do what I expected, uWSGI is indeed smart and understands the intention of creating a sub-Emperor as can be observed in the logs:

Aug 30 05:04:21 <snip> uwsgi[4882]: *** starting uWSGI sub-Emperor ***

An individual configuration file for each app can now be placed in the conf/apps/ directory in each virtual host's repository. An example of one is below:

[uwsgi]
socket = /run/uwsgi/rqou.com-testapp1.sock
chmod-socket = 660
# XXX do we need the build thing?
chdir = /var/www/rqou.com/build/testapp1
master = true
virtualenv = /var/www/rqou.com/venv
module = testapp1:app
processes = 1
threads = 1
plugins = python

Finally, insert an appropriate proxying command into conf/nginx.conf:

    location = /testapp1 { rewrite ^ /testapp1/; }
    location /testapp1 {
        include /etc/nginx/uwsgi_params;
        uwsgi_pass unix:/run/uwsgi/rqou.com-testapp1.sock;
    }

When these configuration changes are pushed to the webdeploy user, uWSGI seems to automatically detect the change and gracefully reloads. Hopefully the app now works!