Apache TomEE on AWS User Guide
Overview
Apache TomEE is the Jakarta EE application server from the Apache Tomcat project. The "plus" distribution this image ships bundles the full enterprise stack - JAX-RS, JPA, EJB, CDI, JMS, JSF, JTA and Bean Validation - on top of Tomcat 10.1, so you can run a complete Jakarta EE application without assembling the server yourself.
The cloudimg image installs Apache TomEE on OpenJDK 17, runs it as a systemd service bound to loopback behind an nginx reverse proxy on port 80, keeps application deployments, configuration and logs on a dedicated EBS data volume, and generates a unique Manager password on the first boot of every instance. Backed by 24/7 cloudimg support.
What is included:
- Apache TomEE (plus distribution) installed to
/opt/tomee, downloaded from the official Apache release and verified against its published SHA-512 checksum - OpenJDK 17 headless JVM, with heap sizing applied through the systemd unit
- nginx reverse proxy on port 80, plus an unauthenticated
/healthendpoint for load balancer and Auto Scaling health checks - A dedicated EBS data volume mounted at
/var/lib/tomeeholdingCATALINA_BASE- your deployed applications, configuration, logs, work and temp directories - The TomEE Manager and host-manager consoles, authenticated and restricted to the local reverse proxy
- A deployed sample application at
/sample/so the Manager shows a running deployment immediately - A per instance administrator password generated on first boot and written to a root only file

Architecture and Security Model
Understanding how the pieces fit together makes the rest of this guide straightforward.
- TomEE listens on
127.0.0.1:8080only. The HTTP connector inserver.xmlcarries an explicitaddress="127.0.0.1", so the application server is never directly reachable from the network, whatever your security group allows. - nginx on port 80 is the single public entry point. It proxies everything to TomEE on loopback and serves
/healthitself as a static 200, so a health check never depends on the JVM having finished starting. - The Manager and host-manager consoles require HTTP Basic authentication against
conf/tomcat-users.xml, and are additionally guarded by aRemoteAddrValvethat accepts only127.0.0.1and::1. Because requests arrive at TomEE from nginx on loopback, the consoles work through the proxy but cannot be reached by any other route. - No credential ships in the image. The captured AMI contains a roles only
tomcat-users.xmlwith no<user>element at all. The administrator account is created on the first boot of each instance with a freshly generated password. CATALINA_BASEis on its own EBS volume at/var/lib/tomee, so your deployments, configuration and logs are independently resizable, snapshottable and separate from the operating system disk.
Prerequisites
Before you deploy this image you need:
- An Amazon Web Services account where you can launch EC2 instances
- IAM permissions to launch instances, create security groups, and subscribe to AWS Marketplace products
- An EC2 key pair in the target Region for SSH access to the instance
- A VPC and subnet in the target Region, with a security group allowing inbound port 22 from your management network and inbound port 80 (and later 443) from the networks that will reach your applications
- The AWS CLI version 2 installed locally if you plan to deploy from the command line
Step 1: Launch the Instance from the AWS Marketplace
Sign in to the AWS Management Console, open the EC2 service, and select Launch instance. Under Application and OS Images choose AWS Marketplace AMIs and search for Apache TomEE. Select the cloudimg listing and choose Select, then Continue on the subscription summary.
Complete the launch wizard:
- Name - give the instance a name you will recognise, for example
tomee-prod-1. - Instance type -
m5.largeis the recommended type. TomEE is a JVM application server; give it at least 2 vCPU and 8 GiB of memory for anything beyond evaluation. - Key pair - select your existing EC2 key pair. You need it for SSH.
- Network settings - choose your VPC and subnet. Create or select a security group allowing inbound TCP 22 from your management network and inbound TCP 80 from the networks your users will come from.
- Configure storage - the image defines a 30 GiB root volume and a 30 GiB data volume for
/var/lib/tomee. Increase the data volume now if you expect large deployments; it can also be grown later.
Choose Launch instance and wait for the instance to reach the Running state with 2/2 status checks passed.
Step 2: Launch from the AWS CLI
If you prefer the command line, the equivalent launch looks like the following. Run this on your own machine, substituting your own AMI ID, key pair, subnet and security group.
aws ec2 run-instances \
--image-id <ami-id> \
--instance-type m5.large \
--key-name <your-key-pair> \
--subnet-id <your-subnet-id> \
--security-group-ids <your-security-group-id> \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=tomee-prod-1}]'
Step 3: Connect to Your Instance
Connect over SSH using the key pair you selected at launch. The login user depends on the operating system variant you launched.
Connecting to your instance
| Operating system variant | SSH login user |
|---|---|
| Ubuntu 24.04 LTS | ubuntu |
Run the following on your own machine, replacing the key path and address:
ssh -i /path/to/your-key.pem ubuntu@<public-ip>
If the connection is refused, confirm the instance has finished its status checks and that your security group allows port 22 from your current address.
Step 4: Retrieve the Per Instance Administrator Password
On the first boot of every instance a one-shot systemd service generates a unique administrator password, writes it into the TomEE user realm, and records it in /root/tomee-credentials.txt with mode 0600. Read it with sudo:
sudo cat /root/tomee-credentials.txt
The file records the console URLs, the administrator username, and the generated password. You can confirm which fields it holds without displaying the password itself:
sudo grep -oE '^[A-Z_]+=' /root/tomee-credentials.txt
TOMEE_URL=
TOMEE_MANAGER_URL=
TOMEE_HOST_MANAGER_URL=
TOMEE_ADMIN_USERNAME=
TOMEE_ADMIN_PASSWORD=
The administrator username is admin. Store the password in your password manager - it is unique to this instance and is not recorded anywhere else.
Step 5: Verify the Server Is Healthy
Confirm both services are running:
systemctl is-active tomee.service nginx.service
active
active
Check the version of TomEE and the JVM it is running on:
sudo CATALINA_HOME=/opt/tomee /opt/tomee/bin/version.sh 2>/dev/null | grep -E 'Server version|JVM Version'
Server version: Apache Tomcat (TomEE)/10.1.57 (10.2.0)
JVM Version: 17.0.19+10-1-24.04.2-Ubuntu
Confirm the listener layout - nginx is public on port 80, while TomEE is bound to loopback on 8080:
ss -tln | grep -E ':80 |:8080 '
LISTEN 0 511 0.0.0.0:80 0.0.0.0:*
LISTEN 0 511 [::]:80 [::]:*
LISTEN 0 100 [::ffff:127.0.0.1]:8080 *:*
The [::ffff:127.0.0.1]:8080 form is the IPv4 mapped IPv6 address the JVM binds when listening on loopback. What matters is that 8080 is not bound to 0.0.0.0.
Confirm CATALINA_BASE is on its own EBS volume:
findmnt -no SOURCE,TARGET,FSTYPE,SIZE /var/lib/tomee
/dev/nvme1n1 /var/lib/tomee ext4 29.4G
Check the unauthenticated health endpoint that nginx serves directly:
curl -s -o /dev/null -w 'health endpoint: HTTP %{http_code}\n' http://127.0.0.1/health
health endpoint: HTTP 200
This endpoint is the one to point an Elastic Load Balancing target group or an Auto Scaling health check at.
Step 6: Confirm Manager Authentication
The Manager console rejects an incorrect password and accepts the generated one. You can prove both directions without ever printing the password:
PW=$(sudo grep '^TOMEE_ADMIN_PASSWORD=' /root/tomee-credentials.txt | cut -d= -f2-)
curl -s -o /dev/null -w 'wrong password -> HTTP %{http_code}\n' -u 'admin:not-the-real-password' http://127.0.0.1/manager/text/list
curl -s -o /dev/null -w 'correct password -> HTTP %{http_code}\n' -u "admin:$PW" http://127.0.0.1/manager/text/list
wrong password -> HTTP 401
correct password -> HTTP 200
List the deployed applications through the Manager text interface:
PW=$(sudo grep '^TOMEE_ADMIN_PASSWORD=' /root/tomee-credentials.txt | cut -d= -f2-)
curl -s -u "admin:$PW" http://127.0.0.1/manager/text/list
OK - Listed applications for virtual host [localhost]
/sample:running:3:sample
/host-manager:running:0:host-manager
/:running:0:ROOT
/manager:running:2:manager
/docs:running:0:docs
Step 7: Sign In to the TomEE Manager
Open http://<public-ip>/manager/html in a browser. Your browser will prompt for HTTP Basic credentials: sign in as admin with the password from Step 4.

The Applications table lists every deployed context and lets you start, stop, reload and undeploy each one. The Deploy panel below it uploads a WAR directly from your browser. Server Information at the foot of the page confirms the TomEE version, the JVM, and the operating system.
The Server Status page shows live JVM memory and connector throughput:

The JVM memory line reflects the heap settings the image applies through the systemd unit - an initial heap of 256 MiB and a maximum of 1024 MiB. Step 10 explains how to change them for a larger instance type.
The host-manager console at http://<public-ip>/host-manager/html manages virtual hosts and uses the same administrator credentials.
Step 8: The Bundled Sample Application
The image deploys a small Jakarta EE sample application so the Manager shows a real running deployment out of the box. Browse to http://<public-ip>/sample/:

The page renders values evaluated at request time by the server - the current time, the JVM version and the reported server identity - which confirms JSP compilation and request handling are working end to end.
The sample application is there for verification only. Once you have deployed your own application you can remove it from the Manager's Applications table with Undeploy on the /sample row, or from the command line:
PW=$(sudo grep '^TOMEE_ADMIN_PASSWORD=' /root/tomee-credentials.txt | cut -d= -f2-)
curl -s -u "admin:$PW" 'http://127.0.0.1/manager/text/undeploy?path=/sample'
Step 9: Deploy Your Own Application
There are three ways to deploy a WAR. All of them are shown here as commands to run when you are ready - they change the state of your server, so they are not part of the verification steps above.
From the Manager console. Open http://<public-ip>/manager/html, scroll to WAR file to deploy, choose your .war file and select Deploy. The application appears in the Applications table and is served at the context path matching the WAR filename.
By copying the WAR onto the data volume. TomEE watches CATALINA_BASE/webapps and deploys anything dropped there. Copy the file up from your machine and move it into place:
scp -i /path/to/your-key.pem myapp.war ubuntu@<public-ip>:/tmp/
sudo install -o tomee -g tomee -m 0640 /tmp/myapp.war /var/lib/tomee/webapps/myapp.war
The application is deployed within a few seconds and served at http://<public-ip>/myapp/.
Through the Manager text interface, which is convenient from a CI pipeline:
PW=$(sudo grep '^TOMEE_ADMIN_PASSWORD=' /root/tomee-credentials.txt | cut -d= -f2-)
curl -s -u "admin:$PW" -T myapp.war 'http://127.0.0.1/manager/text/deploy?path=/myapp&update=true'
Because /var/lib/tomee/webapps lives on the dedicated data volume, deployed applications survive an instance stop and start, and are captured by an EBS snapshot of that volume alone.
Step 10: Tuning the JVM Heap
The service unit sets an initial heap of 256 MiB and a maximum of 1024 MiB, which suits evaluation and small workloads. On a larger instance type you will want to raise the ceiling. Edit the unit and restart:
sudo systemctl edit --full tomee.service
Change the CATALINA_OPTS line, keeping the surrounding double quotes exactly as they are:
Environment="CATALINA_OPTS=-server -Xms1024m -Xmx4096m -Djava.security.egd=file:/dev/urandom"
The quotes are load bearing. A systemd Environment= value that is not quoted is split on whitespace and only the first token survives, which would silently discard every heap flag while the service still appears to start normally.
Then apply the change:
sudo systemctl daemon-reload
sudo systemctl restart tomee.service
Confirm the new values on the Server Status page, or from the running process.
Step 11: Configuring HTTPS
The image serves plain HTTP on port 80. Put TLS in front of it before exposing the server to production traffic. The simplest route is a certificate from Let's Encrypt terminated at nginx, which requires a DNS name pointing at the instance and inbound port 443 open in the security group.
sudo apt-get update
sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d tomee.example.com
Certbot edits the nginx site in place, adds the TLS listener and installs a renewal timer. The proxy configuration to TomEE is untouched, so no TomEE change is needed.
If you terminate TLS at an Application Load Balancer instead, point the target group at port 80 with /health as the health check path, and leave the instance configuration as it is.
Step 12: Backup and Maintenance
Back up the data volume. Everything that is yours - deployed applications, configuration and logs - lives on the EBS volume mounted at /var/lib/tomee. A snapshot of that volume captures the full application state:
aws ec2 create-snapshot \
--volume-id <data-volume-id> \
--description "TomEE application data $(date -u +%F)"
Schedule these with Amazon Data Lifecycle Manager or AWS Backup for policy driven retention.
Apply operating system updates on your normal patching schedule:
sudo apt-get update
sudo apt-get -y upgrade
Read the server logs. TomEE writes to CATALINA_BASE/logs on the data volume, and the systemd journal carries the service level record:
sudo tail -n 100 /var/lib/tomee/logs/catalina.out
sudo journalctl -u tomee.service -n 100 --no-pager
Troubleshooting
A browser request returns 502 Bad Gateway. nginx is running but TomEE is not yet accepting connections. This is normal for a few seconds after a boot or a restart while the JVM starts. If it persists, check systemctl is-active tomee.service and read the service log.
The Manager returns 401 repeatedly. The password is per instance and is read from /root/tomee-credentials.txt on the instance you are actually connected to. Confirm you are reading the file on the right host, and that the username is admin.
The Manager returns 403. The consoles accept requests only from the loopback reverse proxy. Reach them through http://<public-ip>/manager/html on port 80, not by connecting to port 8080 directly.
A deployed application does not appear. Confirm the WAR is owned by the tomee user and readable, and that it landed in /var/lib/tomee/webapps. Check the service log for a deployment failure.
Port 8080 is not reachable from outside. That is by design. TomEE binds loopback only; all external access is through nginx on port 80.
Support
This image is published and supported by cloudimg. Our engineers provide 24/7 technical support by email and live chat, covering deployment, Apache TomEE and Tomcat configuration, application deployment troubleshooting, JVM and heap tuning, and reverse proxy and TLS setup.
Apache, Apache TomEE, TomEE, Apache Tomcat and Tomcat are trademarks of The Apache Software Foundation. All product and company names are trademarks or registered trademarks of their respective holders. Use of them does not imply any affiliation with or endorsement by them.