Restarting Apache Tomcat on Linux — Stop, Start, and Troubleshooting
On this page (17sections)
Use this guide when you need a clean Tomcat restart on a Linux server — after a deployment, config change, or when the instance is hung. It covers the approaches used today: systemd (preferred on most servers), catalina scripts, and safe process cleanup when graceful shutdown fails.
Version note: Examples assume Apache Tomcat 10.1 or 11 on Linux. Tomcat 10+ uses Jakarta EE (
jakarta.*packages). Tomcat 11 requires Java 17+; Tomcat 10.1 requires Java 11+. Adjust paths if your install uses a different layout.
Before you restart
- Notify users if this is production — brief downtime or failed sessions may occur.
- Note
CATALINA_HOME— usually/opt/tomcat,/usr/share/tomcat10, or a custom path like/home/app/apache-tomcat-10.1.x. - Confirm the run user — Tomcat should run as a dedicated non-root user (e.g.
tomcat), notroot. - Back up
conf/server.xml, deployed WARs, and any custom context configs if you changed them recently.
Set environment variables if your install expects them:
export CATALINA_HOME=/opt/tomcat
export CATALINA_BASE=/opt/tomcat # often same as CATALINA_HOME
export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64
Method 1 — systemd (recommended)
Most Linux packages register Tomcat as a systemd service.
# Check service name (varies by distro)
systemctl list-units --type=service | grep -i tomcat
# Graceful restart
sudo systemctl restart tomcat
# or: tomcat10, tomcat11, tomcat.service
# Stop and start separately
sudo systemctl stop tomcat
sudo systemctl start tomcat
# Status and recent logs
sudo systemctl status tomcat
sudo journalctl -u tomcat -n 100 --no-pager
Enable on boot (if not already):
sudo systemctl enable tomcat
This is the cleanest approach: systemd sends the correct stop signal, tracks PID, and logs to the journal.
Method 2 — catalina.sh scripts
When Tomcat is installed manually (tarball), use the scripts in $CATALINA_HOME/bin:
cd "$CATALINA_HOME/bin"
# Graceful stop (preferred)
./catalina.sh stop
# Wait a few seconds, then start
./catalina.sh start
# Foreground run (debugging only — use a separate terminal)
./catalina.sh run
Legacy wrappers still work on many installs:
./shutdown.sh # calls catalina stop
./startup.sh # calls catalina start (detached)
Do not run startup.sh as root in production. Use the tomcat service user:
sudo -u tomcat "$CATALINA_HOME/bin/catalina.sh" start
Method 3 — When Tomcat will not stop
If systemctl stop or catalina.sh stop hangs, find and terminate the process carefully.
1. Find the Tomcat process
Prefer matching catalina over a broad java grep:
ps aux | grep '[c]atalina'
# or
pgrep -af catalina
Example output:
tomcat 12845 ... org.apache.catalina.startup.Bootstrap start
The second column is the PID (here 12845).
2. Try graceful kill first
kill -15 12845 # SIGTERM — allows shutdown hooks
sleep 10
ps -p 12845 || echo "Process stopped"
3. Force kill only if needed
kill -9 12845 # SIGKILL — last resort; may leave lock files
Or kill all Tomcat JVMs matching catalina:
pkill -15 -f 'org.apache.catalina.startup.Bootstrap'
sleep 10
pkill -9 -f 'org.apache.catalina.startup.Bootstrap' # only if still running
4. Clean up and start again
After a forced kill, remove stale PID or lock files if present:
rm -f "$CATALINA_HOME/work/Catalina/localhost/*/SESSIONS.ser" 2>/dev/null
# Check logs before restart
tail -50 "$CATALINA_HOME/logs/catalina.out"
Then start via systemd or catalina.sh start.
Verify Tomcat is running
Check logs
cd "$CATALINA_HOME/logs"
tail -f catalina.out
Look for:
Server startup in [XXXX] milliseconds- No repeating
SEVEREorOutOfMemoryErrorlines - Each web application context started (your WAR or ROOT context)
Check the HTTP port
Default port is 8080 (configured in conf/server.xml):
curl -s -o /dev/null -w "%{http_code}\n" http://localhost:8080/
ss -tlnp | grep 8080
A response code 200, 302, or 404 from the root context usually means the connector is up.
File permissions (modern practice)
Tomcat needs read access to its install directory and write access to logs/, work/, and temp/. Use a dedicated user and avoid chmod 777.
# Tomcat install owned by service user
sudo chown -R tomcat:tomcat "$CATALINA_HOME"
# Directories: owner rwx, group rx
sudo find "$CATALINA_HOME" -type d -exec chmod 750 {} \;
# Files: owner rw, group r
sudo find "$CATALINA_HOME" -type f -exec chmod 640 {} \;
# Writable runtime dirs
sudo chmod 770 "$CATALINA_HOME/logs" "$CATALINA_HOME/work" "$CATALINA_HOME/temp"
If your app writes uploads outside Tomcat (e.g. /var/app/uploads):
sudo chown tomcat:tomcat /var/app/uploads
sudo chmod 750 /var/app/uploads
Grant only what the process needs — world-writable directories are a security risk.
Common restart scenarios
| Scenario | What to do |
|---|---|
| Deploy new WAR | Stop Tomcat → replace WAR in webapps/ → clear exploded folder if needed → start |
Change server.xml | Full restart required (systemctl restart tomcat) |
| Out of memory | Fix -Xmx in setenv.sh or systemd override, then restart |
| Port 8080 in use | ss -tlnp | grep 8080 — stop conflicting process or change connector port |
| Stuck after kill -9 | Remove work/ cache for the app, check catalina.out for lock errors |
setenv.sh example (heap size)
Create $CATALINA_HOME/bin/setenv.sh:
#!/bin/sh
export CATALINA_OPTS="-Xms512m -Xmx1024m -Dfile.encoding=UTF-8"
chmod +x "$CATALINA_HOME/bin/setenv.sh"
sudo chown tomcat:tomcat "$CATALINA_HOME/bin/setenv.sh"
Spring Boot with embedded Tomcat
If you run a Spring Boot executable JAR (embedded Tomcat), you do not use catalina.sh. Restart the application process instead:
# systemd example
sudo systemctl restart myapp
# manual JAR
kill -15 "$(pgrep -f myapp.jar)"
java -jar myapp.jar &
For dev, Spring Boot DevTools supports automatic restart on classpath changes — that is separate from standalone Tomcat administration.
Quick checklist
- Stop via systemd or
catalina.sh stopbefore force-killing - Confirm process exit with
psorsystemctl status - Read
catalina.outafter start — no fatal errors - Hit the app on its HTTP port or through your reverse proxy
- Permissions:
tomcatuser, no777on app or upload dirs