Key Architecture Takeaways
- ›Jakarta EE 10 Namespace Shift: Tomcat 10 marks the monumental migration from
javax.servlettojakarta.servlet(Servlet 6.0 / JSP 3.1). Legacy Java servlets must be refactored or converted using the Apache Tomcat Migration Tool. - ›Enterprise JVM Runtime: Tomcat 10.1 runs optimally on OpenJDK 21 LTS or OpenJDK 17 LTS. Configure
-XX:+UseG1GCand-XX:MaxGCPauseMillis=200to prevent destructive stop-the-world GC pauses under concurrent web traffic. - ›Strict Process Sandboxing: The Tomcat daemon must run under a dedicated unprivileged system account (
tomcat) with/usr/sbin/nologinshell. Hardened systemd configurations enforceProtectSystem=strictand isolate temporary directories withPrivateTmp=yes. - ›Edge Reverse Proxying: In production, never expose Tomcat port 8080 directly to WAN. Bind Tomcat to loopback (
127.0.0.1:8080) behind an Nginx reverse proxy terminating TLS 1.3, forwarding real client headers viaRemoteIpValve.
<MetricGrid metrics={[ { label: "Servlet Engine", value: "Jakarta EE 10", change: "Servlet 6.0", trend: "up", description: "Modern Java EE standard" }, { label: "JVM Runtime", value: "OpenJDK 21 LTS", change: "64-Bit Server VM", trend: "up", description: "Long Term Support" }, { label: "GC Pause Latency", value: "< 85ms", change: "Garbage-First (G1)", trend: "down", description: "Zero freeze spikes" }, { label: "Security Sandbox", value: "Systemd Strict", change: "Read-Only /usr /etc", trend: "up", description: "Zero privilege escalation" } ]} />
1. Architectural Overview: The Jakarta EE 10 Servlet Container
Apache Tomcat 10 is an open-source implementation of the Jakarta Servlet, Jakarta Server Pages (JSP), and Jakarta WebSocket technologies. Unlike lightweight Python or Node runtimes, Tomcat operates a layered container architecture designed for concurrent multi-threaded execution within the Java Virtual Machine.
Container Subsystem Hierarchy
- [01]Coyote NIO Connector: The protocol handler listening on port 8080. It uses non-blocking Java I/O (
NioEndpoint) to multiplex thousands of concurrent client keep-alive TCP connections across a fixed worker thread pool (maxThreads="200"). - [02]Catalina Engine: The top-level servlet container responsible for evaluating incoming HTTP requests, executing pipeline valves, and delegating execution to the appropriate Virtual Host (
<Host name="localhost">). - [03]Context / Webapps: Represents individual web applications (
.wararchives or unpacked directories under/opt/tomcat/webapps/). Each Context possesses an isolated class loader hierarchy, preventing dependency collisions across applications. - [04]Valves Pipeline: Interceptors placed within Catalina to handle cross-cutting concerns:
AccessLogValvefor structured request logging, andRemoteIpValvefor extracting real client IPs from upstream reverse proxies.
2. Step 1: Provisioning the OpenJDK 21 LTS Runtime
Tomcat 10.1 requires Java 11 or higher. For modern production deployments on Ubuntu 20.04, 22.04, or 24.04 LTS, OpenJDK 21 LTS or OpenJDK 17 LTS is the strongly recommended standard.
<CodeTabs tabs={[ { name: "Ubuntu 22.04 / 24.04 LTS (OpenJDK 21)", language: "bash", content: `# Update repository indexes sudo apt update
Install headless Java Development Kit (avoids pulling unneeded X11 desktop packages) sudo apt install -y openjdk-21-jdk-headless
Verify installed Java version
java -version
},
{
name: "Ubuntu 20.04 LTS (OpenJDK 17)",
language: "bash",
content: # Update repository indexes
sudo apt update
Install OpenJDK 17 LTS headless runtime sudo apt install -y openjdk-17-jdk-headless
Verify active version java -version` } ]} />
Verify the active JVM output:
openjdk version "21.0.4" 2024-07-16 LTS
OpenJDK Runtime Environment (build 21.0.4+7-Ubuntu-1ubuntu2)
OpenJDK 64-Bit Server VM (build 21.0.4+7-Ubuntu-1ubuntu2, mixed mode, sharing)3. Step 2: System User & Secure Directory Provisioning
Never run application servers as root. If an unauthenticated remote code execution vulnerability exists in an uploaded servlet, executing as root grants full kernel takeover.
<Steps steps={[ { title: "Create Dedicated System Account", description: "Create an unprivileged system user and group named 'tomcat' with home directory set to /opt/tomcat and interactive shell disabled.", code: "sudo useradd -m -U -d /opt/tomcat -s /usr/sbin/nologin tomcat" }, { title: "Download & Verify Tomcat 10.1 LTS", description: "Fetch the latest production release of Apache Tomcat 10.1 from the official Apache archive mirrors.", code: "cd /tmp\nwget https://downloads.apache.org/tomcat/tomcat-10/v10.1.28/bin/apache-tomcat-10.1.28.tar.gz" }, { title: "Extract Archive into /opt/tomcat", description: "Extract the binaries directly into the tomcat user's home directory, stripping the outer folder wrapper.", code: "sudo tar xzf apache-tomcat-10.1.28.tar.gz -C /opt/tomcat --strip-components=1" } ]} />
Enforcing Strict POSIX File Permissions
A critical security flaw in naive tutorials is running chmod -R 777 or giving ownership of the entire directory tree to the tomcat user. If the tomcat user owns the binaries (bin/) and configuration (conf/), a compromised servlet can rewrite its own startup scripts or tamper with server.xml.
<CodeTabs tabs={[ { name: "Hardened Permissions Script", language: "bash", content: `# 1. Assign base directory ownership to tomcat user and group sudo chown -R tomcat:tomcat /opt/tomcat
2. Make startup scripts executable by the tomcat user sudo chmod -R u+x /opt/tomcat/bin
3. Restrict conf/ to root:tomcat with 0750 permissions # Only root and members of the tomcat group can access configuration files sudo chown -R root:tomcat /opt/tomcat/conf sudo chmod 0750 /opt/tomcat/conf
4. Strictly lock down password files to 0640 sudo chmod 0640 /opt/tomcat/conf/tomcat-users.xml sudo chmod 0640 /opt/tomcat/conf/server.xml` } ]} />
4. Step 3: Configuring Administrative RBAC (tomcat-users.xml)
Access to the Tomcat Web Application Manager (/manager/html) and Virtual Host Manager (/host-manager/html) is governed by XML-based Role-Based Access Control (RBAC).
Edit /opt/tomcat/conf/tomcat-users.xml:
<tomcat-users xmlns="http://tomcat.apache.org/xml"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://tomcat.apache.org/xml tomcat-users.xsd"
version="1.0">
<!-- Define administrative roles -->
<role rolename="manager-gui"/>
<role rolename="manager-script"/>
<role rolename="admin-gui"/>
<!-- Restricted operator account (Replace 'ChangeMe_StrictPass_2026!' with a strong credential) -->
<user username="sysadmin"
password="ChangeMe_StrictPass_2026!"
roles="manager-gui,admin-gui,manager-script"/>
</tomcat-users>/manager and /host-manager strictly to localhost (127.0.0.1 / ::1) via a RemoteAddrValve inside /opt/tomcat/webapps/manager/META-INF/context.xml. Never comment out this valve to allow 0.0.0.0/0! Doing so exposes your admin console to automated credential stuffing attacks across the internet. Instead, manage Tomcat via an SSH tunnel or behind an authenticated reverse proxy.5. Step 4: Enterprise Systemd Unit with JVM Memory & G1GC Calibration
Create a production-grade systemd service file at /etc/systemd/system/tomcat.service.
<CodeTabs tabs={[ { name: "/etc/systemd/system/tomcat.service", language: "ini", content: `[Unit] Description=Apache Tomcat 10.1 Web Application Server After=network.target network-online.target Wants=network-online.target
[Service] Type=forking
User=tomcat Group=tomcat
Environment variables Environment="JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64" Environment="JAVA_OPTS=-Djava.awt.headless=true -Djava.security.egd=file:/dev/./urandom" Environment="CATALINA_HOME=/opt/tomcat" Environment="CATALINA_BASE=/opt/tomcat" Environment="CATALINA_PID=/opt/tomcat/temp/tomcat.pid"
Production JVM Heap & G1GC Memory Calibration: # -Xms: Initial heap size (set equal to -Xmx to avoid runtime heap allocation overhead) # -Xmx: Maximum heap size (allocate 50-70% of available server RAM) # -XX:+UseG1GC: Low-latency regional Garbage-First collector # -XX:MaxGCPauseMillis=200: Soft ceiling for GC collection pauses Environment="CATALINA_OPTS=-Xms2048M -Xmx2048M -server -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:+ParallelRefProcEnabled -XX:+AlwaysPreTouch"
ExecStart=/opt/tomcat/bin/startup.sh ExecStop=/opt/tomcat/bin/shutdown.sh
Lifecycle Restart & Supervision Restart=on-failure RestartSec=10 SuccessExitStatus=143
OS Resource Limits LimitNOFILE=65536 LimitNPROC=4096
Kernel Sandboxing & Filesystem Protection PrivateTmp=yes ProtectSystem=strict ProtectHome=yes ReadWritePaths=/opt/tomcat/logs /opt/tomcat/temp /opt/tomcat/webapps /opt/tomcat/work
[Install] WantedBy=multi-user.target` } ]} />
Reload Systemd and Enable Service
# Reload systemd manager configuration
sudo systemctl daemon-reload
# Enable service to start on system boot
sudo systemctl enable tomcat
# Start the Tomcat daemon
sudo systemctl start tomcat
# Verify active status
sudo systemctl status tomcat --no-pager6. Step 5: Production Nginx Edge Reverse Proxy & RemoteIpValve
To deliver high performance and industry standard security, place Tomcat behind Nginx acting as a TLS 1.3 termination proxy.
A. Enable RemoteIpValve in Tomcat (conf/server.xml)
Edit /opt/tomcat/conf/server.xml and locate the <Host name="localhost"> block. Insert the RemoteIpValve to ensure Tomcat logs the real client IP and acknowledges HTTPS requests:
<Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true">
<!-- Ingest X-Forwarded-For & X-Forwarded-Proto from Nginx -->
<Valve className="org.apache.catalina.valves.RemoteIpValve"
internalProxies="127\.0\.0\.1|::1"
remoteIpHeader="x-forwarded-for"
protocolHeader="x-forwarded-proto" />
<!-- Existing AccessLogValve follows... -->
</Host>B. Configure Nginx VirtualHost (/etc/nginx/sites-available/tomcat.conf)
<CodeTabs tabs={[ { name: "Nginx Reverse Proxy Config", language: "nginx", content: `server { listen 80; listen [::]:80; server_name java.nhit.in; return 301 https://$host$request_uri; }
server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name java.nhit.in;
ssl_certificate /etc/letsencrypt/live/java.nhit.in/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/java.nhit.in/privkey.pem; ssl_protocols TLSv1.2 TLSv1.3;
access_log /var/log/nginx/tomcat_access.log; error_log /var/log/nginx/tomcat_error.log;
Pass traffic to local Tomcat Coyote NIO connector location / { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme;
WebSocket support proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade";
Buffer & Timeout calibrations proxy_connect_timeout 60s; proxy_read_timeout 120s; proxy_buffer_size 16k; proxy_buffers 4 64k; } }` } ]} />
# Test Nginx syntax and reload
sudo nginx -t && sudo systemctl reload nginx7. Verifying Active Deployment & JVM Telemetry
Verify that the local Coyote connector responds on port 8080 and that the Nginx edge proxy terminates TLS cleanly:
# 1. Inspect listening TCP sockets
ss -tulnp | grep :8080
# 2. Local loopback probe
curl -I http://127.0.0.1:8080/
# 3. External HTTPS probe through Nginx
curl -I https://java.nhit.in/Output:
HTTP/1.1 200 OK
Server: nginx/1.24.0 (Ubuntu)
Date: Sun, 25 Oct 2026 10:35:00 GMT
Content-Type: text/html;charset=UTF-8
Connection: keep-aliveApache Tomcat 10.1 Official Documentation
Comprehensive reference manual covering Catalina configuration, Coyote connectors, and Jakarta EE 10 specifications.

