Contents
Using Nginx as your main web server for multiple Trac projects
This recipe below describes some setups of the Nginx webserver in your Trac project. Nginx is an Apache replacement for load balancing purposes among other things and is written by Igor Sysoev.
First set up Nginx as your main web server and then set up multiple instances of tracd web server. Trac has an embedded webserver called tracd. Then you have Nginx serve requests to your tracd web server instances.
Why you should use tracd behind Nginx
This is a setup where Nginx acts as a load balancer. In addition, Tracd is lightweight and fast and it is easy to get working with Trac. You can start multiple instances of the tracd web server on the ports for different Trac projects. Nginx as your main webserver can serve requests to these instances of the tracd web server. This also works for multiple Trac Projects on multiple vhosts.
Here are the steps you need to take:
- Set up Nginx as your main webserver on port 80.
- Start multiple instances of the tracd web server on the ports for each Trac project.
- Configure Nginx to serve requests to your running instances of tracd web server.
Using tracd with Nginx in Cluster Mode
When using Trac and Apache with multiple vhosts, and multiple Trac sites per vhost, and upgrading from Subversion 1.2.3 to 1.4, we hit this bug:
- apache/mod_python occasionally segfaults.
- apache/mod_python was occasionally causing delays, likely related to the segfaults.
Caveat: Only use this with PostgreSQL. If you want to do this, but are on SQLite, then use Pacopablos Sqlite-to-Pg script. It is used here.
Tracd - Trac's light and fast embedded web server
Run multiple tracd instances. This offers good concurrency responsiveness.
Using the Gentoo init system, it is easy to create simple init scripts, which are attached to this page. Here is a simplified example:
Multi-Site tracd Startup: works with Trac-0.10 and up:
tracd -d -p 3050 --pidfile=/var/live/run/tracd.3050 --protocol=http -e /var/live/trac
Single-Site tracd Startup: will not work with Trac before version 0.10:
tracd -d -p 3050 --pidfile=/var/live/run/tracd.3051 --protocol=http -s /var/live/trac/telecardia
Nginx
Install Nginx. All examples are based on Gentoo and the Gentoo package is under /etc/nginx
.
Sample /etc/nginx/nginx.conf
:
http { include /etc/nginx/mime.types; default_type application/octet-stream; include /etc/nginx/nginx-defaults.conf; upstream live_trachosts_com { server 127.0.0.1:3050; server 127.0.0.1:3051; #[... up to the number of instance, or more, if you want to add more ...] } server { listen 192.168.1.254:80; server_name live.trachosts.com live; access_log /var/log/nginx/live.access.log main; error_log /var/log/nginx/live.error_log info; location / { proxy_pass http://live_trachosts_com; include /etc/nginx/proxy.conf; # if your system doesn't have the proxy.conf file, add the following two lines to get redirects working: # proxy_redirect on; # proxy_set_header Host $host; } }
Nginx + SSL
Here is what we do for SSL in /etc/nginx/nginx.conf
:
http { include /etc/nginx/mime.types; default_type application/octet-stream; include /etc/nginx/nginx-defaults.conf; upstream live_trachosts_com { server 127.0.0.1:3050; server 127.0.0.1:3051; #[... up to the number of instance, or more, if you want to add more ...] } server { listen 192.168.1.254:80; server_name live.trachosts.com live; access_log /var/log/nginx/live.access.log main; error_log /var/log/nginx/live.error_log info; location / { rewrite ^/(.*)$ https://imrlive.com/$1 redirect; } } server { listen 192.168.1.254:443; server_name live.trachosts.com live; access_log /var/log/nginx/live.access.log main; error_log /var/log/nginx/live.error_log info; ssl on; ssl_certificate /etc/nginx/ssl/_nginx.cert; ssl_certificate_key /etc/nginx/ssl/traclive.key; keepalive_timeout 70; add_header Front-End-Https on; location / { proxy_pass http://live_trachosts_com; include /etc/nginx/proxy.conf; # my system doesn't have the proxy.conf file so I added the following two lines to get redirects working: # proxy_redirect on; # proxy_set_header Host $host; } } }
Static Content
Serving static files from the htdocs directory as in /<site>/chrome/site
aliases http://live.trachosts.com/myproj/chrome/site
to /var/trachosts/trac/myproj/htdocs
:
location ~ /(.*?)/chrome/site/ { rewrite /(.*?)/chrome/site/(.*) /$1/htdocs/$2 break; root /var/trachosts/trac; }
Subversion
This section can be skipped, if you're using tracd and start it as follows:
/usr/bin/python /usr/bin/tracd -d -p 3050 --basic-auth projec1,/var/www/trac/project1/db/users.htdigest,svn --pidfile=/var/www/trac/tracd.3050 --protocol=http -e /var/www/trac
We still need to get access to Subversion via Apache mod_dav_svn. I created a vhost in Apache for _only_ the svn URLs. Other people might not use this setup.
Listen 127.0.0.1:80 <VirtualHost *:80> ServerAdmin webmaster@trachosts.com # in order to support COPY and MOVE, etc over https (443), # ServerName _must_ be the same as the nginx servername ServerName live.trachosts.com UseCanonicalName on DocumentRoot "/var/www/live.trachosts.com/htdocs" <Directory "/var/www/live.trachosts.com/htdocs"> Options FollowSymLinks AllowOverride None Order allow,deny Allow from all </Directory> # Subversion Configuration <IfModule mod_dav_svn.c> <Location /svn> DAV svn SVNParentPath /var/live/svn AuthType Basic AuthName "Client Trac Sites - Subversion Repository" AuthUserFile /var/live/conf/users <IfModule !mod_authz_svn.c> AuthzSVNAccessFile /var/live/conf/svnauthz </IfModule> Require valid-user </Location> </IfModule> </VirtualHost>
Add this to the server section of the Nginx config, in the :80 line, or the :443:
location /svn { proxy_pass http://127.0.0.1:80; include /etc/nginx/proxy.conf; set $dest $http_destination; if ($http_destination ~ "^https://(.+)") { set $dest http://$1; } proxy_set_header Destination $dest; }
Script Examples
start-meta-site.sh
:
INSTANCES="3050 3051 3052 3053 3054 3055 3056" USER="apache" VERSION="0.10.1" #ENV="/var/live/trac" PIDFILE="/var/live/run/tracd" ### Extra Args here, for instance --basic-auth #ARGS="--basic-auth=${ENV},${ENV}/vccbob.pwd,FarQ" ARGS="-e /var/live/trac" PYTHON_EGG_CACHE="/var/live/egg_cache" function start(){ export PYTHON_EGG_CACHE for I in $INSTANCES; do su ${USER} -c "/usr/bin/tracd -d -p ${I} --pidfile=${PIDFILE}.${I} --protocol=http ${ARGS} ${ENV}" done } function stop(){ for x in `ls ${PIDFILE}.*}`; do kill `cat ${x}` done } $1
start-single-site.sh
INSTANCES="3050 3051 3052" USER="apache" VERSION="0.10.1" ENV="/var/lib/trac-single" PIDFILE="/var/lib/trac-single/run" ### Extra Args here, for instance --basic-auth #ARGS="--basic-auth=${ENV},${ENV}/vccbob.pwd,FarQ" ARGS="--basic-auth=${ENV},${ENV}/vccbob.pwd,FarQ -s ${ENV}" PYTHON_EGG_CACHE="${ENV}/egg_cache" function start(){ export PYTHON_EGG_CACHE for I in $INSTANCES; do su ${USER} -c "/usr/bin/tracd -d -p ${I} --pidfile=${PIDFILE}.${I} --protocol=http ${ARGS}" done } function stop(){ for x in `ls ${PIDFILE}.*`; do kill `cat ${x}` done } $1
FreeBSD
Startup rc.d script for FreeBSD that allows you to run StandAlone server for multiple Trac Projects behind Nginx with ngx_http_proxy_module (proxy_pass) module. This script is an improvement over the default FreeBSD rc.d script, because it supports multiple Trac Projects. Also it supports authentication for each project separately with Trac basic authentication.
/usr/local/etc/rc.d/trac
:
#!/bin/sh # # Trac (http://trac.edgewall.org/) startup rc.d # # (c) 2012 Stanislav Rudenko, sandel{at}ukr.net # Thanks to Alexey Tsvetnov, vorakl{at}fbsd.kiev.ua # last update 11.03.12 # PROVIDE: tracd # REQUIRE: LOGIN # KEYWORD: shutdown # # Add the following line to /etc/rc.conf to enable trac: # trac_enable="YES" # # and optional: # trac_data="/usr/local/www/trac" # trac_config="/usr/local/etc/trac.conf" # trac_user="trac" # trac_group="trac" # trac_port="8000" # trac_host="127.0.0.1" # trac_pidfile="/var/run/trac/trac.pid" # # You _MUST_ create config file --> /usr/local/etc/trac.conf # and add the following lines: # # DIR_PROJECT_1="/usr/local/www/trac/myproj" # NAMEDIR_PROJECT_1="myproj" # PATH_PASSFILE_1="/usr/local/www/trac/.htpasswd_myproj" # NAME_REALM_1="My Project" # # (also you can increment variables to use multiply projects) # (for example DIR_PROJECT_2,...,NAME_REALM_2 etc...) # . "/etc/rc.subr" name="trac" rcvar=`set_rcvar` load_rc_config $name # Set some defaults trac_enable=${trac_enable:-"NO"} trac_data=${trac_data:-"/usr/local/www/trac"} trac_config=${trac_config:-"/usr/local/etc/trac.conf"} trac_user=${trac_user:-"trac"} trac_group=${trac_group:-"trac"} trac_port=${trac_port:-"8000"} trac_host=${trac_host:-"127.0.0.1"} trac_pidfile=${trac_pidfile:-"/var/run/trac/trac.pid"} start_cmd="trac_start" start_precmd="trac_precmd" required_files="/usr/local/bin/sudo" pidfile=${trac_pidfile} command="/usr/local/bin/tracd" command_interpreter="/usr/local/bin/python2.5" trac_precmd() { if [ ! -s "${trac_config}" ] then trac_configexample return 1 fi . "${trac_config}" piddir=$(/usr/bin/dirname "${trac_pidfile}") if [ ! -d ${piddir} ] then /bin/mkdir -p "${piddir}" /usr/sbin/chown ${trac_user}:${trac_group} "${piddir}" fi /usr/local/bin/sudo -u ${trac_user} /bin/sh -c "if [ -d \"${piddir}\" -a -w \"${piddir}\" ];then return 0;else return 1;fi" if [ $? -ne 0 ] then echo "Can't write .pid file to \"${piddir}\"!!! Change directory permissions!" return 1 fi if [ -s "${trac_pidfile}" ] then /bin/ps -axp "$(/bin/cat ${trac_pidfile})" >/dev/null 2>&1 if [ $? -eq 0 ] then echo "trac already running! (check ${trac_pidfile})" return 1 fi /bin/rm -f "${trac_pidfile}" else trpid=$(/usr/bin/pgrep "/usr/local/bin/tracd") if [ -n "${trpid}" ] then echo "trac(${trpid}) already running, but can't find PID file!!!" return 1 fi fi i=0 while : do i=$(( $i + 1 )) [ -z "$(eval echo "\${DIR_PROJECT_${i}}")" ] && break NAMEDIR_PROJECT=$(eval echo "\${NAMEDIR_PROJECT_${i}}") PATH_PASSFILE=$(eval echo "\${PATH_PASSFILE_${i}}") NAME_REALM=$(eval echo "\${NAME_REALM_${i}}") DIR_PROJECT=$(eval echo "\${DIR_PROJECT_${i}}") basicauth="${basicauth} --basic-auth=\"${NAMEDIR_PROJECT},${PATH_PASSFILE},${NAME_REALM}\"" projects="${projects} \"${DIR_PROJECT}\"" done rc_flags="-d -p ${trac_port} -b ${trac_host} -e ${trac_data} --pidfile=${pidfile}${basicauth}${projects}" } trac_start() { /bin/echo -n "Starting ${name}" eval /usr/local/bin/sudo -u ${trac_user} env PYTHON_EGG_CACHE=${trac_data}/.python-eggs ${command} ${rc_flags} /bin/echo "." } trac_configexample() { echo "" echo "==========================================================" echo "You _MUST_ create config at /usr/local/etc/trac.conf" echo "and add the following lines:" echo "" echo "DIR_PROJECT_1=\"/usr/local/www/trac/myproj\"" echo "NAMEDIR_PROJECT_1=\"myproj\"" echo "PATH_PASSFILE_1=\"/usr/local/www/trac/.htpasswd_myproj\"" echo "NAME_REALM_1=\"My Project\"" echo "==========================================================" echo "" } run_rc_command "$1"
Handling authentication in Nginx
If you want to handle the authentication in Nginx rather than through Trac, that is also possible. Since you are proxying the tracd server from Nginx, you just have to tell Nginx to forward the authorization header to tracd, and use the same authentication scheme in both (Basic / Digest). Also, both Nginx and Trac must access the same password file, or an identical copy. As a simple example, let's assume you are using Basic authentication. Digest would be very similar.
This is the Nginx configuration snippet:
server { location / { proxy_pass http://localhost:8000; # Replace localhost:8000 with your server:port auth_basic "Restricted"; auth_basic_user_file htpasswd; # Will effectively be /etc/nginx/htpasswd in Ubuntu, check your distribution proxy_pass_header Authorization; # Here you tell Nginx to forward the Authorization header to tracd } }
And then, you can start tracd with the following command if you use multi-project setup (notice the *):
tracd --port=8000 --hostname=127.0.0.1 --env-parent-dir=/home/trac --basic-auth="*,/etc/nginx/htpasswd,Restricted"
Or the following command if you run one tracd per project:
tracd --port=8000 --single-env /path/to/trac/environments/project --basic-auth="project,/etc/nginx/htpasswd,Restricted"
You can adjust those commands to your specific needs (daemonize, etc).
Todo
- Post the actual config files somewhere.
Questions
- is this possible with client certificate authentication?
See also TracFastCgi#NginxConfiguration