Apache TomEE 10.2.0
Overview
This guide covers launching and using the Apache TomEE 10.2.0 on Ubuntu 24.04 AMI from SecureAnyCloud on Amazon EC2. It is one version of the SecureAnyCloud Java application servers listing on AWS Marketplace; you pick it as the software version when you subscribe and launch.
Apache TomEE is the Jakarta EE application server from the Apache Software Foundation, built on Apache Tomcat. This image ships the TomEE 10.2.0 "plus" distribution, which provides the Jakarta EE 10 stack (Servlet, JSP, JSF, CDI, EJB, JPA, JAX-RS, JAX-WS, JMS, JTA, Bean Validation and more) on Apache Tomcat and OpenJDK 17.
TomEE 10.2.0 embeds Tomcat 10.1.57. This image applies the Apache Tomcat 10.1.60 security patch release over TomEE 10.2.0's 10.1.57 (the same 10.1 line, patch level only), which fixes the Important security-constraint bypasses CVE-2026-65182 and CVE-2026-76183, among others. TomEE 10.2.0 is the current stable release of the TomEE 10 line (Jakarta EE 10), which replaces the end-of-life 9.x line. Tomcat 10.1 is Apache's supported Tomcat branch for it.
What you get: the following SecureAnyCloud additions to the upstream download.
- HTTPS from the first boot. nginx serves TomEE on port 443 with a certificate generated on your instance's first boot. Port 80 redirects to HTTPS, and requests for any host name the instance does not serve are dropped.
- No default login. The image ships with no Manager user at all. First boot creates two users with passwords unique to your instance:
adminfor the Manager web interface anddeployerfor scripted deployments. TomEE stores only PBKDF2 hashes of them, and locks out a user after repeated wrong passwords. The plain passwords are in/root/tomee-credentials.txt, readable by root only. - Fail closed. TomEE refuses to start unless every Manager user has a hashed password, and nginx refuses to start until first boot has finished.
- TomEE on loopback. TomEE's only connector listens on
127.0.0.1:8080behind nginx. The shutdown port is disabled, there is no AJP connector, and the documentation, examples and host manager applications are not deployed. Error pages do not show the server version. - The host name comes from configuration. TomEE and nginx are pinned to the instance's configured name, so applications build their URLs from it whatever headers a client sends. The client address TomEE logs and hands to your applications is the real address of the connection.
- Hardened defaults. TomEE runs as its own unprivileged user in a systemd sandbox, TomEE and nginx are blocked from the instance metadata service, and the AMI requires IMDSv2.
- Fully patched Ubuntu 24.04 LTS at build time, with unattended security upgrades switched on.
What is on the image: Apache TomEE 10.2.0 plus (/opt/tomee, the official Apache release, checksum verified, with its bundled Jackson libraries updated from 2.22.1 to the 2.22.4 security release and its embedded Apache Tomcat jars updated from 10.1.57 to the 10.1.60 security patch release), OpenJDK 17 (runtime only), nginx, Ubuntu 24.04 LTS, cloud-init and the AWS Systems Manager agent. Your applications, configuration and logs live in /var/lib/tomee on a separate 30 GiB EBS data volume.
Default user: ubuntu; Architecture: x86_64; Recommended instance type: m5.large
Prerequisites
- An AWS account subscribed to the listing, an EC2 key pair and a VPC subnet with a route to the internet (or a private route you use for management).
- A security group that allows TCP 22 (SSH), TCP 443 (TomEE over HTTPS) and TCP 80 (redirects to HTTPS) from your own networks only.
Launch the instance
In the AWS Marketplace listing choose Continue to Subscribe, then Continue to Configuration, pick Apache TomEE 10.2.0 on Ubuntu 24.04 as the software version and your Region, then Launch through EC2. Choose m5.large, or a larger type for bigger applications. TomEE sizes its Java heap to half of the instance's memory. Then select your key pair and a security group as described above.
The AMI carries two volumes: the root volume and the 30 GiB /var/lib/tomee data volume. Enlarge the data volume at launch if your applications need more space.
With the AWS CLI (replace the values in angle brackets):
aws ec2 run-instances --region <region> --image-id <ami-id-from-the-listing> \
--instance-type m5.large --key-name <your-key-pair> \
--security-group-ids <your-security-group> --subnet-id <your-subnet> \
--metadata-options HttpTokens=required,HttpEndpoint=enabled \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=tomee}]'Connect to your instance
Version SSH user
Apache TomEE 10.2.0 on Ubuntu 24.04 ubuntu
ssh -i <your-key.pem> ubuntu@<public-ip>Check the services
First boot generates the certificate and the passwords, then TomEE and nginx start:
systemctl is-active tomee nginx apache-tomee-firstboot
sudo test -f /var/lib/sac/data/apache-tomee-firstboot.done && echo FIRSTBOOT_DONEAll three units are active and the first boot marker is present:
active active active FIRSTBOOT_DONEOnly SSH and nginx listen on public addresses; TomEE listens on loopback only:
sudo ss -lntp | awk 'NR>1 {print $4, $6}' | grep -v '127.0.0.5' | sed -E 's/,pid=[0-9]+,fd=[0-9]+//g' | sort0.0.0.0:22 users:(("sshd"),("systemd")) 0.0.0.0:443 users:(("nginx"),("nginx"),("nginx")) 0.0.0.0:80 users:(("nginx"),("nginx"),("nginx")) 127.0.0.1:8080 users:(("java")) [::]:22 users:(("sshd"),("systemd")) [::]:443 users:(("nginx"),("nginx"),("nginx")) [::]:80 users:(("nginx"),("nginx"),("nginx"))Get your URL and passwords
sudo cat /root/tomee-credentials.txtThe file is readable by root only and holds the URLs, the two user names and this instance's passwords:
TOMEE_URL=https://ec2-<public-ip-dashed>.<region>.compute.amazonaws.com/ TOMEE_MANAGER_URL=https://ec2-<public-ip-dashed>.<region>.compute.amazonaws.com/manager/html TOMEE_ADMIN_USERNAME=admin TOMEE_ADMIN_PASSWORD=<TOMEE_ADMIN_PASSWORD> TOMEE_DEPLOYER_USERNAME=deployer TOMEE_DEPLOYER_PASSWORD=<TOMEE_DEPLOYER_PASSWORD>The URL uses the instance's public DNS name from EC2 instance metadata. The certificate also names the public and private IP addresses, so https://<public-ip>/ works too. To serve TomEE under your own DNS name, see Step 9.
Open TomEE in your browser
Open the TOMEE_URL in your browser. The certificate is self-signed, so your browser asks you to confirm it the first time (or install your own certificate, Step 9). The SecureAnyCloud welcome page links to the sample application and the Manager:
The sample application is a small JSP page compiled and served by your TomEE. It shows the server version, the address TomEE is served as (always your configured name) and your own address:
From the command line on the instance, the same checks:
H=$(sudo grep '^TOMEE_URL=' /root/tomee-credentials.txt | sed -E 's#^TOMEE_URL=https://([^/]+)/$#\1#') R="--resolve $H:443:127.0.0.1 --resolve $H:80:127.0.0.1"
L=$(curl -s $R -o /dev/null -w '%{http_code} %{redirect_url}' "http://$H/"); echo "http:// -> $L" [ "$L" = "301 https://$H/" ] || exit 1
P=$(curl -sk $R "https://$H/sample/"); echo "$P" | grep -E '<h1>|Server:|Served as:' case "$P" in *"Served as: https://$H:443/sample/"*) ;; *) exit 1 ;; esac
X=$(curl -sk -o /dev/null -w '%{http_code}' -H 'Host: evil.example' https://127.0.0.1/ || true); echo "unknown Host -> $X (connection closed)" [ "$X" = 000 ] || exit 1http:// -> 301 https://ec2-<public-ip-dashed>.<region>.compute.amazonaws.com/ <body><h1>Sample application on Apache TomEE</h1> <li>Server: Apache Tomcat (TomEE)/10.1.60 (10.2.0)</li> <li>Served as: https://ec2-<public-ip-dashed>.<region>.compute.amazonaws.com:443/sample/</li> unknown Host -> 000 (connection closed)Sign in to the Manager
Open the TOMEE_MANAGER_URL and sign in as admin with TOMEE_ADMIN_PASSWORD. The Manager lists the deployed applications (/, /manager and /sample) with buttons to start, stop, reload and undeploy each one, and a form to upload and deploy a WAR file:
Further down are the WAR upload form, diagnostics and the server information (TomEE 10.2.0 on the patched Tomcat 10.1.60, OpenJDK 17):
Server Status shows the JVM memory pools and the loopback connector http-nio-127.0.0.1-8080, including the real client address of each request:
The Manager refuses a missing or wrong login, and the default tomcat/tomcat login does not exist. The admin user can use the web interface but not the text API, which belongs to deployer. The passwords are passed to curl on standard input, so they never appear in the process list or your shell history:
sudo bash -c ' F=/root/tomee-credentials.txt H=$(grep "^TOMEE_URL=" $F | sed -E "s#^TOMEE_URL=https://([^/]+)/\$#\1#") A=$(grep "^TOMEE_ADMIN_PASSWORD=" $F | cut -d= -f2-) R="--resolve $H:443:127.0.0.1" as() { printf "user = \"%s\"\n" "$1"; } check() { echo "$1 -> HTTP $2"; [ "$2" = "$3" ] || exit 1; } check "no login " "$(curl -sk $R -o /dev/null -w "%{http_code}" "https://$H/manager/html")" 401 check "wrong password " "$(as "admin:not-the-password" | curl -K - -sk $R -o /dev/null -w "%{http_code}" "https://$H/manager/html")" 401 check "tomcat/tomcat " "$(as "tomcat:tomcat" | curl -K - -sk $R -o /dev/null -w "%{http_code}" "https://$H/manager/html")" 401 check "admin, web UI " "$(as "admin:$A" | curl -K - -sk $R -o /dev/null -w "%{http_code}" "https://$H/manager/html")" 200 check "admin, text API" "$(as "admin:$A" | curl -K - -sk $R -o /dev/null -w "%{http_code}" "https://$H/manager/text/list")" 403 'no login -> HTTP 401 wrong password -> HTTP 401 tomcat/tomcat -> HTTP 401 admin, web UI -> HTTP 200 admin, text API -> HTTP 403After five wrong passwords in a row a user is locked out for five minutes.
Deploy an application
Upload a WAR file in the Manager's WAR file to deploy form, or deploy from a script or CI pipeline with the deployer user and the Manager text API. This example builds a tiny WAR, deploys it at /hello, fetches it, lists the applications and undeploys it again:
sudo bash -c ' F=/root/tomee-credentials.txt H=$(grep "^TOMEE_URL=" $F | sed -E "s#^TOMEE_URL=https://([^/]+)/\$#\1#") D=$(grep "^TOMEE_DEPLOYER_PASSWORD=" $F | cut -d= -f2-) R="--resolve $H:443:127.0.0.1" dep() { printf "user = \"deployer:%s\"\n" "$D" | curl -K - -sk $R "$@"; } W=$(mktemp -d); mkdir -p $W/hello echo "<h1>Hello from my first TomEE deployment</h1>" > $W/hello/index.html (cd $W/hello && python3 -c "import zipfile; z=zipfile.ZipFile(\"../hello.war\",\"w\"); z.write(\"index.html\"); z.close()") OUT=$(dep -T $W/hello.war "https://$H/manager/text/deploy?path=/hello"); echo "$OUT" case "$OUT" in "OK - Deployed"*) ;; *) exit 1 ;; esac sleep 2 PAGE=$(curl -sk $R "https://$H/hello/"); echo "$PAGE" case "$PAGE" in *"Hello from my first TomEE deployment"*) ;; *) exit 1 ;; esac dep "https://$H/manager/text/list" | sort OUT=$(dep "https://$H/manager/text/undeploy?path=/hello"); echo "$OUT" case "$OUT" in "OK - Undeployed"*) ;; *) exit 1 ;; esac rm -rf $W 'OK - Deployed application at context path [/hello] <h1>Hello from my first TomEE deployment</h1> /:running:0:ROOT /hello:running:0:hello /manager:running:3:manager /sample:running:7:sample OK - Listed applications for virtual host [localhost] OK - Undeployed application at context path [/hello]From your own machine the same API works over HTTPS (-k accepts the self-signed certificate):
curl -k -u deployer:<TOMEE_DEPLOYER_PASSWORD> -T ./myapp.war 'https://<public-ip>/manager/text/deploy?path=/myapp&update=true'You can also copy a WAR into /var/lib/tomee/webapps over SSH (sudo install -o tomee -g tomee -m 0640 myapp.war /var/lib/tomee/webapps/); TomEE deploys it automatically. Deploy your application as ROOT.war to replace the welcome page at /.
Security checks
The Manager users are stored as PBKDF2 hashes, never as plain passwords:
sudo grep '<user ' /var/lib/tomee/conf/tomcat-users.xml | sed -E 's/password="[^"]*"/password="<pbkdf2-hash>"/'<user username="admin" password="<pbkdf2-hash>" roles="manager-gui,manager-status"/> <user username="deployer" password="<pbkdf2-hash>" roles="manager-script"/>TomEE's server configuration disables the shutdown port, binds the only connector to loopback, takes the host name from configuration and trusts forwarded client addresses from the local nginx only. The Manager's RemoteCIDRValve answers only connections whose peer is that nginx:
sudo grep -E '<Server |<Connector |proxyName=|internalProxies=' /var/lib/tomee/conf/server.xml | sed 's/^ *//'
sudo grep -o 'usePeerAddress="true" allow="[^"]*"' /var/lib/tomee/webapps/manager/META-INF/context.xml<Server port="-1" shutdown="SHUTDOWN"> <Connector port="8080" address="127.0.0.1" protocol="HTTP/1.1" proxyName="${sac.tomee.host}" proxyPort="443" scheme="https" secure="true" /> internalProxies="127\.0\.0\.1" trustedProxies="" usePeerAddress="true" allow="127.0.0.1/32"nginx hands TomEE the configured host name and the real client address from the connection, never values a client sends:
grep -E 'proxy_set_header (Host|X-Forwarded-For|X-Real-IP)' /etc/nginx/tomee-proxy.confproxy_set_header Host $tomee_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $remote_addr;TomEE runs unprivileged in a sandbox, and neither TomEE nor nginx can reach the instance metadata service:
systemctl show tomee -p User -p ProtectSystem -p NoNewPrivileges -p IPAddressDeny
systemctl show nginx -p IPAddressDenyIPAddressDeny=169.254.169.254/32 168.63.129.16/32 fd00:ec2::254/128 User=tomee ProtectSystem=strict NoNewPrivileges=yes IPAddressDeny=169.254.169.254/32 168.63.129.16/32 fd00:ec2::254/128The certificate was generated on this instance and names its own addresses:
H=$(sudo grep '^TOMEE_URL=' /root/tomee-credentials.txt | sed -E 's#^TOMEE_URL=https://([^/]+)/$#\1#')
echo | openssl s_client -connect 127.0.0.1:443 -servername "$H" 2>/dev/null | openssl x509 -noout -subject -ext subjectAltNamesubject=CN = ec2-<public-ip-dashed>.<region>.compute.amazonaws.com, O = sac X509v3 Subject Alternative Name: DNS:ec2-<public-ip-dashed>.<region>.compute.amazonaws.com, IP Address:<public-ip>, IP Address:<private-ip>, IP Address:127.0.0.1, DNS:localhostnginx also refuses, with HTTP 403, a state-changing request to the Manager whose Origin is another website (browsers resend Basic Auth credentials on cross-site requests), and a WebSocket upgrade from another website to any application.
Your own DNS name and certificate
Point a DNS record at the instance, then set the name TomEE is served as. This mints a new self-signed certificate for the name, pins nginx and TomEE to it, updates the URLs in /root/tomee-credentials.txt, restarts TomEE and keeps the name across reboots:
sudo tomee-set-hostname <your-domain>To use a certificate from your own certificate authority instead, copy it over the generated pair and reload nginx (running tomee-set-hostname again replaces it with a self-signed one):
sudo install -m 0644 <your-domain>.crt /etc/ssl/tomee/tomee.crt
sudo install -m 0600 <your-domain>.key /etc/ssl/tomee/tomee.key
sudo nginx -t && sudo systemctl reload nginxPasswords, configuration and tuning
Set a new password for a Manager user with tomee-set-password. It stores the PBKDF2 hash, records the new password in /root/tomee-credentials.txt and restarts TomEE. With --generate it creates a random one:
sudo tomee-set-password admin --generateRun sudo tomee-set-password deployer (without --generate) to type a password of your own (at least 16 characters) at the prompt; it never takes a password on the command line, so it stays out of your shell history and the system logs.
TomEE's configuration lives in /var/lib/tomee/conf (server.xml, tomee.xml, system.properties, context.xml). Resources such as data sources go in tomee.xml or your application's WEB-INF/resources.xml. The JVM options are set in the systemd unit; to change them (for example a fixed heap), add an override and restart:
sudo systemctl edit tomee [Service] Environment="CATALINA_OPTS=-server -Xms1g -Xmx4g -Djava.net.preferIPv4Stack=true -Djava.awt.headless=true" sudo systemctl restart tomeeUploads through nginx are limited to 512 MB per request by client_max_body_size in /etc/nginx/sites-available/sac-tomee; change it there and reload nginx.
Server components
Component Path
TomEE release (read only) /opt/tomee
Configuration, applications, logs /var/lib/tomee (conf, webapps, logs) on the data volume
Service unit /etc/systemd/system/tomee.service
nginx site /etc/nginx/sites-available/sac-tomee
Certificate and key /etc/ssl/tomee/
First boot script /usr/local/sbin/apache-tomee-firstboot.sh
Host name and password helpers /usr/local/sbin/tomee-set-hostname, /usr/local/sbin/tomee-set-password
Credentials /root/tomee-credentials.txt (mode 0600)
sudo JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64 /opt/tomee/bin/version.sh 2>/dev/null | grep -E 'Server (version|number)|JVM Version'
sudo journalctl -u tomee --no-pager -o cat | grep 'Server startup in' | tail -1Server version: Apache Tomcat/10.1.60 Server number: 10.1.60.0 JVM Version: 17.0.20.1+1-1-24.04-Ubuntu 20-Oct-2026 01:31:27.996 INFO [main] jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke Server startup in [1684] millisecondsversion.sh reads only the Tomcat jar, so it shows the 10.1.60 patch release; the running server reports itself as Apache Tomcat (TomEE)/10.1.60 (10.2.0) (see the sample application). TomEE's own logs are in /var/lib/tomee/logs (catalina.<date>.log, localhost_access_log.<date>.txt); nginx logs to /var/log/nginx.
Security recommendations
- Allow ports 22, 80 and 443 in the security group from your own networks only, and consider limiting
/managerto your own addresses as well (anallow/denylist in the/managerlocation of the nginx site).
- Keep the per-instance passwords secret, and rotate them if they may have been shared (Step 10).
- Use your own DNS name and a certificate your clients trust (Step 9).
- Keep the operating system patched; unattended security upgrades are on, and
sudo apt-get update && sudo apt-get upgradeapplies everything else. Watch the TomEE security page for new releases of the 10.x line.
- Snapshot the
/var/lib/tomeedata volume to back up your applications and configuration.
Support and licensing
Apache TomEE is open source under the Apache License 2.0; there is no per-user or per-CPU licence fee. The AMI price covers SecureAnyCloud's hardening, first-boot setup and support. Apache TomEE, Apache Tomcat and Apache are trademarks of the Apache Software Foundation; SecureAnyCloud is not affiliated with or endorsed by the Apache Software Foundation.
- Email: sales@secureanycloud.com
- Website: https://secureanycloud.com
- Support: 24/7, with a 24-hour response SLA
Deploy on AWS
Launch Apache TomEE 10.2.0 on Ubuntu 24.04 on AWS with 24/7 support from SecureAnyCloud.
View on Marketplace
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com