LaTeX build server with Git and Hudson on Ubuntu 10.04

LaTeX build server with Git and Hudson on Ubuntu 10.04

I’m currently working on a bigger paper for university using LaTeX. As it’s necessary to compile source files multiple times (especially when using BibTeX or TOCs), build runs can take quite some time. As an example, my current build script:

pdflatex -interaction=nonstopmode $BN.tex
bibtex $BN
pdflatex -interaction=nonstopmode $BN.tex
bibtex $BN
pdflatex -interaction=nonstopmode $BN.tex
makeindex -s $ -t $BN.glg -o $BN.gls $BN.glo
pdflatex -interaction=nonstopmode $BN.tex
pdflatex -interaction=nonstopmode $BN.tex
rm -rf $BN.aux
rm -rf $BN.lof
rm -rf $BN.lot
rm -rf $BN.out
rm -rf $BN.toc
rm -rf $BN.bbl
rm -rf $BN.blg
rm -rf $BN.brf
rm -rf $BN.idx
rm -rf $BN.glo
rm -rf $
rm -rf $BN.glg
rm -rf $BN.gls
rm -rf texput.log

This is OK on my workstation, but running a build on my notebook using a small 1.4 GHz single core processor can take up to a minute which is definitely too long. So I looked for solutions how to move the build process to a central server. As I was already using Git for source control on the project, I tried setting up a remote repository on the server which triggered a build using a post-receive script. This basically worked fine, but I wanted to go a step further. I had a look at CI servers and gave Hudson a try as it seems to have a lot of features while being quite easy to set up.

The result is the following: Hudson is polling the Git repository (can be remote or local, in my case it’s a self-hosted remote gitosis installation, but could be github too), starting a new build on changes and publishing the resulting PDF if successful. Hudson is accessible over https using an Apache2 server as frontend to a Tomcat installation.

Ready? Let’s go.

Installing the toolset

First of all, install some basic tools (including git).

$ aptitude install nano curl wget git-core

We need Java to run Hudson, but Sun’s Java has moved to the partner repository which is not enabled by default. Add the repository and install the Java JDK:

$ echo "deb  lucid partner" >> /etc/apt/sources.list
$ aptitude update
$ aptitude install sun-java6-jdk

To build LaTeX papers, we need (of course) a TeX distribution. Texlive should provide you with all you need. I simply installed the full distribution as I didn’t want to hassle with missing packages. Be aware that texlive-full is quite heavy:

Need to get 1035MB of archives. After unpacking 1862MB will be used.

If you are not lazy and are limited on disk space, just install the texlive-* packages you need.

$ aptitude install texlive-full

Install the web server stack:

$ aptitude install apache2 tomcat6 libapache2-mod-jk libapache2-mod-gnutls ssl-cert

We’ll use ant build files to build our projects, so install ant:

$ aptitude install ant

Configure Apache and mod_jk

First of all, activate needed apache modules:

$ a2enmod rewrite jk gnutls

Enable name-based virtual hosts on SSL. Edit /etc/apache2/ports.conf and add a NameVirtualHost *:443 in the mod_gnutls section:

<IfModule mod_gnutls.c>
    NameVirtualHost *:443
    Listen 443

Disable and remove the default apache config:

$ a2dissite default
$ rm /etc/apache2/sites-available/default
$ rm /etc/apache2/sites-available/default-ssl

Create a new virtual host file in /etc/apache2/sites-available/hudson. Mine looks like this (you’ll probably want to adjust the SSL certificates or omit the SSL part by removing the *:80 vhost, changing the *:443 vhost to *:80 and removing the gnutls lines):

<VirtualHost *:80>

        RewriteEngine On
        RewriteRule ^(.*)$$1  [R,L]
<VirtualHost *:443>

        GnuTLSEnable on
        GnuTLSCertificateFile /etc/ssl/certs/ssl-cert-snakeoil.pem
        GnuTLSKeyFile         /etc/ssl/private/ssl-cert-snakeoil.key
        GnuTLSPriorities NORMAL

        DocumentRoot /var/lib/tomcat6/webapps/ROOT/
        <Directory "/var/lib/tomcat6/webapps/ROOT/">
                Options Indexes FollowSymlinks +Includes
                AllowOverride All

        JkMount /* hudson

        ErrorLog /var/log/apache2/hudson.error.log
        CustomLog /var/log/apache2/hudson.access.log combined
        LogLevel error

Create the file /etc/apache2/mods-available/jk.conf with the following content:

JkWorkersFile /etc/apache2/
JkLogFile /var/log/apache2/mod_jk.log
JkLogLevel info

Disable and re-enable the jk module to let apache use jk.conf

$ a2dismod jk
$ a2enmod jk

Create the file /etc/apache2/



Enable the virtual host and restart apache. When you try to connect to your server through your browser you should get redirected to the SSL vhost. The site should say 503 Service Temporarily Unavailable as tomcat is not configured yet.

$ a2ensite hudson
$ /etc/init.d/apache2 restart

Configure Tomcat

Edit /etc/tomcat6/server.xml and enable the AJP connector on port 8009 by uncommenting the following line (somewhere around line 93):

    <!-- Define an AJP 1.3 Connector on port 8009 -->
    <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />

Bind the direct access to tomcat on port 8080 to the local interface. Edit line 71 and add the address attribute:

    <!-- A "Connector" represents an endpoint by which requests are received
         and responses are returned. Documentation at :
         Java HTTP Connector: /docs/config/http.html (blocking & non-blocking)
         Java AJP  Connector: /docs/config/ajp.html
         APR (HTTP/AJP) Connector: /docs/apr.html
         Define a non-SSL HTTP/1.1 Connector on port 8080
    <Connector port="8080" address="" protocol="HTTP/1.1"
               redirectPort="8443" />

Restart tomcat and connect to your apache virtual host defined before. You should see tomcat’s “It works!” page.

Deploy hudson

As hudson is the only application running on this tomcat instance, I deployed it as root application.

$ /etc/init.d/tomcat6 stop
$ rm -rf /var/lib/tomcat6/webapps/ROOT
$ wget -O /var/lib/tomcat6/webapps/ROOT.war

Hudson needs the env variable HUDSON_HOME to run. Edit /etc/tomcat6/context.xml and add the following line inside the Context element:

<Environment name="HUDSON_HOME" value="/var/lib/tomcat6/webapps/ROOT" type="java.lang.String" override="false" />

Now start tomcat and reload the page. You should see a running hudson instance.

Configure hudson

Go to Manage/Configuration and set all options you need (e.g. email settings). If you are running this on a public server, make sure you set some kind of hudson security (I use the internal hudson user database with projectmatrix security). In addition set the environment variable HOME with /usr/share/tomcat6 as value (this is needed for git to work properly).

Next, go to the plugin management site and install the plugins you need. I installed “Hudson GIT plugin” and “Green balls”. After installation restart hudson.

Hudson and Git

To make hudson work with git, we need to configure git for the tomcat user. First, create a .gitconfig file in /usr/share/tomcat6 (tomcat6’s homedir):

    name = Hudson
    email =

If you want to use repositories only accessible via SSH, tomcat needs a SSH keypair. Create a keypair with an empty passphrase:

$ cd /usr/share/tomcat6
$ mkdir .ssh
$ ssh-keygen -t rsa -f .ssh/id_rsa
$ chown tomcat6.tomcat6 .ssh -R

Hudson git cloning will fail if the server you are connecting to is not in tomcat’s known_hosts file. For each server you want to connect to, clone a random repository. This will add the host and avoid later problems. Of course, tomcat has to have access to that server you are cloning from (you need set up the public key we created before on the server – e.g. add it to your public keys on github). For example:

$ cd /tmp
$ sudo -u tomcat6 git clone
# answer with yes
$ rm -rf testrepo

Create a new job

Hudson provides different methods to specify how to build a job – I’m using ant build files. My git repository layout is basically the following:


I created a sample project on github to test the build process. You can find it at

To reflect the build process listed above in the shell script, I created the following buildfile and saved it as /var/lib/tomcat6/webapps/ROOT/build-latex.xml. The file expects a parameter basename, which is the base name of the tex source to be compiled. In my case, it’s set to paper (see job configuration below). Of course, you’ll have to adapt the build file if you need a different build process

<?xml version="1.0" encoding="UTF-8"?>
<project name="latex" default="build" basedir=".">
  <property environment="env"/>
  <property name="workspace" value="${env.WORKSPACE}"/>

  <target name="build">
    <antcall target="clean" />
    <antcall target="cleanoutput" />
    <antcall target="compile" />
    <antcall target="clean" />

  <target name="pdflatex">
    <exec executable="pdflatex" dir="${workspace}/src">
      <arg line="-interaction=nonstopmode ${basename}.tex" />

  <target name="bibtex">
    <exec executable="bibtex" dir="${workspace}/src">
      <arg line="${basename}" />

  <target name="makeindex">
    <exec executable="makeindex" dir="${workspace}/src">
      <arg line="-s ${basename}.ist -t ${basename}.glg -o ${basename}.gls ${basename}.glo" />

  <target name="compile">
    <antcall target="pdflatex" />
    <antcall target="bibtex" />
    <antcall target="pdflatex" />
    <antcall target="bibtex" />
    <antcall target="pdflatex" />
    <antcall target="makeindex" />
    <antcall target="pdflatex" />
    <antcall target="pdflatex" />

  <target name="clean">
      <fileset dir="${workspace}/src" includes="${basename}.aux, ${basename}.lof, ${basename}.lot, ${basename}.out, ${basename}.toc, ${basename}.bbl, ${basename}.blg, ${basename}.brf, ${basename}.idx, ${basename}.glo, ${basename}.ist, ${basename}.glg, ${basename}.gls, texput.log" />

  <target name="cleanoutput">
      <fileset dir="${workspace}/src" includes="${basename}.pdf, ${basename}.log" />

Switch to the web interface and create a new job. Choose “Free style” software project and click next. Using the default settings, I changed the following (labels may differ a bit as my interface is in german, but you’ll find what you need):

Choose Git and set the repo-URL (can be local or remote, e.g. /tmp/project.git, git:// or
Build trigger
Select “poll SCM” and set the value to a valid cron-style line. * * * * * would poll the repository every minute and build the project if it finds changes.
Build process
Add an ant task, click on advanced and specify ../../../build-latex.xml as buildfile. Additionally enter basename=paper in the properties input (specifies the file basename mentioned before).
I only chose “archive artifacts” and set the pattern to src/paper.pdf, src/paper.log. This depends on your environment and your needs.


Alright, time to test our setup. Select the newly created job and click build in the left menu. If all goes well, the build will succeed and you can access the built PDF directly from the web interface. Take a look at the job’s console output to identify potential errors. Happy building :)