Start, Stop, and Environment
Introduction
Installing Tomcat is only half the job—you need reliable ways to start, watch, stop, and restart the JVM process, with the right environment variables set every time. This chapter covers startup / shutdown, foreground catalina run, JVM options via CATALINA_OPTS, Linux systemd units, Windows service basics, and how to fix port conflicts when something else already listens on 8080.
Prerequisites
- Installing Tomcat — Tomcat extracted and JDK 17+ available
- Tomcat Directory Layout — you know
bin/,conf/,logs/ - Optional: Processes and systemd for Linux service management
How Tomcat Starts (Mental Model)
Code explanation:
- Tomcat is a Java main class launched by shell/batch scripts—you are not running a separate native binary like Nginx
conf/server.xmldefines HTTP port 8080 and shutdown port 8005 by default- Failures almost always appear in
logs/catalina.*.logor on the console when usingrun
Basic Start and Stop (Linux / macOS)
Assume:
export CATALINA_HOME=/opt/tomcat
export JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64 # adjust pathBackground start
$CATALINA_HOME/bin/startup.sh
curl -I http://127.0.0.1:8080/Code explanation:
startup.shcallscatalina.sh start—Tomcat detaches and logs tologs/- On macOS/Linux you may see
catalina.outinlogs/depending on version and redirect settings
Graceful stop
$CATALINA_HOME/bin/shutdown.shCode explanation:
shutdown.shopens the shutdown port (default 8005) and sends the stringSHUTDOWN- Tomcat stops accepting new requests and winds down threads—prefer this over
kill -9
Verify it stopped:
curl -I http://127.0.0.1:8080/ # should fail to connect
ss -tlnp | grep 8080 # Linux: no listenerForeground Run (Best for Learning)
cd $CATALINA_HOME
./bin/catalina.sh runCode explanation:
runkeeps Tomcat in the foreground—logs stream to your terminal- Ctrl+C sends a shutdown signal—similar to stopping a Spring Boot app from IDEA
- Use this when deploy fails and you want immediate stack traces
Stop from another terminal while run is active:
$CATALINA_HOME/bin/shutdown.shTip
Develop with run, Deploy with startup
Local debugging: catalina.sh run. Long-running laptop session or after you trust config: startup.sh.
Windows Start and Stop
CMD or PowerShell:
set CATALINA_HOME=C:\apache-tomcat-10.1.34
set JAVA_HOME=C:\Program Files\Eclipse Adoptium\jdk-21.0.x-hotspot
cd %CATALINA_HOME%\bin
startup.batForeground:
catalina.bat runStop:
shutdown.batCode explanation:
startup.batoften opens a second console window for Tomcat logs- Set
JAVA_HOMEandCATALINA_HOMEin System Environment Variables so every new CMD window inherits them
Windows service (overview)
Tomcat ships service.bat in bin/ to register a Windows service (requires Administrator):
cd %CATALINA_HOME%\bin
service.bat install Tomcat10Then use Services (services.msc) or:
sc start Tomcat10
sc stop Tomcat10Uninstall:
service.bat uninstall Tomcat10Details vary by Tomcat minor version—run service.bat with no args for usage text on your install.
Environment Variables in Depth
| Variable | Scope | Typical use |
|---|---|---|
JAVA_HOME | Required | JDK root—scripts locate java |
JRE_HOME | Alternative | Rare on dev machines; JDK is preferred |
CATALINA_HOME | Strongly recommended | Tomcat product directory |
CATALINA_BASE | Optional | Instance dir; defaults to CATALINA_HOME |
CATALINA_OPTS | Tomcat JVM only | Heap, GC, debug flags for this Tomcat process |
JAVA_OPTS | All Java in script | Shared flags—can affect child tools; prefer CATALINA_OPTS for Tomcat tuning |
Example: heap size and encoding
Linux/macOS—export before start:
export CATALINA_OPTS="-Xms256m -Xmx512m -Dfile.encoding=UTF-8"
$CATALINA_HOME/bin/startup.shWindows CMD:
set CATALINA_OPTS=-Xms256m -Xmx512m -Dfile.encoding=UTF-8
startup.batCode explanation:
-Xms/-Xmx— initial and maximum heap; start small when learning-Dfile.encoding=UTF-8— reduces garbled logs or request handling on some locales- Do not set enormous heaps on a laptop—512m–1g is enough for tutorial WARs
Persisting variables
| Platform | Where |
|---|---|
| Linux (single user) | ~/.bashrc, ~/.profile |
| Linux (system service) | systemd Environment= or EnvironmentFile= |
| macOS | ~/.zshrc |
| Windows | System Properties → Environment Variables |
After editing shell profiles, open a new terminal before starting Tomcat.
catalina.sh and your exports
Scripts honor variables already in the environment. You can also create bin/setenv.sh (Linux/macOS) or bin/setenv.bat (Windows)—Tomcat sources them automatically if present:
# $CATALINA_HOME/bin/setenv.sh
export CATALINA_OPTS="-Xms256m -Xmx512m -Dfile.encoding=UTF-8"Code explanation:
setenv.shkeeps JVM flags with the Tomcat install instead of scattered shell profiles- File must be executable on Unix:
chmod +x bin/setenv.sh
Graceful Shutdown vs Force Kill
| Method | Command | When |
|---|---|---|
| Graceful | shutdown.sh / shutdown.bat | Default—always try first |
| SIGTERM | kill <pid> | If shutdown port broken but process healthy |
| SIGKILL | kill -9 <pid> | Last resort—may corrupt in-flight work |
Find Tomcat PID on Linux:
ss -tlnp | grep 8080
pgrep -f catalina
ps aux | grep '[o]rg.apache.catalina'Windows:
netstat -ano | findstr :8080
tasklist | findstr javaWarning
Avoid kill -9 in Production
Forced kill skips servlet destroy() hooks and may leave lock files or half-written sessions. Fix shutdown.sh failures by checking port 8005 and logs/ first.
Shutdown port (8005)
In conf/server.xml (default):
<Server port="8005" shutdown="SHUTDOWN">Code explanation:
- Only localhost should reach this port—firewall it on shared servers
- If 8005 is blocked or changed without updating scripts,
shutdown.shhangs - Changing the shutdown string from
SHUTDOWNis a hardening option for advanced setups
Port 8080 Already in Use
Symptom in logs:
java.net.BindException: Address already in useFind the conflict
Linux:
ss -tlnp | grep 8080
sudo lsof -i :8080Windows:
netstat -ano | findstr :8080Common culprits: another Tomcat, Spring Boot on 8080, IDE-run app, Docker container.
Option A: stop the other process
Stop Spring Boot in IDEA, run shutdown.sh on the other Tomcat, or docker stop ….
Option B: change Tomcat HTTP port
Edit conf/server.xml (backup first):
<Connector port="8081" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />Restart Tomcat, then:
curl -I http://127.0.0.1:8081/Full Connector discussion: server.xml and Connectors.
Linux systemd Service (Official tar.gz Install)
Production servers should not depend on manual startup.sh after SSH logout. Create a unit file.
Option 1: Type=simple with foreground run (recommended)
/etc/systemd/system/tomcat.service:
Enable and control:
sudo systemctl daemon-reload
sudo systemctl enable tomcat
sudo systemctl start tomcat
sudo systemctl status tomcat
sudo journalctl -u tomcat -fCode explanation:
Type=simple— systemd considers the service started whencatalina.sh runexecs; logs go to journal andlogs/User=tomcat— run as non-root; ensure/opt/tomcatownership matchesExecStop— calls gracefulshutdown.sh
Option 2: Type=forking with startup.sh
Some teams use Type=forking because startup.sh backgrounds the JVM. That requires a correct PIDFile= and Tomcat version alignment—easy to misconfigure. Prefer Option 1 unless your runbook already standardizes on forking.
EnvironmentFile for secrets
EnvironmentFile=/etc/tomcat/tomcat.env/etc/tomcat/tomcat.env:
JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64
CATALINA_HOME=/opt/tomcat
CATALINA_OPTS=-Xms256m -Xmx512mRestrict permissions: chmod 640 /etc/tomcat/tomcat.env.
See also Linux developer workflow for Environment= patterns with Java apps.
OS Package Tomcat (systemctl)
If you installed tomcat10 via APT:
sudo systemctl start tomcat10
sudo systemctl stop tomcat10
sudo systemctl restart tomcat10
sudo systemctl status tomcat10Code explanation:
- Service name and paths are distro-defined—not
/opt/tomcat/bin/startup.sh - Config may live under
/etc/tomcat10/; logs under/var/log/tomcat10/ - Mixing package Tomcat with manual
startup.shon the same port causes conflicts—pick one method
Restart Workflow After Config Change
Many conf/ changes require a restart (not hot-reload):
$CATALINA_HOME/bin/shutdown.sh
# wait until port 8080 is free
$CATALINA_HOME/bin/startup.shOr with systemd:
sudo systemctl restart tomcatCode explanation:
server.xmlConnector changes need restart- Some
context.xml/web.xmlchanges trigger redeploy depending on settings—do not rely on that in production; restart during maintenance windows
Health Check Habit
After every start:
./bin/catalina.sh version
curl -I http://127.0.0.1:8080/
tail -20 logs/catalina.$(date +%Y-%m-%d).log| Check | Pass criteria |
|---|---|
| Version script | Shows Tomcat 10.1.x and expected Java |
| HTTP | HTTP/1.1 200 or redirect from ROOT app |
| Log tail | No BindException, no OutOfMemoryError |
Common Problems
| Symptom | Likely cause | Fix |
|---|---|---|
Neither JAVA_HOME nor JRE_HOME | Env not set in this shell/service | Set JAVA_HOME; restart terminal or systemd |
| Shutdown hangs | Wrong shutdown port; hung threads | Check 8005; use catalina.sh run to see thread dump; last resort kill then inspect logs |
Tomcat starts, 404 on / | ROOT app removed | Deploy app or open /docs if samples remain |
| systemd start fails immediately | Wrong paths, permission | journalctl -u tomcat -n 50; verify User= owns logs/ and webapps/ |
| Two Tomcats after reboot | Manual startup + systemd both enabled | Disable one; only one process per port |
Post-Chapter Checklist
- Start with
startup.shand stop withshutdown.sh - Run once with
catalina.sh runand read console output - Set
JAVA_HOME,CATALINA_HOME, and optionalCATALINA_OPTS - Know how to find what uses port 8080
- (Linux) Created or understood a systemd unit for persistent Tomcat
What Comes Next
| Chapter | Topic |
|---|---|
| 05 — server.xml and Connectors | Ports, Engine, Host, Connector tuning |
| 07 — Deploying WAR | Put applications in webapps/ |
| 20 — Troubleshooting | Extended diagnostic playbook |
FAQ
startup.sh vs catalina.sh start?
They are the same path—startup.sh is a thin wrapper that invokes catalina.sh start.
Do I need to export CATALINA_HOME every time?
If you use setenv.sh and full paths in systemd, no. Interactive shells still benefit from CATALINA_HOME in your profile.
Can Tomcat reload without full restart?
Some app redeploys happen with autoDeploy, but Connector and JVM flag changes need a full restart. When in doubt, restart.
Tomcat vs Spring Boot on the same machine?
Both default to 8080—run only one listener, or change one port. Stop Boot in IDEA before starting standalone Tomcat.
Why run as a non-root tomcat user?
Principle of least privilege—compromised web apps should not own the whole server. See Tomcat Security Basics.
WSL2: startup from Windows or WSL?
Start Tomcat inside WSL with Linux scripts; browse at http://127.0.0.1:8080/ from Windows browsers.
journalctl empty but Tomcat works?
Logs may only go to $CATALINA_HOME/logs/ depending on unit setup. Check both journalctl -u tomcat and logs/catalina*.log.