Deploying Trac with Nginx and Gunicorn

I very recently re-deployed the Trac application on my server. The first time I did this, I used the included tracd server, but I had a very hard time configuring it on a non-root path with Nginx. So I decided to use Gunicorn, which is also reported to give better performance (is it true? I did not test).

The configuration is a front-end server running Nginx coupled with a back-end server running Gunicorn + Trac and the database which will be PostgreSQL. Static files will be deported at the end to the front-end server for performance. I will not go too much into details in the process of installing the components, as they were packaged for the distribution I use (ArchLinux) and my environment did not need a strong separation into a virtual environment. So these steps are rather easy and better explained on the official website.

Setting up the Trac environment

First of all, let’s setup the trac environment. We create a user on the back-end server that will run the Gunicorn + Trac services and get permission on the content. With this approach, it is easier to manage permissions and handle administration tasks on the projects. This user also needs an access to the database.  The next step is to create the Trac project. We use the home directory as our workspace.

Configure Gunicorn

Now, we are ready to deploy. We need to expose the WSGI application of Trac to Gunicorn, which is basically trac.web.main.dispatch_request. There is just a small tweak to do in the environment, as Gunicorn moves the REMOTE_USER header to HTTP_REMOTE_USER which Trac doesn’t expect. This is needed for authentication to work. So here is the script, to put into /home/trac/tracwsgi.py or somewhere else you like.

Don’t forget then to create the appropriate egg cache directory:

You may also choose to delete the line in the Python script if your eggs are already unpacked by your package manager when installed. Then, we must configure Gunicorn, for example in /home/trac/gunicorn.py:

Finally, we need a Systemd service file (trac-gunicorn.service):

Inside the project configuration (repo/PROJECT/conf/trac.ini), you may want to change the option logging/log_type to stderr and adjust the log_level so that Trac logs to journald. And we’re good to go for the backend.

Configure Nginx for Single Project

The configuration here is rather simple. It is assumed that Trac will be reachable from http[s]://www.example.com/prefix:

After reloading Nginx, you should be able to get into Trac with your browser! If you prefer to force your users to authenticate before accessing the site, remove the nested location bloc (/prefix/login) and place the auth directives in the parent bloc (/prefix). You can of course also use other kinds of authentication mechanisms that work with Nginx (I use LDAP).

Let’s now offload the static files to the front-end. On the back-end server, collect the files:

Inside the created statics folder, there is a directory called htdocs, containing all the static files. This is the folder we need to transfer to the front-end (for example using tar and scp). Let’s say we want to have the statics in /www/assets. The easiest way would be to place the htdocs folder into /www/assets/prefix and rename that folder to chrome. That way, the folder structure will match the URLs (e.g. prefix/chrome/common/js/trac.js). In case it is not possible (or you don’t want to) you’ll have to play with the rewrite action to make the URLs point to the right file path. Once done, we need to direct Nginx to catch every request to prefix/chrome and return a local file. Add these lines inside the location ^~ /prefix bloc where the placeholder comment shows:

and reload Nginx. And voilà!

Configure Nginx for Multiple Projects

I still have to test this configuration. Coming soon!