Edgewall Software

Running Trac with Gunicorn

Gunicorn (Green Unicorn) is a Python WSGI HTTP Server for UNIX. It is a pre-fork worker model ported from Ruby's Unicorn project. The Gunicorn server is broadly compatible with various web frameworks, simply implemented, light on server resources, and fairly speedy.

1. Install Gunicorn

Gunicorn is a Python project which can be found on PyPi, so we can use easy_install or pip to install:

$> pip install gunicorn

I prefer to use a virtualenv, to not have to install Gunicorn in a system wide fashion.

2. Write your wsgi file

Gunicorn is WSGI compliant, so we need a Python script tracwsgi.py that functions as an entry point:

import sys
import os

sys.stdout = sys.stderr

# put here your ENV variables
# here is an example with multiple instances
os.environ['TRAC_ENV_PARENT_DIR'] = '/home/repos/trac/'
os.environ['PYTHON_EGG_CACHE'] = '/home/repos/projects/.eggs/'

import trac.web.main
application = trac.web.main.dispatch_request

Now to test it:

$> gunicorn -w2 tracwsgi:application -b 0.0.0.0:8000
2011-03-04 13:21:10 [14900] [INFO] Starting gunicorn 0.12.0
2011-03-04 13:21:10 [14900] [INFO] Listening at: http://0.0.0.0:8000 (14900)
2011-03-04 13:21:10 [14901] [INFO] Booting worker with pid: 14901
2011-03-04 13:21:10 [14902] [INFO] Booting worker with pid: 14902

In your web browser go to http://localhost:8000 and it should work. How it works: Gunicorn is looking for a method called "application" in the tracwsgi.py file.

3. Configure Nginx

Now we will make it work with Nginx, which will redirect Trac requests to Gunicorn:

upstream trac_gunicorn {
    server unix:///home/repos/projects/run/trac.sock;
}

server {
    listen 80;
    server_name     trac.example.com;
    access_log      /var/log/nginx/trac.access.log;
    error_log       /var/log/nginx/trac.error.log info;


    include         "/etc/nginx/acl.conf";

    location / {
        auth_basic "Secure Login";
        auth_basic_user_file /etc/nginx/conf/users;

        proxy_pass      http://trac_gunicorn;
    }

    location ~ /(.*?)/chrome/site/ {
        rewrite /(.*?)/chrome/site/(.*) /$1/htdocs/$2 break;
        root    /home/repos/trac.enabled;
    }
}

Note: Nginx will redirect requests to a UNIX socket. This can either be a simple ip:port combination or multiple instances of Gunicorn. Take a look in the Nginx documentation.

To add a basic or digest authentication, you will have to provide a file as in the example above, created using the htpasswd file. To make Gunicorn listen on a UNIX socket, add this option:

gunicorn -w2 -b unix:///home/repos/projects/run/trac.sock tracwsgi:application

Note: It seems the WSGI entry point does not handle the digest or basic http authentication. To ensure the authentication middleware is passed, you have to modify tracwsgi.py:

import sys
import os

sys.stdout = sys.stderr
os.environ['TRAC_ENV_PARENT_DIR'] = '/home/repos/trac.enabled/'
os.environ['PYTHON_EGG_CACHE'] = '/home/repos/projects/.eggs/'

from trac.web.standalone import AuthenticationMiddleware
from trac.web.main import dispatch_request
from trac.web.auth import BasicAuthentication
def application(environ, start_application):
    auth = {"*" : BasicAuthentication("/etc/nginx/conf/users", "realm")}
    wsgi_app = AuthenticationMiddleware(dispatch_request, auth)
    return wsgi_app(environ, start_application)

Note: If you are using digest authentication, you must create the authentication handler outside of the application def, otherwise the DigestAuthentication object won't retain state between requests.

import sys
import os

sys.stdout = sys.stderr

os.environ['TRAC_ENV_PARENT_DIR'] = '/home/repos/trac.enabled/'
os.environ['PYTHON_EGG_CACHE'] = '/home/repos/projects/.eggs/'

from trac.web.standalone import AuthenticationMiddleware
from trac.web.main import dispatch_request
from trac.web.auth import DigestAuthentication

digest_auth = DigestAuthentication("/etc/nginx/conf/users.htdigest", "realm")

def application(environ, start_application):
    auth = {"*" : digest_auth}
    wsgi_app = AuthenticationMiddleware(dispatch_request, auth)
    return wsgi_app(environ, start_application)

Last modified 5 months ago Last modified on Aug 15, 2017, 9:21:16 PM
Note: See TracWiki for help on using the wiki.