Setting Up Nginx for CATMAID

Of course, using Apache and its WSGI module (mod_wsgi) is not the only way to run CATMAID. There are many web and WSGI servers available. This section is intended to provide information on such alternatives, and in particular the use of Nginx and various WSGI servers.

The installation instructions provided here, assume that you have set up the database and Django as described in the standard installation instructions.

Setup based on Nginx and Gevent

Nginx is a web server with focus on high performance and concurrency while maintaining a low memory footprint. However, it is (by default) not a WSGI server and one needs to set this up separately. Here, we will use Gevent to provide this functionality. It is a WSGI server is based on Python coroutines and greenlets.

Of course, you need to install Nginx, and the libevent package if you will use gevent. In Debian based distributions, this can be done with:

sudo apt-get install nginx libevent-dev

Nginx can be started after this.

Gevent in turn is a Python module. To make it usable, activate the virtualenv and install Gevent by running:

pip install gevent

After this, Gevent is usable. In the next sections we will configure both the web and the WSGI server.

Nginx configuration

A good general introduction to Nginx configuration can be found here. In the following, a Nginx configuration is provided to give access to CATMAID:

upstream catmaid-wsgi {
    server 127.0.0.1:8080;
}

server {
    listen 80;
    server_name <CATMAID-HOST>;

    # Give access to Django's static files
    location /catmaid/static/ {
        alias <CATMAID-PATH>/django/static/;
    }

    # Route all CATMAID Django WSGI requests to the Gevent WSGI server
    location /catmaid/ {
        proxy_pass http://catmaid-wsgi/;
        proxy_redirect http://catmaid-wsgi/ http://$host/;
        # This is required to tell Django it is behind a proxy
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        # This lets Django know which protocol was used to connect and also
        # overrides the header a client who fakes it.
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

This setup expects CATMAID to be accessible from a catmaid subdirectory under the domain’s root. To use this configuration when CATMAID lives on the domain’s root, just remove /catmaid from every location block (and do the same in Django’s settings.py, of course).

The first block (upstream) defines where the Gevent server will be available. In this case, we assumed we can access it under 127.0.0.1:8080. The server block defines the actual web server.

There you have to adjust <CATMAID-HOST> to where your CATMAID instance should be available (e.g. catmaid.example.org). The first location block defines from where the static files should be served. The <CATMAID-PATH> placeholder needs to be replaced with the absolute path to your CATMAID folder. The second location block passes all requests to the WSGI server defined before and allows therefore the execution of Django.

Image data

Serving image data works the same way as serving CATMAID static data. However, you might want to add a so called CORS header to your Nginx location block:

Access-Control-Allow-Origin *

Without this header, only a CATMAID instance served from the same domain name as the image data will be able to access it. If the image data should be accessed by CATMAID instances served on other domains, this header is required. A typical tile data location block could look like this:

location /tiles/ {
  # Regular cached tile access
  alias /path/to/tiles/;
  expires max;
  add_header Cache-Control public;
  # CORS header to allow cross-site access to the tile data
  add_header Access-Control-Allow-Origin *;
}

Besides adding the CORS header, caching is also set to be explicitly allowed, which might be helpful for data that doesn’t change often.

A note on the proxy_redirect command

In general, this command modifies the Location and the Refresh HTTP header fields in the header of a redirect reply of the proxied server. In our case this is the WSGI server, running CATMAID. Redirects happen e.g. as the correct response to HTTP POST request (which e.g. happen if you change something from within the admin interface). The first URL gets replaced by the second one, i.e. http://catmaid-wsgi/ with http://$host/. The $host variable is the header’s Host field and therefore the host CATMAID is running on. This makes the outside world see the front end server in the request URLs—a good thing and if CATMAID is not running in a subdirectory, one can remove this line and the default behavior should just work. The default behavior replaces the URL given to proxy_pass with the path of the whole location block. When CATMAID doesn’t live in a subdirectory, this is equivalent to:

proxy_redirect http://catmaid-wsgi/ /;

This is fine, so the line could be removed, but it gets a problem if CATMAID lives in a subdirectory. The default behavior would then translate to (wrt. to the configuration above):

proxy_redirect http://catmaid-wsgi/ /catmaid/;

If CATMAID lives in a subdirectory, you likely also have the FORCE_SCRIPT_NAME property in your settings file set accordingly (e.g. to /catmaid). In short, this leads Django to prepend every generated URL with this path. If in a subdirectory, it is needed for all types of HTTP requests—not only, but also for redirects. This in turn results in prepending the subdirectory twice for redirect requests: 1. Django does it due to FORCE_SCRIPT_NAME 2. Nginx does it when proxy_redirect is used with its default behavior (e.g. if left out). To fix this, the rewrite of proxies redirects has to be explicitly set to rewrite the WSGI URL to $host or to /, i.e. to:

proxy_redirect http://catmaid-wsgi/ http://$host/;

Therefore, it is is part of the above configuration.

Gevent run script

To start Gevent, a small Python script is used. It is best to place it in:

<CATMAID-path>/django/projects/mysite/

There, you put the following lines into a file (e.g. run-gevent.py):

#!/usr/bin/env python

# Import gevent monkey and patch everything
from gevent import monkey
monkey.patch_all(httplib=True)

# Import the rest
from django.core.wsgi import get_wsgi_application
from django.core.management import setup_environ
from gevent.wsgi import WSGIServer
import sys
import settings

setup_environ(settings)

def runserver():
    # Create the server
    application = get_wsgi_application()
    address = "127.0.0.1", 8080
    server = WSGIServer( address, application )
    # Run the server
    try:
        server.serve_forever()
    except KeyboardInterrupt:
        server.stop()
        sys.exit(0)

if __name__ == '__main__':
    runserver()

If executed, this will start a Gevent server on IP 127.0.0.1 and port 8080. Adjust those values to your liking.

Having configured and started both servers, you should now be able to access CATMAID.

Setup based on Nginx and uWSGI

uWSGI is a versatile WSGI server written in C, and can serve as the middle layer between Nginx and CATMAID.

On Ubuntu 12.04, install nginx and uwsgi:

sudo apt-get install nginx uwsgi uwsgi-plugin-python

Here is a sample uWSGI configuration file. On Ubuntu, this can be saved as /etc/uwsgi/apps-available/catmaid.ini, with a soft link to /etc/uwsgi/apps-enabled/catmaid.ini:

; uWSGI instance configuration for CATMAID
[uwsgi]
virtualenv = /home/alice/.virtualenvs/catmaid
chdir = <CATMAID-PATH>/django
socket = /run/uwsgi/app/catmaid/socket
mount = /=<CATMAID-path>/django/projects/mysite/django.wsgi
plugins = python
; manage-script-name is required if CATMAID will be run in a subdirectory
manage-script-name = true

You now be able to start uWSGI manually with one of the following:

uwsgi --ini /etc/uwsgi/apps-available/catmaid.ini
(or)
service uwsgi start catmaid.ini

Here is a sample nginx configuration file:

server {
    listen 8080;
    server_name <CATMAID-HOST>;

    # Give access to Django's static files
    location /catmaid/static/ {
       alias <CATMAID-PATH>/django/static/;
    }

    # Route all CATMAID Django WSGI requests to uWSGI
    location /catmaid/ {
        include uwsgi_params;
        uwsgi_pass unix:///run/uwsgi/app/catmaid/socket;
    }
}

Setup based on Nginx and Gunicorn

For using the Gunicorn WSGI server, the same Nginx configuration can be used as that given above for use with gevent. (You may need to change the port, however.) As an example of how to start Gunicorn, there is a upstart script, suitable for Ubuntu, in django/projects/mysite/gunicorn-catmaid.conf. You would copy this to /etc/init/, customize it, and start Gunicorn with initctl start gunicorn-catmaid. (Thereafter it will be started on boot automatically, and can be restarted with initctl restart gunicorn-catmaid.

Using Supervisord for process management

Depending on your setup, you might use custom scripts to run a WSGI server, Celery or other server components. In this case, process management has to be taken care of as well, so that these scripts are run after a e.g. a server restart. One way to do this is using supervisord. We found it to be reliable, flexible and easy to configure with multiple custom scripts. For each program or program group a new configuration file has to be created:

/etc/supervisor/conf.d/<name>.conf

Such a configuration file can contain information about individual programs and groups of them (to manage them together). Below you will find an example of a typical setup with a Gunicorn start script and a Celery start script, both grouped under the name “catmaid”:

[program:catmaid-app]
command = /opt/catmaid/django/projects/mysite/run-gunicorn.sh
user = www-data
stdout_logfile = /opt/catmaid/django/projects/mysite/gunicorn.log
redirect_stderr = true

[program:catmaid-celery]
command = /opt/catmaid/django/projects/mysite/run-celery.sh
user = www-data
stdout_logfile = /opt/catmaid/django/projects/mysite/celery.log
redirect_stderr = true

[group:catmaid]
programs=catmaid-app,catmaid-celery

This of course expects a CATMAID instance installed in the folder /opt/catmaid/. An example for a working run-celery.sh script can be found here. With the configuration and the scripts in place, supervisord can be instructed to reload its configuration and start the catmaid group:

$ sudo supervisorctl reread
$ sudo supervisorctl update
$ sudo supervisorctl start catmaid:

For changed configuration files also both reread and update are required.