Graceful Tomcat Shutdown via Supervisor: Best Practices for Process Management in Production Environments


3 views

When managing Tomcat with Supervisor, we face two critical technical challenges:

  1. Supervisor's signal-based process termination conflicts with Tomcat's preferred shutdown protocol
  2. The environment setup complexity when bypassing Tomcat's startup scripts

Tomcat's standard shutdown sequence works through its dedicated shutdown port (default 8005). The shutdown.sh script essentially sends the SHUTDOWN command to this port, triggering:

  • Completion of current requests
  • Proper release of resources
  • Orderly thread pool termination

Here's an optimal Supervisor configuration that preserves Tomcat's environment while ensuring proper process tracking:

[program:tomcat]
command=/path/to/tomcat/bin/catalina.sh run
environment=CATALINA_HOME="/path/to/tomcat",JAVA_HOME="/path/to/java"
user=tomcat_user
autostart=true
autorestart=true
startsecs=10
startretries=3
redirect_stderr=true
stdout_logfile=/var/log/tomcat/stdout.log
stopsignal=INT
stopwaitsecs=60

For production environments, we recommend combining Supervisor with a custom shutdown script:

  1. Create a shutdown wrapper script:
#!/bin/bash
/path/to/tomcat/bin/shutdown.sh
# Wait for clean shutdown
sleep 15
# Force kill if still running
pkill -INT -f "catalina"

Then configure Supervisor to use this script:

[program:tomcat_shutdown]
command=/path/to/shutdown_wrapper.sh
autostart=false
autorestart=false

For complex deployments, consider these enhancements:

; Additional JVM options in Supervisor config
environment=CATALINA_OPTS="-Xms512m -Xmx1024m -Djava.security.egd=file:/dev/./urandom"

; Multiple Tomcat instances
[program:tomcat_instance1]
command=/path/to/tomcat1/bin/catalina.sh run
...
[program:tomcat_instance2]
command=/path/to/tomcat2/bin/catalina.sh run
...

Essential commands for managing the setup:

# Check Supervisor status
sudo supervisorctl status

# Tail Tomcat logs
tail -f /var/log/tomcat/stdout.log /path/to/tomcat/logs/catalina.out

# Graceful restart sequence
sudo supervisorctl stop tomcat
sleep 15
sudo supervisorctl start tomcat

For production systems, consider adding these monitoring enhancements:

  • Log rotation configuration
  • Email alerts for restarts
  • Integration with monitoring tools (Prometheus, New Relic)

When managing Tomcat with Supervisor, we face two key technical hurdles:

  • Losing Tomcat's native graceful shutdown capability (via shutdown.sh)
  • Difficulty maintaining environment variables from startup.sh

While Supervisor doesn't support custom shutdown commands, we can leverage Unix signals. Tomcat responds to SIGTERM for graceful shutdown:


[program:tomcat]
command=/path/to/java -Dcatalina.home=/opt/tomcat ...
stopasgroup=true
killasgroup=true
stopsignal=TERM

Key configuration parameters:

  • stopasgroup: Ensures child processes are stopped
  • killasgroup: Sends signal to entire process group
  • stopsignal: Uses SIGTERM instead of default SIGQUIT

To maintain the environment setup from startup.sh while still letting Supervisor track the Java process:


[program:tomcat]
command=sh -c '. /path/to/setenv.sh && exec /path/to/java ...'
environment=CATALINA_HOME="/opt/tomcat",JAVA_HOME="/usr/lib/jvm/java-11"
directory=/opt/tomcat

The critical exec replaces the shell process with Java, allowing proper process tracking.

Here's a production-ready Supervisor config combining both solutions:


[program:tomcat-production]
user=tomcat
command=sh -c '. /opt/tomcat/bin/setenv.sh && exec $JAVA_HOME/bin/java \
    -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager \
    -Djava.util.logging.config.file=/opt/tomcat/conf/logging.properties \
    -Xms512m -Xmx1024m \
    -classpath /opt/tomcat/bin/bootstrap.jar:/opt/tomcat/bin/tomcat-juli.jar \
    -Dcatalina.base=/opt/tomcat \
    -Dcatalina.home=/opt/tomcat \
    -Djava.io.tmpdir=/opt/tomcat/temp \
    org.apache.catalina.startup.Bootstrap start'
environment=CATALINA_PID="/var/run/tomcat.pid"
directory=/opt/tomcat
autostart=true
autorestart=true
startsecs=30
stopwaitsecs=30
stopasgroup=true
killasgroup=true
stopsignal=TERM
redirect_stderr=true
stdout_logfile=/var/log/tomcat/catalina.out

After implementation, verify proper shutdown behavior:


# Check process tree
ps auxf | grep tomcat

# Test graceful shutdown
supervisorctl stop tomcat-production

# Verify in catalina.out
tail -f /var/log/tomcat/catalina.out

Look for these log entries indicating proper shutdown sequence:

INFO [main] org.apache.catalina.core.StandardServer.await Received shutdown command
INFO [Thread-1] org.apache.catalina.core.StandardService.stopInternal Stopping service [Catalina]