Multiple Tomcat Instances

When building a real application you often find yourself having to deal with different stages of the software. The most common stages are development, testing and production, but you can have many more. This means that you need a different environment to deploy the application on each of the current stages. You use different environments to be able to perform versioning, different configurations, test bug fixes and so on. This also poses challenges on upgrading environments, changing shared configuration or keeping track of the servers. I will show you how to do it with Tomcat.

The easiest way to set up multiple Tomcat instances is to duplicate the entire Tomcat folder and change a few configurations. I don’t advise doing it this way, since it’s harder to maintain, harder to spin up new instances and harder to upgrade. Instead, we will set up the instances in a much more flexible way, by duplicating only a few things and keeping a shared base folder for all instances.

Installation

You need Tomcat of course. Download it here.

I’ve used version 7.0, but this should also work with other versions. I’m also doing the setup in a Unix like environment. This can also be accomplished in a Windows box, but the commands need to be adjusted.

Unzip the installation folder to a directory of your choice. I just recommend to do it in a parent folder and you can use a name like tomcat or server .

Now, instead of using the unzipped folder, we are going to create a link to it, like this:

ln -s apache-tomcat-7.0.64/ current

Here is a sample:

Tomcat Install radcortez:tomcat radcortez$ pwd /usr/local/share/java/tomcat radcortez:tomcat radcortez$ ln -s apache-tomcat-7.0.64/ current radcortez:tomcat radcortez$ ls total 8 drwxr-xr-x 4 radcortez admin 136B Sep 24 01:49 . drwxr-xr-x@ 26 radcortez admin 884B Sep 24 01:38 .. drwxr-xr-x 13 radcortez admin 442B Sep 24 01:45 apache-tomcat-7.0.64 lrwxr-xr-x 1 radcortez admin 21B Sep 24 01:49 current -> apache-tomcat-7.0.64/ radcortez:tomcat radcortez$ 1 2 3 4 5 6 7 8 9 10 radcortez : tomcat radcortez $ pwd / usr / local / share / java / tomcat radcortez : tomcat radcortez $ ln - s apache - tomcat - 7.0.64 / current radcortez : tomcat radcortez $ ls total 8 drwxr - xr - x 4 radcortez admin 136B Sep 24 01 : 49 . drwxr - xr - x @ 26 radcortez admin 884B Sep 24 01 : 38 . . drwxr - xr - x 13 radcortez admin 442B Sep 24 01 : 45 apache - tomcat - 7.0.64 lrwxr - xr - x 1 radcortez admin 21B Sep 24 01 : 49 current -> apache - tomcat - 7.0.64 / radcortez : tomcat radcortez $

Setup

To keep this simple, we are going to create two instances: development and production . But keep in mind that you can create as many as you want by making the necessary adjustments to the scripts.

Folders

Create a folder now named instances or environments . Inside, create a folder named development :

Tomcat Instances radcortez:tomcat radcortez$ mkdir instances radcortez:tomcat radcortez$ cd instances/ radcortez:instances radcortez$ mkdir development radcortez:instances radcortez$ ls total 0 drwxr-xr-x 4 radcortez admin 136B Sep 24 01:56 . drwxr-xr-x 5 radcortez admin 170B Sep 24 01:56 .. drwxr-xr-x 2 radcortez admin 68B Sep 24 01:56 development radcortez:instances radcortez$ pwd /usr/local/share/java/tomcat/instances radcortez:instances radcortez$ 1 2 3 4 5 6 7 8 9 10 11 radcortez : tomcat radcortez $ mkdir instances radcortez : tomcat radcortez $ cd instances / radcortez : instances radcortez $ mkdir development radcortez : instances radcortez $ ls total 0 drwxr - xr - x 4 radcortez admin 136B Sep 24 01 : 56 . drwxr - xr - x 5 radcortez admin 170B Sep 24 01 : 56 . . drwxr - xr - x 2 radcortez admin 68B Sep 24 01 : 56 development radcortez : instances radcortez $ pwd / usr / local / share / java / tomcat / instances radcortez : instances radcortez $

Now copy the folders conf , logs , temp , webapps and work from the Tomcat install folder into development and production :

Tomcat Instances Setup radcortez:instances radcortez$ ls total 0 drwxr-xr-x 4 radcortez admin 136B Sep 24 02:06 . drwxr-xr-x 5 radcortez admin 170B Sep 24 01:56 .. drwxr-xr-x 2 radcortez admin 68B Sep 24 02:06 development drwxr-xr-x 2 radcortez admin 68B Sep 24 02:06 production radcortez:instances radcortez$ cp -rf ../current/conf/ development/conf radcortez:instances radcortez$ cp -rf ../current/logs/ development/logs radcortez:instances radcortez$ cp -rf ../current/temp/ development/temp radcortez:instances radcortez$ cp -rf ../current/webapps/ development/webapps radcortez:instances radcortez$ cp -rf ../current/work/ development/work radcortez:instances radcortez$ ls development/ total 0 drwxr-xr-x 7 radcortez admin 238B Sep 24 02:08 . drwxr-xr-x 4 radcortez admin 136B Sep 24 02:06 .. drwxr-xr-x 9 radcortez admin 306B Sep 24 02:07 conf drwxr-xr-x 2 radcortez admin 68B Sep 24 02:07 logs drwxr-xr-x 3 radcortez admin 102B Sep 24 02:08 temp drwxr-xr-x 7 radcortez admin 238B Sep 24 02:08 webapps drwxr-xr-x 2 radcortez admin 68B Sep 24 02:08 work radcortez:instances radcortez$ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 radcortez : instances radcortez $ ls total 0 drwxr - xr - x 4 radcortez admin 136B Sep 24 02 : 06 . drwxr - xr - x 5 radcortez admin 170B Sep 24 01 : 56 . . drwxr - xr - x 2 radcortez admin 68B Sep 24 02 : 06 development drwxr - xr - x 2 radcortez admin 68B Sep 24 02 : 06 production radcortez : instances radcortez $ cp - rf . . / current / conf / development / conf radcortez : instances radcortez $ cp - rf . . / current / logs / development / logs radcortez : instances radcortez $ cp - rf . . / current / temp / development / temp radcortez : instances radcortez $ cp - rf . . / current / webapps / development / webapps radcortez : instances radcortez $ cp - rf . . / current / work / development / work radcortez : instances radcortez $ ls development / total 0 drwxr - xr - x 7 radcortez admin 238B Sep 24 02 : 08 . drwxr - xr - x 4 radcortez admin 136B Sep 24 02 : 06 . . drwxr - xr - x 9 radcortez admin 306B Sep 24 02 : 07 conf drwxr - xr - x 2 radcortez admin 68B Sep 24 02 : 07 logs drwxr - xr - x 3 radcortez admin 102B Sep 24 02 : 08 temp drwxr - xr - x 7 radcortez admin 238B Sep 24 02 : 08 webapps drwxr - xr - x 2 radcortez admin 68B Sep 24 02 : 08 work radcortez : instances radcortez $

If you wish, you can now remove these folders from the Tomcat install folder, but is not mandatory.

Home and Base

The idea here is to share the main Tomcat folders and each instance has a copy of their personal folders to not clash with each other. Tomcat defines two environment variables called CATALINA_HOME and CATALINA_BASE that allow us to do that.

Create a bin folder in the instances development . Add the following exec.sh script:

exec.sh #!/bin/bash TOMCAT_HOME="$(dirname $0)/.." cd $TOMCAT_HOME && TOMCAT_HOME=$PWD && cd - &> /dev/null export TOMCAT_HOME export CATALINA_HOME="$(readlink -f "$TOMCAT_HOME/../../current")" export CATALINA_BASE="$(readlink -f "$TOMCAT_HOME")" export CATALINA_OPTS="-Dhttp.port=8080 $CATALINA_OPTS" export CATALINA_OPTS="-Dhttps.port=8443 $CATALINA_OPTS" export CATALINA_OPTS="-Dajp.port=8009 $CATALINA_OPTS" export CATALINA_OPTS="-Dshutdown.port=8005 $CATALINA_OPTS" echo "JAVA_HOME set to $JAVA_HOME" echo "CATALINA_BASE set to $CATALINA_BASE" echo "CATALINA_HOME set to $CATALINA_HOME" $CATALINA_HOME/bin/"$(basename "$0")" "$@" 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #!/bin/bash TOMCAT_HOME = "$(dirname $0)/.." cd $TOMCAT_HOME && TOMCAT_HOME = $PWD && cd - & > / dev / null export TOMCAT_HOME export CATALINA_HOME = "$(readlink -f " $TOMCAT_HOME / . . / . . / current ")" export CATALINA_BASE = "$(readlink -f " $TOMCAT_HOME ")" export CATALINA_OPTS = "-Dhttp.port=8080 $CATALINA_OPTS" export CATALINA_OPTS = "-Dhttps.port=8443 $CATALINA_OPTS" export CATALINA_OPTS = "-Dajp.port=8009 $CATALINA_OPTS" export CATALINA_OPTS = "-Dshutdown.port=8005 $CATALINA_OPTS" echo "JAVA_HOME set to $JAVA_HOME" echo "CATALINA_BASE set to $CATALINA_BASE" echo "CATALINA_HOME set to $CATALINA_HOME" $CATALINA_HOME / bin / "$(basename " $ 0 ")" "$@"

Note: If you are using MacOSX, you might need to install core-utils using brew and replace readlink by greadlink to achieve the proper behaviour.

This script is going to set up the proper configuration variables to point to our shared Tomcat and the specific instance folders. Note the properties http.port , https.port , ajp.port and shutdown.port are included in the CATALINA_OPTS environment variable. With these we can pass specific configuration to the server.xml file. Tomcat is smart enough to perform property replace substitution as long as you have the proper placeholders in place.

Files

All these operations are performed in the development folder instance.

conf/server.xml

Edit the file conf/server.xml do the following changes;

Replace By 8080 ${http.port} 8443 ${https.port} 8009 ${ajp.port}

Note: Unfortunately the only place where property replacement doesn’t work is the shutdown port. I think this is a bug in Tomcat and should be fixed. So for now, we need to keep it hardcoded.

bin/exec.sh

On the bin folder, create links to exec.sh to the following files: catalina.sh , startup.sh , shutdown.sh .

Tomcat Bin Scripts radcortez:bin radcortez$ ln -s exec.sh catalina.sh radcortez:bin radcortez$ ln -s exec.sh startup.sh radcortez:bin radcortez$ ln -s exec.sh shutdown.sh radcortez:bin radcortez$ ls total 32 drwxr-xr-x 6 radcortez admin 204B Sep 24 10:14 . drwxr-xr-x 8 radcortez admin 272B Sep 24 08:07 .. lrwxr-xr-x 1 radcortez admin 7B Sep 24 10:13 catalina.sh -> exec.sh -rw-r--r-- 1 radcortez admin 651B Sep 24 09:32 exec.sh lrwxr-xr-x 1 radcortez admin 7B Sep 24 10:14 shutdown.sh -> exec.sh lrwxr-xr-x 1 radcortez admin 7B Sep 24 10:13 startup.sh -> exec.sh radcortez:bin radcortez$ 1 2 3 4 5 6 7 8 9 10 11 12 radcortez : bin radcortez $ ln - s exec .sh catalina .sh radcortez : bin radcortez $ ln - s exec .sh startup .sh radcortez : bin radcortez $ ln - s exec .sh shutdown .sh radcortez : bin radcortez $ ls total 32 drwxr - xr - x 6 radcortez admin 204B Sep 24 10 : 14 . drwxr - xr - x 8 radcortez admin 272B Sep 24 08 : 07 . . lrwxr - xr - x 1 radcortez admin 7B Sep 24 10 : 13 catalina .sh -> exec .sh - rw - r -- r -- 1 radcortez admin 651B Sep 24 09 : 32 exec .sh lrwxr - xr - x 1 radcortez admin 7B Sep 24 10 : 14 shutdown .sh -> exec .sh lrwxr - xr - x 1 radcortez admin 7B Sep 24 10 : 13 startup .sh -> exec .sh radcortez : bin radcortez $

This will allow you to call the original Tomcat, but by calling the exec.sh set up first. The magic is done by the line $CATALINA_HOME/bin/"$(basename "$0")" "$@" in the exec.sh script.

Run

The instance should be ready to be executed. Just run it as you would do it normally by executing sh catalina.sh run or sh startup.sh from the development instance folder.

Additional Instances

Just duplicate the development instance folder to a production one and edit the bin/exec.sh to update it with different ports. You can user 7080 for http, 7443 for https, 7009 for ajp and 7005 for the shutdown.

Since property replacement is not working properly for the shutdown port, we need to manually edit conf/server.xml from the production instance and replace 8005 by 7005. When this bug is fixed, and you actually use a property, you don’t have to worry about doing this.

Note: You might need to reestablish the proper links in the scripts catalina.sh , startup.sh and shutdown.sh stored in the bin folder.

After this, your second instance production is ready to run. If you need more, just repeat the last steps, making sure to pick ports that don’t conflict with the instances already set up.

Perks

With this set up you can now:

Create new instances easily with minimum changes. You can actually have one untouched unchanged instance, that you can use to copy from to create others.

Update the Tomcat version, just by installing a new distribution and updating the link to current .

. If you place jars in the libs folder of the HOME installation, they become instantly available to all instances.

folder of the installation, they become instantly available to all instances. Instead of duplicating the conf folder, you can actually link to the one in HOME and also share the configuration between all environments. Or just link to the files you want to share.

folder, you can actually link to the one in and also share the configuration between all environments. Or just link to the files you want to share. Remove an instance, by just deleting its BASE folder.

folder. Also works for TomEE!

Alternatives

If you don’t like this set up, you can try using Docker. Check the following post: Get Into Docker.

Let me know if this was useful to you or if you had any trouble following the blog instructions!