Trac + WSGI on Ubuntu in the Root
As at the time of writing (2008-04-14) Trac 0.11b is not available in the Ubuntu repositories. I've packaged it and made it available from my PPA.
I use mod_wsgi (rather than mod_python) with apache 2.2 and I've packaged that, too (available from my PPA).
These are the instructions for installing and configuring Trac 0.11+ with mod_wsgi apache2.2 in Ubuntu 7.10 Gutsy. Apache 2.2 is configured for virtual hosting and is managed using Webmin and Virtualmin. Virtualmin creates each virtual domain as a user with files in /home/user. In my case Trac is being retro-fitted so it must co-operate nicely with existing directories and files on the web server. That requires some careful use of mod_rewrite RewriteRules since I want Trac to respond at the root of the virtual host (e.g. http://localhost/).
Note: I've used $TRAC_USER to represent the linux user account in all the shell commands here. That makes it easy for you to copy-and-paste these instructions and to include them in scripts. Just remember to set $TRAC_USER first
export TRAC_USER="user"
Add the repository
Add to /etc/apt/sources.list:
Gutsy
deb http://ppa.launchpad.net/intuitivenipple/ubuntu gutsy main deb-src http://ppa.launchpad.net/intuitivenipple/ubuntu gutsy main
Hardy
deb http://ppa.launchpad.net/intuitivenipple/ubuntu hardy main deb-src http://ppa.launchpad.net/intuitivenipple/ubuntu hardy main
Update apt's local package list:
sudo apt-get update
Install Packages
We need to install the packages Trac 0.11+ depends on as well as Trac itself.
sudo apt-get install python-pygments python-genshi trac
SVN
Even if planning on using another repository (i.e. git) it's easiest to install SVN first just to ensure the Trac installation works.
Prepare SVN Directories
Create a skeleton template directory for SVN that can be reused as an empty import each time an SVN repository is created:
sudo mkdir -p /etc/svn/skel/branches /etc/svn/skel/tags /etc/svn/skel/trunk
Create SVN repository
These instructions are geared towards hosting multiple SVN repositories. You can repeat these steps for each repository, replacing $TRAC_USER with the account you are configuring.
svnadmin create /home/$TRAC_USER/svn svn import /etc/svn/skel file:///home/$TRAC_USER/svn -m "Create repository"
Note: the three contiguous forward-slashes in the URL. Read them as schema (file://) + path (/home/$TRAC_USER/svn).
Set the correct permissions:
find /home/$TRAC_USER/svn -type f -exec chmod 660 {} \;
find /home/$TRAC_USER/svn -type d -exec chmod 2770 {} \;
chown -R $TRAC_USER:www-data /home/$TRAC_USER/svn
Prepare Database
If you are using the default Trac choice of SQLite you can skip this step. If you want to use MySQL read on.
In a Virtualmin installation virtual domains each have a MySQL database with a username and password that is the same as the user account. You only need to create the database manually if there isn't one already or you want the user's Trac environment to use a dedicated database rather than keep all the user's tables in the same database.
Install MySQL and Python bindings
Usually MySQL will already be installed, but if not:
sudo apt-get install mysql-server
The Python binding:
sudo apt-get install python-mysqldb
Create a database and set permissions
Note: replace user here with whatever $TRAC_USER is set to.
Log into the database server using an account with full permissions:
mysql -u root -p Password: mysql> CREATE DATABASE user_trac; mysql> GRANT ALL ON user_trac.* TO 'user'@'localhost' IDENTIFIED BY 'password'; mysql> quit
The Trac database connection parameter for this example would be mysql://user:password@localhost/user_trac
Create Trac environment
Because this is a virtual hosting configuration all user-related data is stored under the user's home directory. We'll create the Trac environment:
trac-admin /home/$TRAC_USER/trac initenv "Example Project" \
mysql://$TRAC_USER:password@localhost/${TRAC_USER}_trac svn /home/$TRAC_USER/svn
If everything has gone well the Trac environment will be configured.
Install mod_wsgi
If you've enabled my PPA in your apt's sources.list this should be quick:
sudo apt-get install libapache2-mod-wsgi
The package will enable the module in apache's /etc/apache2/mods-enabled/ directory and restart the server. If you have problems that seem to indicate mod_wsgi isn't responding try fully stopping and restarting the server manually:
sudo /etc/init.d/apache2 stop sudo /etc/init.d/apache2 start
Configure Apache Virtual Host
Confirm mod_wsgi is enabled
ls /etc/apache2/mods-enabled/wsgi* /etc/apache2/mods-enabled/wsgi.conf /etc/apache2/mods-enabled/wsgi.load
Create a Trac WSGI wrapper
Create an apache configuration directory and somewhere for mod_wsgi to extract eggs in the Trac installation:
mkdir /home/$TRAC_USER/trac/apache /home/$TRAC_USER/trac/eggs
Copy the following python script to the file /home/$TRAC_USER/trac/apache/trac.wsgi
# limit per-process stack size (linux default is usually 8MB) to help Virtual Private servers with limited memory allowances
import thread
thread.stack_size(524288)
import sys
sys.stdout = sys.stderr
import os
os.environ['TRAC_ENV'] = '/home/user/trac'
os.environ['PYTHON_EGG_CACHE'] = '/home/user/trac/eggs'
import trac.web.main
_application = trac.web.main.dispatch_request
def application(environ, start_response):
# place-holder in case pre-Trac processing is required
return _application(environ, start_response)
Note: replace user with whatever your $TRAC_USER is set to
This is a WSGI wrapper script that calls Trac. In the application() function you can do some pre-Trac processing of CGI environmental variables, debug logging, or anything else you might need.
Check permissions
Because a process with the web-server credentials is going to need access to the Trac directories and files you need to ensure the permissions are correct.
sudo chown -R $TRAC_USER:www-data /home/$TRAC_USER/trac
sudo find /home/$TRAC_USER/trac -type d -exec chmod 770 {} \;
sudo find /home/$TRAC_USER/trac -type f -exec chmod 660 {} \;
You might be able to tighten these up since the server doesn't need write-access to every file.
Configure the VirtualHost
A standard installation of apache 2 has a per-site configuration file in /etc/apache2/sites-available/ and for each enabled site a symbolic link in /etc/apache2/sites-enabled/.
Edit the site's configuration file with your favourite editor or if using Virtualmin select the domain then choose Services > Configure Website and in the Virtual Server group choose Edit Directives.
Add these sections to the file.
The Trac WSGI wrapper:
WSGIScriptAlias /trac /home/user/trac/apache/trac.wsgi
<Directory /home/user/trac/apache>
WSGIApplicationGroup %{GLOBAL}
Order deny,allow
Allow from all
</Directory>
Note: although it is set to match a URI beginning with /trac a RewriteRule will ensure that it is mapped to the root ( / ) of the domain.
To provide authentication for the login scripts:
<Location /trac/login> AuthName "Trac" AuthType Basic AuthUserFile /home/user/.trac-htpasswd require valid-user </location>
and on the server create the password file with the initial user. You'll be prompted for the user's password:
htpasswd -c /home/$TRAC_USER/.trac-htpasswd $TRAC_USER
Note: don't use the -c option again or you'll overwrite the existing file with a newly created one!
And now the complicated bit - the mod_rewrite rules:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteLog "/home/user/logs/rewrite.log"
RewriteLogLevel 9
# Don't let DirectoryIndex mess with Trac installed in the root
RewriteCond %{REQUEST_URI} ^/$
RewriteRule . /trac [QSA,PT,L]
# Don't let Trac handle existing directories, files or aliases
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME} !-d
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_URI} !^/(awstats|cgi-bin/)
# prepend /trac to URI and append Query String, Pass-Through to xxxAlias directives, Last rule.
RewriteRule ^(.*)$ /trac$1 [QSA,PT,L]
</IfModule>
Note: RewriteLogLevel is set to 9, the DEBUG level, in case of problems. Once you're happy set the level to 0.
Note: Don't forget to replace user with whatever your $TRAC_USER value is
The RewriteCond group is responsible for ensuring:
- The request isn't an existing directory
- The request isn't an existing file
- The request isn't an existing ScriptAlias or Alias (alter these to suit the VirtualHost)
Configure the Locale
This is an important step. Because apache is running with the non-interactive user account www-data it doesn't have it's own locale setting. As a result the default locale "C" of GNU libc is used, which is normally U.S.A. date styles mm/dd/yyyy. This affects any dates Trac displays. To set the correct locale decide on the local name and then add to the VirtualHost:
SetEnv trac.locale en_GB.UTF-8
Save the file and apply the changes (restart apache)
If using Virtualmin simply press the button-link in the top-right of the page that says "Apply Changes". Manually you can use apache2ctl:
sudo apache2ctl restart
or you can use the init script:
sudo /etc/init.d/apache2 restart
Test and Debug
At this point when you access the site root you should see the default Trac front page:
http://localhost/
If you get a server error check the logs:
- Apache VirtualHost error log (/home/$TRAC_USER/logs/error.log)
- Apache mod_rewrite log (/home/$TRAC_USER/logs/rewrite.log)
- Trac log (/home/$TRAC_USER/trac/log/trac.log)
- Apache server log (/var/log/apache2/error.log)
If needed add debug messages to the WSGI wrapper script (/home/$TRAC_USER/trac/apache/trac.wsgi):
def application(environ, start_response):
# place-holder in case pre-Trac processing is required
print >> environ['wsgi.errors'], "*** new request ***"
for key, value in environ.items():
print >> environ['wsgi.errors'], "%s=%s" % (key, value)
return _application(environ, start_response)
and look in /home/$TRAC_USER/logs/error.log:
tail -n 100 /home/$TRAC_USER/logs/error.log | sed 's/^.*\[client .*\..*\..*\..*\] \(.*\), referer.*/\1/' *** new request *** mod_wsgi.reload_mechanism=0 mod_wsgi.listener_port=80 HTTP_REFERER=http://localhost/chrome/common/css/trac.css mod_wsgi.listener_host= SERVER_SOFTWARE=Apache/2.2.4 (Ubuntu) mod_python/3.3.1 Python/2.5.1 proxy_html/2.5 mod_wsgi/2.0 SCRIPT_NAME=/trac HTTP_IF_MODIFIED_SINCE=Fri, 15 Feb 2008 21:07:20 GMT SERVER_SIGNATURE=<address>Apache/2.2.4 (Ubuntu) mod_python/3.3.1 Python/2.5.1 proxy_html/2.5 mod_wsgi/2.0 Server at localhost Port 80</address> REQUEST_METHOD=GET PATH_INFO=/chrome/common/extlink.gif SERVER_PROTOCOL=HTTP/1.1 QUERY_STRING= PATH=/usr/local/bin:/usr/bin:/bin HTTP_ACCEPT_CHARSET=ISO-8859-1,utf-8;q=0.7,*;q=0.7 HTTP_USER_AGENT=Mozilla/5.0 (X11; U; Linux x86_64; en-GB; rv:1.8.1.13) Gecko/20080325 Ubuntu/7.10 (gutsy) Firefox/2.0.0.13 HTTP_CONNECTION=keep-alive HTTP_COOKIE=trac_form_token=e687a532d832295948b8e634; trac_auth=94fe5618847a9d983e514a93d4becd6c SERVER_NAME=localhost REMOTE_ADDR=127.0.0.1 wsgi.url_scheme=http PATH_TRANSLATED=/home/user/trac/apache/trac.wsgi/chrome/common/extlink.gif SERVER_PORT=80 wsgi.multiprocess=True SERVER_ADDR=127.0.0.1 DOCUMENT_ROOT=/home/user/public_html mod_wsgi.process_group= SCRIPT_FILENAME=/home/user/trac/apache/trac.wsgi SERVER_ADMIN=webmaster@domain.net SCRIPT_URI=http://localhost/chrome/common/extlink.gif wsgi.input=<mod_wsgi.Input object at 0x2aaaac21f4f0> HTTP_HOST=localhost SCRIPT_URL=/chrome/common/extlink.gif wsgi.multithread=True mod_wsgi.callable_object=application HTTP_CACHE_CONTROL=max-age=0 REQUEST_URI=/chrome/common/extlink.gif HTTP_ACCEPT=image/png,*/*;q=0.5 wsgi.version=(1, 0) GATEWAY_INTERFACE=CGI/1.1 wsgi.run_once=False wsgi.errors=<mod_wsgi.Log object at 0x166efc0> REMOTE_PORT=51423 HTTP_ACCEPT_LANGUAGE=en-gb,en;q=0.5 mod_wsgi.application_group= mod_wsgi.script_reloading=1 wsgi.file_wrapper=<built-in method file_wrapper of mod_wsgi.Adapter object at 0x1625cd8> HTTP_ACCEPT_ENCODING=gzip,deflate HTTP_KEEP_ALIVE=300
