Project: External docBase Deploy

Introduction

Dropping WARs into $CATALINA_HOME/webapps/ mixes application artifacts with the Tomcat install. This project stores WARs under /opt/apps/ and registers a Context fragment in conf/Catalina/localhost/—a production-friendly pattern from Deploying WAR.

Prerequisites

Project Goals

  • WAR lives at /opt/apps/blog-api.war
  • Context /blog-api via blog-api.xml
  • Upgrade WAR without touching Tomcat webapps/
  • Tomcat webapps/ stays empty of this app

Step 1: Prepare App Directory

bash
sudo mkdir -p /opt/apps
sudo cp target/blog-api.war /opt/apps/blog-api.war
sudo chown tomcat:tomcat /opt/apps/blog-api.war

If learning as your own user (no tomcat user yet), adjust ownership to the user running Tomcat.

Step 2: Remove webapps Copy (If Any)

bash
export CATALINA_HOME=/opt/tomcat
$CATALINA_HOME/bin/shutdown.sh
rm -rf $CATALINA_HOME/webapps/blog-api $CATALINA_HOME/webapps/blog-api.war

Step 3: Context Fragment

Create $CATALINA_HOME/conf/Catalina/localhost/blog-api.xml:

xml
<?xml version="1.0" encoding="UTF-8"?>
<Context docBase="/opt/apps/blog-api.war"
         reloadable="false"
         unpackWARs="true" />

Code explanation:

  • File name blog-api.xml → context path /blog-api
  • docBase — absolute path to WAR outside Tomcat home
  • reloadable="false" — production-safe

Backup:

bash
cp conf/Catalina/localhost/blog-api.xml conf/Catalina/localhost/blog-api.xml.bak

Step 4: Start and Verify

bash
$CATALINA_HOME/bin/startup.sh
curl -s http://127.0.0.1:8080/blog-api/posts
ls -la $CATALINA_HOME/webapps/    # should NOT contain blog-api
ls -la /opt/apps/

Pass criteria:

  • API responds
  • webapps/ has no blog-api folder (Tomcat may unpack to work/ or temp under webapps depending on version—key is docBase drives deploy)

Check log:

bash
grep -i "blog-api" $CATALINA_HOME/logs/catalina.$(date +%Y-%m-%d).log

Step 5: Upgrade WAR Only

Rebuild and replace artifact:

bash
mvn clean package
sudo cp target/blog-api.war /opt/apps/blog-api.war
sudo chown tomcat:tomcat /opt/apps/blog-api.war
$CATALINA_HOME/bin/shutdown.sh
$CATALINA_HOME/bin/startup.sh
curl -s http://127.0.0.1:8080/blog-api/posts

Tomcat install tree unchanged except conf/Catalina/localhost/blog-api.xml.

Optional: ROOT Context

Serve at / with conf/Catalina/localhost/ROOT.xml:

xml
<Context docBase="/opt/apps/blog-api.war" reloadable="false" />

URL becomes http://127.0.0.1:8080/posts—only one ROOT app allowed.

Acceptance Checklist

  • docBase points to /opt/apps/blog-api.war
  • Context fragment file name matches URL prefix
  • Upgrade replaces /opt/apps/ WAR only
  • Permissions allow Tomcat user to read WAR

Troubleshooting

IssueFix
IllegalArgumentException: docBasePath typo; WAR missing
404Wrong XML file name vs expected context
Permission deniedchown tomcat:tomcat /opt/apps/blog-api.war

Next

Project: Nginx + Tomcat