= How to use Nginx as your main web server for multiple Trac Projects = This recipe below is actually a very simple set up but the instructions did not make it obvious so let me explain. This set up is perfect if you use Nginx as your main webserver. First you set up Nginx as your main web server. Then you set up multiple instances of Tracd Web Server. (Trac has an embedded webserver that is included with Trac by default and this webserver is called Tracd.) Then you have Nginx serve requests to your Tracd web server instances. == Why you should use Tracd behind Nginx == This is an easy set up which allows Nginx to act as an excellent load balancer. Tracd is lightweight and fast and its easy to get working with your Trac projects. You can start multiple instances of the Tracd web server on different ports for different Trac projects. Nginx as your main webserver can serve requests to these various running instances of the Tracd web server. This also works for multiple Trac Projects on multiple vhosts. In short its fast, lightweight, and easy to set up. Here are the steps you need to take. 1. Set up Nginx as your main webserver (on port 80) 2. Start multiple instances of the Tracd embedded web server on different ports for each Trac Project 3. Configure Nginx to serve requests to your various running instances of Tracd Webserver. = Using Tracd with Nginx in Cluster Mode = I am intensely dissatisfied with Trac and Apache. We have multiple vhosts, and multiple trac sites per vhost. When we tried upgrading from Subversion 1.2.3 we hit [http://trac.edgewall.org/ticket/2611# this bug]. * apache/mod_python still occasionally segfaults. * apache/mod_python was causing strange occasional delays. (Likely related to the segfaults). * We would like to upgrade to SVN 1.4 '''Caveat''': Only use this with PostgreSQL. If you want to do this, but are on SQLite, then use Pacopablos [http://trac-hacks.org/wiki/SqliteToPgScript Sqlite-to-Pg]. We use it here, and it's great. == Tracd - Trac's light and fast embedded web server == Run multiple tracd instances. This offers a speed benefit if you use [http://fasterfox.mozdev.org/ FasterFox], as well as good multi-user concurrency responsiveness. Using the Gentoo init system, it was easy to create simple init scripts (which I attatched to this page). Here is a simplified example, which makes for easier wiki'ing '''Multi-Site tracd Startup'''[[br]] 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'''[[br]] ''note'': will not work with anything pre '''0.10'''. `tracd -d -p 3050 --pidfile=/var/live/run/tracd.3051 --protocol=http -s /var/live/trac/telecardia` == Nginx == Get [http://www.nginx.net/ Nginx], the Excellent Apache Replacement by [http://sysoev.ru/en/ Igor Sysoev]. Install it. I use Gentoo, and all of my examples are based on Gentoo. Gentoo packagers create /etc/nginx. /etc/nginx/nginx.conf {{{ #!nginx 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 be free 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; # my system doesn't have the proxy.conf file so I needed to add the following two lines to get redirects working: # proxy_redirect on; # proxy_set_header Host $host; } } }}} == Nginx + SSL == You might be asking about ssl - here is what we do for ssl: /etc/nginx/nginx.conf {{{ #!nginx 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 be free 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 needed to add the following two lines to get redirects working: # proxy_redirect on; # proxy_set_header Host $host; } } } }}} '''Static Content'''[[br]] Serving static files from htdocs dir ala //chrome/site aliases `http://live.trachosts.com/myproj/chrome/site` to `/var/trachosts/trac/myproj/htdocs` {{{ #!nginx location ~ /(.*?)/chrome/site/ { rewrite /(.*?)/chrome/site/(.*) /$1/htdocs/$2 break; root /var/trachosts/trac; } }}} == Subversion == This section isn't necessary if you're using tracd so long as you start tracd 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 will not use this setup, which is good for them. {{{ #!apache Listen 127.0.0.1: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" Options FollowSymLinks AllowOverride None Order allow,deny Allow from all # Subversion Configuration DAV svn SVNParentPath /var/live/svn AuthType Basic AuthName "Client Trac Sites - Subversion Repository" AuthUserFile /var/live/conf/users AuthzSVNAccessFile /var/live/conf/svnauthz Require valid-user }}} Add this to the server section of the Nginx config. (in the :80 line, or the :443, whatever) {{{ #!nginx 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` {{{ #!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` {{{ #!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 }}} ---- 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 better then default FreeBSD rc.d script because it’s smarter and supports multiple Trac Projects.\\ Also it supports '''authentication for each project separately''' with Trac basic authentication! `/usr/local/etc/rc.d/trac` {{{ #!sh #!/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 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 be sure to 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, lets assume that you are using Basic authentication. Digest would be very similar. This is the Nginx configuration snippet: {{{ #!nginx 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 *): {{{ #!sh 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: {{{ #!sh 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 actualy config files ''somewhere''. == Questions == * is this possible with client certificate authentication? ---- See also TracFastCgi#SimpleNginxConfiguration1 {{{ #!sh 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}}")" ] }}}