Set up nginx as a reverse-proxy for Jira and Jenkins

As you can probably tell, my motto for this January was infrastructure. I set up a new machine as my Xen host, automated the set up of Jenkins for PHP projects, and migrated Jira from my former server to a new virtual instance (sorry, no post. It was too simple to deserve one.).

Now I find myself with a couple of boxes on my network. If you are just a little bit as nerdy as me, your setup might be similar to mine (watch out, simplified for the sake of argument):

In this scenario I have dedicated (virtual) machines for my various applications. However, my ISP only grants me a single IP (bummer!). What I have done in the past is to configure my smart router/modem device to play some clever NAT games and redirecting port 22 to one IP; port 8080 to another; then port 8085 to a third. The issues with this was that I had to use my router, which offers a very cumbersome interface to manage NAT, to set up my infrastructure. Also I had to think about what to do with two boxes needing port 8080 forwarded, I started mixing things (forward 8080 to IP 1:8080 and 8085 from external to IP 2:8080). It became a nightmare.

Using nginx as a proxy

Since I have been using nginx for quite a while now it seemed natural to put it in front of my applications. Especially since other team members had to access them to take part in development work. I have nginx running in front of several Apache HTTP servers successfully, but in all cases both nginx and Apache were running on the same box. This is the scenario you will find covered almost anywhere.

Proxying a Java application server is a different beast, as it has its own intelligence in processing requests. Let’s cut to the chase and have a look at the configuration files.

nginx site configuration

I want one nginx to serve as a proxy to multiple machines. In order to identify where to route the traffic, I decide to use different URLs, locations in nginx terms.

Here’s the location for Jira:

    location ^~ /jira { # myhome.mydomain.org/jira will take you here
        proxy_set_header   Host             $host;
        proxy_set_header   X-Real-IP        $remote_addr;
        proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
        proxy_pass http://192.168.2.2:8080; # configure according to your setup
        proxy_set_header   Authorization ""; # unset authorization in case you use .htaccess
    }

And this is the location for jenkins:

    location ^~ /jenkins/ {
        # Based on https://wiki.jenkins-ci.org/display/JENKINS/Running+Hudson+behind+Nginx
        sendfile off;
        proxy_set_header   Host             $host;
        proxy_set_header   X-Real-IP        $remote_addr;
        proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
        proxy_pass http://192.168.2.32k:8080/jenkins/;
        proxy_max_temp_file_size 0;
        client_max_body_size       10m;
        client_body_buffer_size    128k;
        proxy_connect_timeout      90;
        proxy_send_timeout         90;
        proxy_read_timeout         90;
        proxy_buffer_size          4k;
        proxy_buffers              4 32k;
        proxy_busy_buffers_size    64k;
        proxy_temp_file_write_size 64k;
  }

Download the configuration file for an nginx reverse proxy site.

If you did nothing else than this, then your nginx would already forward all requests properly to your application servers. Unfortunately they would see the request for a URL containing /jira or /jenkins and they would return errors as those URLs are not known. Therefore, we shall adjust the servers to expect visitors to ask for an additional URL path.

Set up Jenkins for nginx reverse proxy

If you are running Jenkins on Debian, preparing it is very simple. You need to add a simple --prefix=/jenkins to your JENKINS_ARGS. This can be done in the file /etc/default/jenkins. If you haven’t changed anything from a base install you must adjust the last line so it looks like this:

JENKINS_ARGS="--webroot=/var/cache/jenkins/war --httpPort=$HTTP_PORT --ajp13Port=$AJP_PORT --prefix=/jenkins"

Set up Jira for nginx reverse proxy

Jira is a little bit more demanding, but not much. I assume you used the suggested way to install it using the Jira installer and haven’t deployed it to your own Tomcat (but that will work just the same). In your Jira application directory (if you haven’t changed anything from a default install it should be in /opt/atlassian/jira you can find a file in the conf directory called server.xml. This file needs to be changed.

There are several XML nodes in here, since some are commented out it can be a bit confusing at first. Try and edit the file in an editor with syntax highlighting, that makes it easier to tell what is commented out.

Find the node <Service name="Catalina">. It is followed by a line that looks somewhat like this:

<Connector  
    acceptCount="100" 
    connectionTimeout="20000" 
    disableUploadTimeout="true" 
    enableLookups="false" 
    maxHttpHeaderSize="8192" 
    maxThreads="150" 
    minSpareThreads="25" 
    port="8080" 
    protocol="HTTP/1.1" 
    redirectPort="8443" 
    useBodyEncodingForURI="true" 
/>

You need to add the three arguments service (either http or https, but I would suggest to always use http as you can terminate your ssl connection at nginx and then use http from there. But let’s not get into this, it is a topic for another time), proxyName, and proxyPort. Then your Connector should be like this:

<Connector  
    acceptCount="100" 
    connectionTimeout="20000" 
    disableUploadTimeout="true" 
    enableLookups="false" 
    maxHttpHeaderSize="8192" 
    maxThreads="150" 
    minSpareThreads="25" 
    port="8080" 
    protocol="HTTP/1.1" 
    redirectPort="8443" 
    useBodyEncodingForURI="true" 
    service="http" 
    proxyName="myhome.mydomain.org" 
    proxyPort="80"
/>

Also there is yet another section that needs to be edited. Find the Context for Engine/Host that looks like this:

<Engine defaultHost="localhost" name="Catalina">  
            <Host appBase="webapps" autoDeploy="true" name="localhost" unpackWARs="true">
                <Context docBase="${catalina.home}/atlassian-jira" path="" reloadable="false" useHttpOnly="true">

Here also adjust the path

<Engine defaultHost="localhost" name="Catalina">  
            <Host appBase="webapps" autoDeploy="true" name="localhost" unpackWARs="true">
                <Context docBase="${catalina.home}/atlassian-jira" path="/jira" reloadable="false" useHttpOnly="true">

Access all the machines

Now you can use http://myhome.mydomain.org to access your nginx behind the router/modem and be forwarded to your various application servers. Almost as if you had a DMZ (well, not quite, but the difference is material enough for another blog post someday). You should use some kind of authentication for both Jira and Jenkins. I typically also want some access restrictions on the reverse proxy already, but you can also leave this out (for example, if you want to serve public sites from your home server as well).

Author image
Blogging since 2003 about life, tech, yoga. Passionate about the details and eager to know more. Systems theory meets empathy.
Bochum. Germany.
top