Set up Apache Tomcat 11 with SSL certificates and JVM optimization for Java web applications

Intermediate 45 min Apr 03, 2026 30 views
Ubuntu 24.04 Ubuntu 22.04 Debian 12 AlmaLinux 9 Rocky Linux 9 Fedora 41

Install and configure Apache Tomcat 11 with Java 21, SSL/TLS certificates, JVM performance tuning, security hardening, and production-ready monitoring for enterprise Java web applications.

Prerequisites

  • Root or sudo access
  • Domain name pointed to server
  • At least 4GB RAM
  • Java 21 compatible hardware

What this solves

Apache Tomcat 11 provides a robust servlet container for Java web applications with support for Jakarta EE 10 and modern Java features. This tutorial sets up a production-ready Tomcat 11 installation with SSL certificates, optimized JVM settings, security hardening, and comprehensive monitoring. You'll configure automatic SSL certificate management, fine-tune garbage collection and memory allocation, secure the management interface, and implement JMX monitoring for performance insights.

Step-by-step installation

Update system packages

Start by updating your package manager to ensure you get the latest security patches and package versions.

sudo apt update && sudo apt upgrade -y
sudo apt install -y wget curl unzip
sudo dnf update -y
sudo dnf install -y wget curl unzip

Install Java 21 JDK

Tomcat 11 requires Java 11 or newer, but Java 21 provides better performance and security features for production environments.

sudo apt install -y openjdk-21-jdk
java -version
sudo dnf install -y java-21-openjdk-devel
java -version

Create Tomcat user and directories

Create a dedicated system user for Tomcat with restricted privileges and proper directory structure.

sudo useradd -r -m -U -d /opt/tomcat -s /bin/false tomcat
sudo mkdir -p /opt/tomcat/logs /opt/tomcat/temp /opt/tomcat/work

Download and install Tomcat 11

Download the latest Tomcat 11 release and extract it to the proper directory structure.

cd /tmp
wget https://downloads.apache.org/tomcat/tomcat-11/v11.0.0/bin/apache-tomcat-11.0.0.tar.gz
tar -xzf apache-tomcat-11.0.0.tar.gz
sudo cp -r apache-tomcat-11.0.0/* /opt/tomcat/
sudo chown -R tomcat:tomcat /opt/tomcat
sudo chmod +x /opt/tomcat/bin/*.sh

Configure environment variables

Set up Java home and Tomcat environment variables for consistent operation across system restarts.

JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64
CATALINA_HOME=/opt/tomcat
CATALINA_BASE=/opt/tomcat

Apply the environment changes:

source /etc/environment

Configure JVM optimization settings

Create optimized JVM settings for production workloads with proper garbage collection and memory management.

#!/bin/bash

JVM Memory Settings

CATALINA_OPTS="$CATALINA_OPTS -Xms2048m -Xmx4096m"

Garbage Collection Optimization (G1GC)

CATALINA_OPTS="$CATALINA_OPTS -XX:+UseG1GC" CATALINA_OPTS="$CATALINA_OPTS -XX:G1HeapRegionSize=16m" CATALINA_OPTS="$CATALINA_OPTS -XX:MaxGCPauseMillis=200" CATALINA_OPTS="$CATALINA_OPTS -XX:+ParallelRefProcEnabled"

JVM Performance Tuning

CATALINA_OPTS="$CATALINA_OPTS -XX:+OptimizeStringConcat" CATALINA_OPTS="$CATALINA_OPTS -XX:+UseStringDeduplication" CATALINA_OPTS="$CATALINA_OPTS -Djava.awt.headless=true"

Security Settings

CATALINA_OPTS="$CATALINA_OPTS -Djava.security.egd=file:/dev/./urandom"

JMX Monitoring

CATALINA_OPTS="$CATALINA_OPTS -Dcom.sun.management.jmxremote" CATALINA_OPTS="$CATALINA_OPTS -Dcom.sun.management.jmxremote.port=9999" CATALINA_OPTS="$CATALINA_OPTS -Dcom.sun.management.jmxremote.ssl=false" CATALINA_OPTS="$CATALINA_OPTS -Dcom.sun.management.jmxremote.authenticate=false" CATALINA_OPTS="$CATALINA_OPTS -Dcom.sun.management.jmxremote.local.only=true"

GC Logging

CATALINA_OPTS="$CATALINA_OPTS -Xlog:gc*:$CATALINA_BASE/logs/gc.log:time,tags" export CATALINA_OPTS

Make the script executable:

sudo chown tomcat:tomcat /opt/tomcat/bin/setenv.sh
sudo chmod 755 /opt/tomcat/bin/setenv.sh

Configure server.xml for SSL and performance

Configure Tomcat's main server configuration with SSL connector and performance optimizations.



  
  
  
  
  

  
    
  

  
    
    

    
    
      
      
        
      
    

    
      
        
      

      
        
      
    
  

Install and configure Let's Encrypt SSL certificates

Install Certbot for automatic SSL certificate management and renewal.

sudo apt install -y certbot
sudo dnf install -y certbot

Generate SSL certificate for your domain:

sudo certbot certonly --standalone -d example.com

Convert SSL certificates to Java keystore

Convert Let's Encrypt certificates to PKCS12 format that Tomcat can use directly.

sudo openssl pkcs12 -export -in /etc/letsencrypt/live/example.com/fullchain.pem \
  -inkey /etc/letsencrypt/live/example.com/privkey.pem \
  -out /opt/tomcat/conf/keystore.p12 \
  -name tomcat -passout pass:changeit

Set proper permissions on the keystore:

sudo chown tomcat:tomcat /opt/tomcat/conf/keystore.p12
sudo chmod 600 /opt/tomcat/conf/keystore.p12
Never use chmod 777. SSL keystores contain private keys and must only be readable by the Tomcat user. Use chmod 600 for private keys and chown to set correct ownership.

Configure SSL certificate auto-renewal

Create a script to automatically renew certificates and update the Tomcat keystore.

#!/bin/bash

Renew Let's Encrypt certificate and update Tomcat keystore

certbot renew --quiet if [ $? -eq 0 ]; then # Convert renewed certificate to PKCS12 openssl pkcs12 -export -in /etc/letsencrypt/live/example.com/fullchain.pem \ -inkey /etc/letsencrypt/live/example.com/privkey.pem \ -out /opt/tomcat/conf/keystore.p12 \ -name tomcat -passout pass:changeit # Set correct permissions chown tomcat:tomcat /opt/tomcat/conf/keystore.p12 chmod 600 /opt/tomcat/conf/keystore.p12 # Restart Tomcat to load new certificate systemctl restart tomcat fi

Make the script executable and add to crontab:

sudo chmod +x /usr/local/bin/renew-tomcat-ssl.sh
sudo crontab -e

Add this line to run renewal check daily:

0 2   * /usr/local/bin/renew-tomcat-ssl.sh

Configure Tomcat user management and security

Set up secure user authentication for the Tomcat manager application with role-based access control.



  
  
  
  
  

Restrict manager application access

Configure IP-based access restrictions for the Tomcat manager and host-manager applications.



  
  
  


  
  
  

Create systemd service for Tomcat

Configure Tomcat as a systemd service with proper resource limits and automatic startup.

[Unit]
Description=Apache Tomcat Web Application Container
After=network.target

[Service]
Type=forking

Environment=JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64
Environment=CATALINA_PID=/opt/tomcat/temp/tomcat.pid
Environment=CATALINA_HOME=/opt/tomcat
Environment=CATALINA_BASE=/opt/tomcat
Environment="CATALINA_OPTS=-Xms2048m -Xmx4096m -server -XX:+UseParallelGC"
Environment="JAVA_OPTS=-Djava.awt.headless=true -Djava.security.egd=file:/dev/./urandom"

ExecStart=/opt/tomcat/bin/startup.sh
ExecStop=/opt/tomcat/bin/shutdown.sh

User=tomcat
Group=tomcat
UMask=0007
RestartSec=10
Restart=always

Security settings

NoNewPrivileges=true PrivateTmp=true ProtectHome=true ProtectSystem=strict ReadWritePaths=/opt/tomcat/

Resource limits

LimitNOFILE=65536 LimitNPROC=4096 [Install] WantedBy=multi-user.target

Configure system resource limits

Set appropriate system resource limits for the Tomcat user to handle high-traffic applications. This complements the systemd resource limits and ensures proper resource allocation across system restarts.

echo "tomcat soft nofile 65536" | sudo tee -a /etc/security/limits.conf
echo "tomcat hard nofile 65536" | sudo tee -a /etc/security/limits.conf
echo "tomcat soft nproc 4096" | sudo tee -a /etc/security/limits.conf
echo "tomcat hard nproc 4096" | sudo tee -a /etc/security/limits.conf

For more detailed resource limit configuration, refer to our Linux system resource limits guide.

Configure log rotation

Set up log rotation to prevent Tomcat logs from consuming excessive disk space.

/opt/tomcat/logs/catalina.out {
    copytruncate
    daily
    rotate 52
    compress
    missingok
    create 644 tomcat tomcat
}

/opt/tomcat/logs/*.log {
    copytruncate
    daily
    rotate 52
    compress
    missingok
    create 644 tomcat tomcat
}

/opt/tomcat/logs/localhost_access_log*.txt {
    copytruncate
    daily
    rotate 52
    compress
    missingok
    create 644 tomcat tomcat
}

Configure firewall rules

Open the necessary ports for Tomcat HTTP, HTTPS, and JMX monitoring access.

sudo ufw allow 8080/tcp
sudo ufw allow 8443/tcp
sudo ufw allow from 127.0.0.1 to any port 9999
sudo firewall-cmd --permanent --add-port=8080/tcp
sudo firewall-cmd --permanent --add-port=8443/tcp
sudo firewall-cmd --permanent --add-rich-rule="rule family='ipv4' source address='127.0.0.1' port protocol='tcp' port='9999' accept"
sudo firewall-cmd --reload

Enable and start Tomcat service

Enable Tomcat to start automatically on boot and start the service immediately.

sudo systemctl daemon-reload
sudo systemctl enable tomcat
sudo systemctl start tomcat
sudo systemctl status tomcat

Configure performance monitoring with JMX

Set up JMX monitoring tools

Install monitoring tools to track JVM performance, garbage collection, and application metrics.

cd /opt/tomcat/bin
sudo -u tomcat wget https://repo1.maven.org/maven2/io/prometheus/jmx/jmx_prometheus_javaagent/0.20.0/jmx_prometheus_javaagent-0.20.0.jar

Create JMX monitoring configuration

Configure JMX metrics collection for comprehensive application monitoring.

---
rules:
  # JVM metrics
  - pattern: 'java.lang(.+)'
    name: jvm_memory_heap_$1
    type: GAUGE
  
  # Garbage Collection metrics
  - pattern: 'java.lang'
    name: jvm_gc_collection_count
    labels:
      gc: $1
    type: COUNTER
  
  # Tomcat connector metrics
  - pattern: 'Catalina<(.+)>'
    name: tomcat_connector_$2
    labels:
      port: $1
    type: GAUGE
  
  # Thread pool metrics
  - pattern: 'Catalina<(.+)>'
    name: tomcat_threadpool_$2
    labels:
      name: $1
    type: GAUGE

Update setenv.sh to include Prometheus JMX agent:

echo 'CATALINA_OPTS="$CATALINA_OPTS -javaagent:/opt/tomcat/bin/jmx_prometheus_javaagent-0.20.0.jar=8088:/opt/tomcat/conf/jmx-config.yml"' | sudo tee -a /opt/tomcat/bin/setenv.sh

Verify your setup

Check that Tomcat is running correctly and all components are properly configured:

# Check Tomcat service status
sudo systemctl status tomcat

Verify Tomcat is listening on correct ports

sudo netstat -tlnp | grep java

Check SSL certificate

curl -I https://example.com:8443

Test HTTP redirect to HTTPS

curl -I http://example.com:8080

Check JVM memory usage

jstat -gc $(pgrep -f tomcat)

Monitor JMX metrics

curl http://localhost:8088/metrics | head -20

Check log files

sudo tail -f /opt/tomcat/logs/catalina.out sudo tail -f /opt/tomcat/logs/gc.log

Access the Tomcat manager interface at https://example.com:8443/manager using the configured admin credentials.

Common issues

SymptomCauseFix
Service fails to start Java not found or wrong version which java && java -version to verify Java 21+ installation
Permission denied errors Incorrect file ownership sudo chown -R tomcat:tomcat /opt/tomcat and chmod 755 /opt/tomcat/bin/*.sh
SSL handshake failures Keystore password mismatch Verify keystore password matches server.xml configuration
Out of memory errors Insufficient heap size Increase -Xmx value in setenv.sh and restart service
Manager app 403 forbidden IP restriction in context.xml Add your IP to RemoteAddrValve allow pattern
High CPU usage Inappropriate GC settings Monitor GC logs and tune G1GC parameters or switch collectors
Port already in use Another process using 8080/8443 sudo lsof -i :8080 to identify conflicting process
Certificate renewal fails Certbot validation issues Check domain DNS resolution and firewall rules for port 80

Next steps

Automated install script

Run this to automate the entire setup

#tomcat 11 #java application server #tomcat ssl #jvm tuning #tomcat security #jakarta ee

Need help?

Don't want to manage this yourself?

We handle infrastructure for businesses that depend on uptime. From initial setup to ongoing operations.

Talk to an engineer