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_HOMEset to your install root (official binary install)- Optional: list your tree with
ls -la $CATALINA_HOME(Linux/macOS) ordir %CATALINA_HOME%(Windows)
Top-Level Tree
Official Apache distribution (Tomcat 10.1.x):
$CATALINA_HOME/
├── bin/
├── conf/
├── lib/
├── logs/
├── webapps/
├── work/ ← created at runtime
├── temp/ ← created at runtime
├── LICENSE
├── NOTICE
├── RELEASE-NOTES
└── RUNNING.txtCode explanation:
- Shipped with the download:
bin,conf,lib, empty or samplewebapps, docs at root - Created when Tomcat runs:
work/,temp/, log files underlogs/ - Treat
conf/and yourwebapps/content as data you own; treatbin/andlib/as product files you upgrade with new Tomcat versions
CATALINA_HOME vs CATALINA_BASE
| Variable | Meaning | Typical value |
|---|---|---|
CATALINA_HOME | Tomcat product install—binaries and default read-only bits | /opt/tomcat or C:\apache-tomcat-10.1.34 |
CATALINA_BASE | One running instance—its conf, logs, webapps, work, temp | Same as CATALINA_HOME for a single instance |
Single-instance learning setup:
CATALINA_HOME = CATALINA_BASE = /opt/tomcatMulti-instance (advanced—one download, several servers on different ports):
CATALINA_HOME = /opt/tomcat ← shared bin/, lib/
CATALINA_BASE = /var/tomcat/instance1 ← own conf/, webapps/, logs/
CATALINA_BASE = /var/tomcat/instance2Code explanation:
- Scripts in
bin/readCATALINA_HOMEfor launchers andCATALINA_BASEfor instance-specific paths - If
CATALINA_BASEis unset, it defaults toCATALINA_HOME - Tomcat and Docker / Multi-Instance expands multi-base layouts
Directory Reference
bin/ — launchers and tools
| File | Role |
|---|---|
startup.sh / startup.bat | Start Tomcat (background on Unix) |
shutdown.sh / shutdown.bat | Graceful stop via shutdown port |
catalina.sh / catalina.bat | Main launcher; run = foreground |
version.sh / version.bat | Print Tomcat and JVM versions |
setclasspath.sh | Internal—sets Java classpath for Tomcat |
digest.sh | Hash passwords for tomcat-users.xml |
Code explanation:
- Prefer
catalina.sh runwhen 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
| File | Role |
|---|---|
server.xml | Connectors (ports), Engine, Host, global container settings |
web.xml | Default deployment descriptor for all web apps (global defaults) |
context.xml | Default Context settings for every deployed app |
tomcat-users.xml | Users and roles for Manager / Host Manager apps |
logging.properties | java.util.logging levels and handlers |
catalina.properties | Classloader rules, package access |
jaspic-providers.xml | JASPIC (uncommon in beginner apps) |
Per-application overrides can also appear under:
conf/Catalina/localhost/
└── myapp.xml ← Context fragment for app "myapp"Code explanation:
server.xml— "Which port? Which host? Where isappBase?"WEB-INF/web.xmlinside a WAR — app-specific servlets and filters (covered in web.xml and context)- Always backup before editing:
cp conf/server.xml conf/server.xml.baklib/ — 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/libinside 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.log | Main container lifecycle, startup errors |
catalina.out | Some installs redirect stdout here (especially when started manually) |
localhost.yyyy-mm-dd.log | Application-level messages, stack traces from web apps |
localhost_access_log.yyyy-mm-dd.txt | HTTP access log (only if AccessLogValve enabled) |
manager.yyyy-mm-dd.log | Manager application activity |
Quick tail after a failed deploy:
tail -80 logs/catalina.$(date +%Y-%m-%d).log
tail -80 logs/localhost.$(date +%Y-%m-%d).logWindows: 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:
webapps/
├── ROOT/ → context path /
├── docs/ → /docs (sample documentation)
├── examples/ → /examples
├── host-manager/ → /host-manager
├── manager/ → /manager
└── myapp.war → /myapp (after auto-deploy)| Entry | Result |
|---|---|
foo.war | Deployed as context /foo (WAR name without extension) |
foo/ directory | Exploded 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
docBaseoutsidewebapps/—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.
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:
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:
http://localhost:8080/myapp/api/users
│ └── servlet / controller path
└── context path (from WAR name or Context config)Environment Variables
| Variable | Required? | Purpose |
|---|---|---|
JAVA_HOME | Yes | JDK root Tomcat uses to start the JVM |
JRE_HOME | Alternative to JAVA_HOME on some scripts | JRE path (JDK preferred for development) |
CATALINA_HOME | Strongly recommended | Tomcat product directory |
CATALINA_BASE | Optional | Instance directory; defaults to CATALINA_HOME |
CATALINA_OPTS | Optional | JVM flags (heap, GC)—e.g. -Xms512m -Xmx512m |
JAVA_OPTS | Optional | JVM flags for all Java processes in scripts—prefer CATALINA_OPTS for Tomcat-only |
Example Linux ~/.bashrc snippet:
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 ifJAVA_HOMEis missing or invalid - Setting
CATALINA_HOMEavoidscdintobinbefore 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:
/opt/tomcat/
├── bin conf lib logs webapps work tempDebian/Ubuntu tomcat10 package (illustrative)
| Role | Package path |
|---|---|
| Binaries / shared libs | /usr/share/tomcat10/ |
| Config | /etc/tomcat10/ |
| Web applications | /var/lib/tomcat10/webapps/ |
| Logs | /var/log/tomcat10/ |
CATALINA_BASE | Often /var/lib/tomcat10/ |
Code explanation:
CATALINA_HOMEmay point at/usr/share/tomcat10whilewebapps/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
aptmay preserve/etc/tomcat10and/var/lib/tomcat10separately from binaries
Always confirm on your system:
systemctl status tomcat10
dpkg -L tomcat10 | head -20What to Back Up vs What to Reinstall
| Back up / version-control | Reinstall from Apache download |
|---|---|
conf/ (or /etc/tomcat10/) | bin/ |
Your WARs and external docBase dirs | lib/ (unless you added JDBC drivers—note them) |
| Custom scripts outside Tomcat | Default empty webapps samples |
Documented CATALINA_OPTS | work/, temp/ (regenerated) |
Logs: archive for audits; do not rely on logs/ as the only backup of application data.
Development Habits
| Do | Avoid |
|---|---|
Build WAR with Maven/Gradle, copy to webapps/ | Edit .class files by hand under webapps/ |
Backup conf/ before changing ports or realms | Run Tomcat as root on Linux |
Check logs/ after every deploy | Put duplicate JDBC drivers in both lib/ and WEB-INF/lib without reason |
Use catalina.sh run while learning startup errors | Commit 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
| Aspect | Standalone Tomcat | Spring Boot fat JAR |
|---|---|---|
| Process | startup.sh → shared Tomcat | java -jar app.jar |
| Deploy unit | WAR in webapps/ | Embedded server inside JAR |
| Config | conf/server.xml, env vars | application.yml, Boot auto-config |
| Multiple apps | One Tomcat, many WARs | One JAR per app (typical) |
| Ops focus | Container dirs, connectors, shared logs | JVM 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:
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 (8080by default) - Shutdown port
8005—shutdown.shsends SHUTDOWN string to this port
Full Connector and Host discussion: server.xml and Connectors.
Exploration Commands
From $CATALINA_HOME:
# 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/ | headWindows 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_HOMEvsCATALINA_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.xmlbefore 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.