Tomcat Directory Layout

Introduction

A standalone Tomcat install is more than one folder with a startup script—it is a layout of roles: binaries, configuration, shared libraries, deployed apps, logs, and scratch space. Once you know which directory answers which question ("where do I deploy?", "where is the error?", "what must I back up?"), install, deploy, and troubleshooting become predictable. This chapter maps every important path for the official zip/tar.gz layout and notes how OS packages split the same ideas across different locations.

Prerequisites

  • Installing Tomcat — Tomcat running at least once on your machine
  • CATALINA_HOME set to your install root (official binary install)
  • Optional: list your tree with ls -la $CATALINA_HOME (Linux/macOS) or dir %CATALINA_HOME% (Windows)

Top-Level Tree

Official Apache distribution (Tomcat 10.1.x):

text
$CATALINA_HOME/
├── bin/
├── conf/
├── lib/
├── logs/
├── webapps/
├── work/              ← created at runtime
├── temp/              ← created at runtime
├── LICENSE
├── NOTICE
├── RELEASE-NOTES
└── RUNNING.txt

Code explanation:

  • Shipped with the download: bin, conf, lib, empty or sample webapps, docs at root
  • Created when Tomcat runs: work/, temp/, log files under logs/
  • Treat conf/ and your webapps/ content as data you own; treat bin/ and lib/ as product files you upgrade with new Tomcat versions

CATALINA_HOME vs CATALINA_BASE

VariableMeaningTypical value
CATALINA_HOMETomcat product install—binaries and default read-only bits/opt/tomcat or C:\apache-tomcat-10.1.34
CATALINA_BASEOne running instance—its conf, logs, webapps, work, tempSame as CATALINA_HOME for a single instance

Single-instance learning setup:

text
CATALINA_HOME = CATALINA_BASE = /opt/tomcat

Multi-instance (advanced—one download, several servers on different ports):

text
CATALINA_HOME = /opt/tomcat          ← shared bin/, lib/
CATALINA_BASE = /var/tomcat/instance1 ← own conf/, webapps/, logs/
CATALINA_BASE = /var/tomcat/instance2

Code explanation:

  • Scripts in bin/ read CATALINA_HOME for launchers and CATALINA_BASE for instance-specific paths
  • If CATALINA_BASE is unset, it defaults to CATALINA_HOME
  • Tomcat and Docker / Multi-Instance expands multi-base layouts

Directory Reference

bin/ — launchers and tools

FileRole
startup.sh / startup.batStart Tomcat (background on Unix)
shutdown.sh / shutdown.batGraceful stop via shutdown port
catalina.sh / catalina.batMain launcher; run = foreground
version.sh / version.batPrint Tomcat and JVM versions
setclasspath.shInternal—sets Java classpath for Tomcat
digest.shHash passwords for tomcat-users.xml

Code explanation:

  • Prefer catalina.sh run when learning—you see stderr immediately
  • service.bat (Windows) can register Tomcat as a Windows service—see Start, Stop, and Environment

Do not store your application source here. bin/ is replaced on Tomcat upgrades.

conf/ — configuration you edit

FileRole
server.xmlConnectors (ports), Engine, Host, global container settings
web.xmlDefault deployment descriptor for all web apps (global defaults)
context.xmlDefault Context settings for every deployed app
tomcat-users.xmlUsers and roles for Manager / Host Manager apps
logging.propertiesjava.util.logging levels and handlers
catalina.propertiesClassloader rules, package access
jaspic-providers.xmlJASPIC (uncommon in beginner apps)

Per-application overrides can also appear under:

text
conf/Catalina/localhost/
└── myapp.xml          ← Context fragment for app "myapp"

Code explanation:

  • server.xml — "Which port? Which host? Where is appBase?"
  • WEB-INF/web.xml inside a WAR — app-specific servlets and filters (covered in web.xml and context)
  • Always backup before editing:
bash
cp conf/server.xml conf/server.xml.bak

lib/ — container-wide JARs

Shared libraries visible to Tomcat itself and every web application (unless overridden by classloader policy).

Examples of what belongs here:

  • JDBC drivers used via JNDI DataSource in context.xml
  • Tomcat-specific plugins you add deliberately

Examples of what does not belong here:

  • Your application's business JARs → put in WEB-INF/lib inside the WAR
  • Random version experiments that conflict with app dependencies

Code explanation:

  • Putting JARs in lib/ affects all apps—use sparingly
  • Most Spring Boot / Maven WARs ship dependencies inside the WAR, not Tomcat lib/

logs/ — first stop when something breaks

Log (typical name)Contents
catalina.yyyy-mm-dd.logMain container lifecycle, startup errors
catalina.outSome installs redirect stdout here (especially when started manually)
localhost.yyyy-mm-dd.logApplication-level messages, stack traces from web apps
localhost_access_log.yyyy-mm-dd.txtHTTP access log (only if AccessLogValve enabled)
manager.yyyy-mm-dd.logManager application activity

Quick tail after a failed deploy:

bash
tail -80 logs/catalina.$(date +%Y-%m-%d).log
tail -80 logs/localhost.$(date +%Y-%m-%d).log

Windows: open the newest dated files in logs\.

Logging and AccessLogValve covers formats and configuration.

webapps/ — deployed applications

Default appBase for the localhost Host in server.xml. Each child becomes a Context:

text
webapps/
├── ROOT/                 → context path /
├── docs/                 → /docs  (sample documentation)
├── examples/             → /examples
├── host-manager/         → /host-manager
├── manager/              → /manager
└── myapp.war             → /myapp  (after auto-deploy)
EntryResult
foo.warDeployed as context /foo (WAR name without extension)
foo/ directoryExploded deploy, context /foo
ROOT.war or ROOT/Application at http://host:8080/

Code explanation:

  • Tomcat can auto-deploy new/changed WARs when autoDeploy="true" on the Host
  • Production often uses external docBase outside webapps/—see Deploying WAR and webapps
  • Do not edit files inside an exploded WAR in place for long-term workflow—build with Maven and redeploy the WAR

work/ — compiled JSP and scratch

Tomcat writes generated Java and class files for JSP pages here, organized by engine/host/context.

text
work/Catalina/localhost/myapp/org/apache/jsp/...

Code explanation:

  • Safe to delete when Tomcat is stopped if JSP pages behave oddly—Tomcat regenerates on next request
  • Large work/ usually means many JSPs or long uptime—not a place for manual "fixes"
  • If you use no JSP (common with Spring MVC / REST), work/ stays relatively small

temp/ — JVM and Tomcat temporary files

File uploads, temporary buffers, and internal Tomcat temp data. Tomcat may clean some files on restart; occasional manual cleanup during maintenance is fine when the server is stopped.

Inside a Deployed Web Application

Whether exploded under webapps/myapp/ or inside myapp.war:

text
myapp/
├── META-INF/
│   └── context.xml       ← optional per-app Context settings
├── WEB-INF/
│   ├── web.xml           ← optional with Servlet 3+ annotations
│   ├── classes/          ← your .class files (com/example/...)
│   └── lib/              ← application JAR dependencies
├── index.html            ← static assets at /myapp/index.html
└── css/, js/, ...

URL mapping recap from What Is Tomcat:

text
http://localhost:8080/myapp/api/users
                      │      └── servlet / controller path
                      └── context path (from WAR name or Context config)

Environment Variables

VariableRequired?Purpose
JAVA_HOMEYesJDK root Tomcat uses to start the JVM
JRE_HOMEAlternative to JAVA_HOME on some scriptsJRE path (JDK preferred for development)
CATALINA_HOMEStrongly recommendedTomcat product directory
CATALINA_BASEOptionalInstance directory; defaults to CATALINA_HOME
CATALINA_OPTSOptionalJVM flags (heap, GC)—e.g. -Xms512m -Xmx512m
JAVA_OPTSOptionalJVM flags for all Java processes in scripts—prefer CATALINA_OPTS for Tomcat-only

Example Linux ~/.bashrc snippet:

bash
export JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64
export CATALINA_HOME=/opt/tomcat
export CATALINA_OPTS="-Xms256m -Xmx512m"

Code explanation:

  • Scripts in bin/ fail fast if JAVA_HOME is missing or invalid
  • Setting CATALINA_HOME avoids cd into bin before every command

Details and systemd integration: Start, Stop, and Environment.

Official Zip vs OS Package Layout

Official zip/tar.gz (Hello Code default)

Everything lives under one tree you control:

text
/opt/tomcat/
├── bin conf lib logs webapps work temp

Debian/Ubuntu tomcat10 package (illustrative)

RolePackage path
Binaries / shared libs/usr/share/tomcat10/
Config/etc/tomcat10/
Web applications/var/lib/tomcat10/webapps/
Logs/var/log/tomcat10/
CATALINA_BASEOften /var/lib/tomcat10/

Code explanation:

  • CATALINA_HOME may point at /usr/share/tomcat10 while webapps/ is under /var/lib/tomcat10
  • When a tutorial says "copy WAR to webapps/", map that to /var/lib/tomcat10/webapps/ for APT installs
  • Upgrades via apt may preserve /etc/tomcat10 and /var/lib/tomcat10 separately from binaries

Always confirm on your system:

bash
systemctl status tomcat10
dpkg -L tomcat10 | head -20

What to Back Up vs What to Reinstall

Back up / version-controlReinstall from Apache download
conf/ (or /etc/tomcat10/)bin/
Your WARs and external docBase dirslib/ (unless you added JDBC drivers—note them)
Custom scripts outside TomcatDefault empty webapps samples
Documented CATALINA_OPTSwork/, temp/ (regenerated)

Logs: archive for audits; do not rely on logs/ as the only backup of application data.

Development Habits

DoAvoid
Build WAR with Maven/Gradle, copy to webapps/Edit .class files by hand under webapps/
Backup conf/ before changing ports or realmsRun Tomcat as root on Linux
Check logs/ after every deployPut duplicate JDBC drivers in both lib/ and WEB-INF/lib without reason
Use catalina.sh run while learning startup errorsCommit Tomcat logs/ or work/ to Git

Tip

Separate Source Tree from Tomcat

Keep project source in ~/projects/myapp/; treat $CATALINA_HOME/webapps/ as a deploy target only—same idea as copying dist/ to Nginx root, not editing files in /var/www as your IDE project.

Standalone Tomcat vs Spring Boot Executable JAR

AspectStandalone TomcatSpring Boot fat JAR
Processstartup.sh → shared Tomcatjava -jar app.jar
Deploy unitWAR in webapps/Embedded server inside JAR
Configconf/server.xml, env varsapplication.yml, Boot auto-config
Multiple appsOne Tomcat, many WARsOne JAR per app (typical)
Ops focusContainer dirs, connectors, shared logsJVM flags, Actuator, cloud deploy

Spring Boot can produce a WAR for external Tomcat—same webapps/ deploy model as plain Servlet apps. See Spring Boot WAR on Tomcat.

How Directories Relate to server.xml (Preview)

You will edit conf/server.xml in depth later; directory layout and this file connect like so:

text
server.xml
└── Service
    └── Connector port="8080"
    └── Engine
        └── Host name="localhost" appBase="webapps"
                └── Context (each app under webapps/ or conf/Catalina/localhost/*.xml)
  • appBase="webapps" — default deploy directory
  • Connector port — must match what you curl (8080 by default)
  • Shutdown port 8005shutdown.sh sends SHUTDOWN string to this port

Full Connector and Host discussion: server.xml and Connectors.

Exploration Commands

From $CATALINA_HOME:

bash
# List top-level dirs
ls -la
 
# Show which Java Tomcat will use
./bin/catalina.sh version
 
# See deployed apps (official install)
ls -la webapps/
 
# Find today's main log
ls -lt logs/ | head

Windows PowerShell:

powershell
Get-ChildItem $env:CATALINA_HOME
& "$env:CATALINA_HOME\bin\catalina.bat" version
Get-ChildItem "$env:CATALINA_HOME\webapps"

Post-Chapter Checklist

  • You can explain CATALINA_HOME vs CATALINA_BASE
  • You know where to deploy (webapps/ or package equivalent)
  • You know where to read errors (logs/)
  • You will not put app JARs in lib/ unless you mean container-wide sharing
  • You backed up conf/server.xml before any edit

FAQ

Can I delete docs/ and examples/ under webapps?

Yes for learning and recommended before production. Removes sample apps at /docs and /examples. Keep manager only if you use it—and secure it.

Why is work/ huge?

Many JSPs, long uptime, or many redeploys. Stop Tomcat, delete work/, start again.

Should JDBC drivers go in lib/ or WEB-INF/lib?

JNDI DataSource in Tomcat context.xml → often lib/. Driver only for one Spring Boot WAR → WEB-INF/lib inside that WAR.

Is webapps/ the only deploy location?

No. conf/Catalina/localhost/app.xml can point docBase anywhere on disk—preferred for controlled production deploys.

Git: which Tomcat paths to ignore?

Ignore entire Tomcat install or at least logs/, work/, temp/, and exploded WARs. Version-control your project source and deployment scripts—not the container runtime tree.

Package install: where is server.xml?

Often /etc/tomcat10/server.xml with symlinks or overrides—edit the file your package documents, not a random copy under /usr/share.

Same layout for Tomcat 9?

Yes at a high level. Servlet API packages differ (javax.* vs jakarta.*); directory names are the same for official Apache distributions.