Apache Tomcat is the go-to open-source Java servlet container for deploying Java web applications. In this guide, we will walk through installing Apache Tomcat 10 (or 11) on RHEL 10, Rocky Linux 10, or AlmaLinux 10, then secure it with a free Let’s Encrypt SSL certificate. Every step includes a verification command so you know things are working before moving on.
Tomcat 10.1.x implements Jakarta EE 10, while Tomcat 11.0.x targets Jakarta EE 11. Both require Java 17 or later – we will use Java 21, which is the current LTS release and ships in the default RHEL 10 repositories.
Prerequisites
Before you begin, make sure you have:
- A server running RHEL 10, Rocky Linux 10, or AlmaLinux 10 with root or sudo access.
- A registered domain name (or subdomain) pointing to your server’s public IP address – required for Let’s Encrypt.
- An active internet connection on the server.
- Ports 8080 and 8443 (or 80/443 if you plan to proxy) available and not blocked by an upstream firewall.
Update your system packages first:
sudo dnf update -y
Step 1 – Install Java 21 (OpenJDK)
Tomcat 10.1 and 11.0 both run on Java 17+. We will install OpenJDK 21, the latest long-term support release available in the default repos:
sudo dnf install -y java-21-openjdk java-21-openjdk-devel
Verify the installation:
java -version
You should see output similar to:
openjdk version "21.0.x" 2025-xx-xx LTS
OpenJDK Runtime Environment (Red_Hat-21.0.x) (build 21.0.x+xx)
OpenJDK 64-Bit Server VM (Red_Hat-21.0.x) (build 21.0.x+xx, mixed mode, sharing)
Set JAVA_HOME for the system. Create a profile script:
echo 'export JAVA_HOME=/usr/lib/jvm/java-21-openjdk' | sudo tee /etc/profile.d/java.sh
source /etc/profile.d/java.sh
echo $JAVA_HOME
Step 2 – Create a Tomcat System User and Group
Running Tomcat under a dedicated non-login user limits what a compromised application can do. Create the user and group now:
sudo groupadd -r tomcat
sudo useradd -r -g tomcat -d /opt/tomcat -s /sbin/nologin tomcat
Verify:
id tomcat
Expected output:
uid=xxx(tomcat) gid=xxx(tomcat) groups=xxx(tomcat)
Step 3 – Download and Install Apache Tomcat
Head to the Apache Tomcat 10 download page (or the Tomcat 11 download page) and grab the latest stable binary tarball. At the time of writing, Tomcat 10.1.39 and 11.0.6 are current. Adjust the version number as needed:
For Tomcat 10:
TOMCAT_VER="10.1.39"
cd /tmp
wget https://dlcdn.apache.org/tomcat/tomcat-10/v${TOMCAT_VER}/bin/apache-tomcat-${TOMCAT_VER}.tar.gz
For Tomcat 11 (alternative):
TOMCAT_VER="11.0.6"
cd /tmp
wget https://dlcdn.apache.org/tomcat/tomcat-11/v${TOMCAT_VER}/bin/apache-tomcat-${TOMCAT_VER}.tar.gz
Extract the archive to /opt/tomcat:
sudo mkdir -p /opt/tomcat
sudo tar -xzf /tmp/apache-tomcat-${TOMCAT_VER}.tar.gz -C /opt/tomcat --strip-components=1
Set ownership so the tomcat user controls everything under /opt/tomcat:
sudo chown -R tomcat:tomcat /opt/tomcat
sudo chmod -R u+x /opt/tomcat/bin
Verify the files are in place:
ls -la /opt/tomcat/
You should see directories like bin, conf, lib, logs, webapps, work, and temp.
Step 4 – Configure CATALINA_HOME Environment Variable
Set the CATALINA_HOME variable system-wide so scripts and the systemd unit can reference it:
echo 'export CATALINA_HOME=/opt/tomcat' | sudo tee /etc/profile.d/tomcat.sh
source /etc/profile.d/tomcat.sh
echo $CATALINA_HOME
The output should print /opt/tomcat.
Step 5 – Create a Systemd Service Unit for Tomcat
A systemd unit file lets you manage Tomcat with standard systemctl commands and ensures it starts automatically on boot.
Create the unit file:
sudo tee /etc/systemd/system/tomcat.service <<'EOF'
[Unit]
Description=Apache Tomcat Web Application Container
After=network.target
[Service]
Type=forking
User=tomcat
Group=tomcat
Environment="JAVA_HOME=/usr/lib/jvm/java-21-openjdk"
Environment="CATALINA_HOME=/opt/tomcat"
Environment="CATALINA_BASE=/opt/tomcat"
Environment="CATALINA_PID=/opt/tomcat/temp/tomcat.pid"
Environment="CATALINA_OPTS=-Xms512M -Xmx1024M -server -XX:+UseParallelGC"
ExecStart=/opt/tomcat/bin/startup.sh
ExecStop=/opt/tomcat/bin/shutdown.sh
RestartSec=10
Restart=always
[Install]
WantedBy=multi-user.target
EOF
Reload systemd, start Tomcat, and enable it on boot:
sudo systemctl daemon-reload
sudo systemctl start tomcat
sudo systemctl enable tomcat
Verify Tomcat is running:
sudo systemctl status tomcat
You should see active (running) in the output. You can also check the listening port:
ss -tlnp | grep 8080
Step 6 – Configure Tomcat Manager Access (tomcat-users.xml)
The Tomcat Manager and Host Manager web apps are useful for deploying and managing applications through the browser. By default, no users have access. Let’s fix that.
Edit the users configuration file:
sudo nano /opt/tomcat/conf/tomcat-users.xml
Add the following lines just before the closing </tomcat-users> tag. Replace the password with something strong:
<role rolename="manager-gui"/>
<role rolename="admin-gui"/>
<user username="admin" password="YourStrongPasswordHere" roles="manager-gui,admin-gui"/>
By default, the Manager app only allows connections from localhost. To access it from your remote machine, edit the context files for both Manager and Host Manager:
sudo nano /opt/tomcat/webapps/manager/META-INF/context.xml
Find the Valve element with the RemoteAddrValve className and either comment it out or update the allow attribute to include your IP address. For example, to allow any IP (not recommended for production):
<!-- <Valve className="org.apache.catalina.valves.RemoteAddrValve"
allow="127\.\d+\.\d+\.\d+|::1|0:0:0:0:0:0:0:1" /> -->
Do the same for the Host Manager context file:
sudo nano /opt/tomcat/webapps/host-manager/META-INF/context.xml
After making the changes, restart Tomcat:
sudo systemctl restart tomcat
Verify: Open http://your-server-ip:8080/manager/html in your browser and log in with the credentials you set above.
Step 7 – Configure server.xml (Connector Ports and Address Binding)
The main Tomcat configuration lives in /opt/tomcat/conf/server.xml. Let’s review the key connector settings.
sudo nano /opt/tomcat/conf/server.xml
The default HTTP connector looks like this:
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
You can change the port or add an address attribute to bind Tomcat to a specific interface. For example, to listen only on the public IP:
<Connector port="8080" protocol="HTTP/1.1"
address="0.0.0.0"
connectionTimeout="20000"
redirectPort="8443" />
We will add the HTTPS/SSL connector in a later step after obtaining the Let’s Encrypt certificate. For now, leave redirectPort="8443" as is.
Step 8 – Open Firewall Ports
RHEL 10 and its derivatives use firewalld by default. Open ports 8080 (HTTP) and 8443 (HTTPS) along with port 80 (needed for Let’s Encrypt HTTP-01 challenge):
sudo firewall-cmd --permanent --add-port=8080/tcp
sudo firewall-cmd --permanent --add-port=8443/tcp
sudo firewall-cmd --permanent --add-port=80/tcp
sudo firewall-cmd --reload
Verify the rules are active:
sudo firewall-cmd --list-ports
You should see 8080/tcp 8443/tcp 80/tcp in the output.
At this point, open http://your-server-ip:8080 in a browser. You should see the default Tomcat welcome page.
Step 9 – Install Certbot and Obtain a Let’s Encrypt Certificate
Certbot is the standard tool for obtaining and renewing Let’s Encrypt certificates. Install it from the EPEL repository:
sudo dnf install -y epel-release
sudo dnf install -y certbot
Verify certbot is installed:
certbot --version
Now obtain a certificate using the standalone mode. This temporarily starts a web server on port 80, so make sure nothing else is using that port. Replace tomcat.example.com with your actual domain:
sudo certbot certonly --standalone -d tomcat.example.com
Follow the prompts. Once done, your certificate files will be at:
- Certificate:
/etc/letsencrypt/live/tomcat.example.com/fullchain.pem - Private key:
/etc/letsencrypt/live/tomcat.example.com/privkey.pem
Verify the certificate:
sudo openssl x509 -in /etc/letsencrypt/live/tomcat.example.com/fullchain.pem -noout -dates
Step 10 – Create a PKCS12 Keystore from the Let’s Encrypt Certificate
Tomcat’s native SSL connector works with PKCS12 keystores. Convert the Let’s Encrypt PEM files into a PKCS12 keystore:
sudo openssl pkcs12 -export \
-in /etc/letsencrypt/live/tomcat.example.com/fullchain.pem \
-inkey /etc/letsencrypt/live/tomcat.example.com/privkey.pem \
-out /opt/tomcat/conf/keystore.p12 \
-name tomcat \
-password pass:YourKeystorePassword
Set proper ownership and permissions on the keystore:
sudo chown tomcat:tomcat /opt/tomcat/conf/keystore.p12
sudo chmod 600 /opt/tomcat/conf/keystore.p12
Verify the keystore contents:
sudo keytool -list -keystore /opt/tomcat/conf/keystore.p12 -storetype PKCS12 -storepass YourKeystorePassword
You should see an entry with the alias tomcat.
Step 11 – Configure the Tomcat SSL Connector
Now edit server.xml to add an HTTPS connector that uses the PKCS12 keystore:
sudo nano /opt/tomcat/conf/server.xml
Add the following connector block (or uncomment and modify the existing SSL connector). Place it after the existing HTTP connector:
<Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol"
maxThreads="150" SSLEnabled="true"
scheme="https" secure="true">
<SSLHostConfig>
<Certificate certificateKeystoreFile="/opt/tomcat/conf/keystore.p12"
certificateKeystorePassword="YourKeystorePassword"
certificateKeystoreType="PKCS12"
certificateKeyAlias="tomcat" />
</SSLHostConfig>
</Connector>
Restart Tomcat to apply the SSL configuration:
sudo systemctl restart tomcat
Verify Tomcat is listening on port 8443:
ss -tlnp | grep 8443
Step 12 – Set Up Automatic Certificate Renewal
Let’s Encrypt certificates expire every 90 days. Certbot installs a systemd timer that handles renewal automatically. However, we need a post-renewal hook that rebuilds the keystore and restarts Tomcat.
Create a renewal hook script:
sudo tee /etc/letsencrypt/renewal-hooks/deploy/tomcat-keystore.sh <<'EOF'
#!/bin/bash
DOMAIN="tomcat.example.com"
KEYSTORE="/opt/tomcat/conf/keystore.p12"
KEYSTORE_PASS="YourKeystorePassword"
# Rebuild the PKCS12 keystore
openssl pkcs12 -export \
-in /etc/letsencrypt/live/${DOMAIN}/fullchain.pem \
-inkey /etc/letsencrypt/live/${DOMAIN}/privkey.pem \
-out ${KEYSTORE} \
-name tomcat \
-password pass:${KEYSTORE_PASS}
chown tomcat:tomcat ${KEYSTORE}
chmod 600 ${KEYSTORE}
# Restart Tomcat to load the new certificate
systemctl restart tomcat
EOF
sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/tomcat-keystore.sh
Verify the certbot timer is active:
sudo systemctl list-timers | grep certbot
You should see the certbot-renew.timer listed. You can also do a dry run to confirm renewal works:
sudo certbot renew --dry-run
Step 13 – Test HTTPS Access
Open your browser and navigate to:
https://tomcat.example.com:8443
You should see the Tomcat default page served over HTTPS with a valid Let’s Encrypt certificate (no browser warnings).
You can also test from the command line:
curl -I https://tomcat.example.com:8443
Expected output should include HTTP/1.1 200 and proper SSL headers.
To check the SSL certificate details:
echo | openssl s_client -connect tomcat.example.com:8443 -servername tomcat.example.com 2>/dev/null | openssl x509 -noout -subject -dates
Troubleshooting
Tomcat fails to start
Check the Catalina log for errors:
sudo tail -100 /opt/tomcat/logs/catalina.out
Common causes include wrong JAVA_HOME path, permission issues on /opt/tomcat, or port conflicts. Verify Java is found:
sudo -u tomcat /usr/lib/jvm/java-21-openjdk/bin/java -version
Port 8080 or 8443 is not accessible
First check that Tomcat is actually listening:
ss -tlnp | grep -E '8080|8443'
Then check firewalld:
sudo firewall-cmd --list-all
If using a cloud provider, verify that the security group or network ACL allows traffic on these ports.
SSL certificate errors in browser
Verify the keystore was created correctly:
sudo keytool -list -v -keystore /opt/tomcat/conf/keystore.p12 -storetype PKCS12 -storepass YourKeystorePassword
Make sure the domain in the certificate matches the domain you are using in the browser. Also confirm that server.xml references the correct keystore path, password, and alias.
Certbot fails to obtain a certificate
The standalone mode needs port 80 to be free. Check if anything is using it:
ss -tlnp | grep :80
If Apache httpd or Nginx is running and you want to keep it, use the --webroot plugin instead of --standalone. Also verify that your DNS A record points to the correct IP:
dig +short tomcat.example.com
Manager app returns 403 Forbidden
This usually means the RemoteAddrValve in the Manager’s context.xml is blocking your IP. Either comment out the Valve or add your IP to the allow regex. Remember to restart Tomcat after changes.
Java heap space errors
If your applications run out of memory, increase the heap size in the systemd unit file. Edit CATALINA_OPTS:
Environment="CATALINA_OPTS=-Xms1024M -Xmx2048M -server -XX:+UseParallelGC"
Then reload and restart:
sudo systemctl daemon-reload
sudo systemctl restart tomcat
SELinux blocking Tomcat
On RHEL 10 with SELinux in enforcing mode, Tomcat may be blocked from binding to ports or accessing files. Check for denials:
sudo ausearch -m avc -ts recent
If SELinux is the issue, allow Tomcat to bind to its ports:
sudo semanage port -a -t http_port_t -p tcp 8080
sudo semanage port -a -t http_port_t -p tcp 8443
You may also need to set the correct file contexts:
sudo semanage fcontext -a -t tomcat_exec_t "/opt/tomcat/bin(/.*)?"
sudo restorecon -Rv /opt/tomcat/bin
Conclusion
You now have Apache Tomcat 10 (or 11) running on RHEL 10 / Rocky Linux 10 / AlmaLinux 10, secured with a Let’s Encrypt SSL certificate. The systemd service ensures Tomcat starts on boot, and the certbot renewal hook automatically rebuilds the keystore when certificates are renewed.
For a production deployment, consider placing Tomcat behind a reverse proxy like Nginx or Apache httpd. This lets you terminate SSL on the proxy, run Tomcat on standard ports without needing root, and add caching or load balancing as your traffic grows.

































































Hey! really useful post! just a question, let’s encrypt recommends to upgrade the certificate with a cron daily, if I update the certificate with .’/path/to/certbot-auto renew –no-self-upgrade’ should I generate again the PKCS12 file and the JKS? do you have any suggestion to automate that task with the approach you posted here?
Thanks!
Hi, yeah you can automate renewal with cron job. Then have a script to generate jks and restart tomcat.
every thing ok but no https appear,
can you help me
What do you mean?, do you get any error?
I got no error but https doesn’t appear,
It still http://…..
This is the only post so far that work for me with Tomcat 9.1.
Cool.. I’m happy to hear that .
Thanks its works