Notes on Setting up Tomcat 9 on Java 11 (AdoptOpenJDK) on Ubuntu 16.04 Alongside Tomcat 8 on Java 8
Table of Contents
- 1. Overview
- 2. Getting Java 11 from AdoptOpenJDK.net
- 3. DONE Installing via
apt
(aptitude) - 4. Download
- 5. Create
tomcat
user and group - 6. Expand archive (if expanding/configuring manually, as opposed to using
apt get
) - 7. DONE 2025:
systemd
HOWTOs - 8. Configure Tomcat to start
- 9. Configure init
- 10. DONE
server.xml
- 11. Configure AWS firewall rules to allow connections on ports 8080, 8443
- 12. DONE Allow acccess to /docs url
- 13. DONE Require authentication/authorization to access manager GUIs
- 14. DONE Allow access to manager GUIs from remote address
- 15. DONE Allow tomcat to open ports < 1024
- 16. DONE Configure Tomcat to run on port 80 (or 90, say)
- 17. Make tomcat run at system startup (and shut down gracefully on halt/reboot)
- 18. Configuring admin user able to access manager-gui and admin-gui (for deploying apps like JspWiki)
- 19. Operations
1 Overview
As the title says, these are my notes on installing Tomcat 9 on Ubuntu Linux 4.4.0-89-generic (Xenial, 16.04LTS), running on Java 11 from AdoptOpenJDK to run in parallel with an existing Tomcat 8 installation running on Java 8.
1.1 2023 Update
In 2023, I did this on a new VM running Ubuntu 22.04 LTS, and tomcat 9 was the only version on the system. Search this file for "2023".
1.2 2025 Restart (Update)
In January of 2025, I restarted this update effort, since I didn't make any progress in 2023, and things have probably changed since then.
Versions, as of
:Description | Version (LTS) | Requires | Comments |
Latest stable Ubuntu | 24.04 | See Amazon instance setup. | |
Latest JDK (Corretto) | 21 | Ubuntu is a Debian-based OS. | |
Latest Tomcat | 9 | JDK 17+ | See notes below. |
Latest JspWiki | 2.12.2 | Tomcat 9 |
- Note that, due to the transition from javaee.* to jakartaee.*, which requires source code changes, Tomcat 9 will be maintained indefinitely to keep up with later versions of Tomcat, at least for security if not for features.
- JspWiki only runs on Tomcat9 (at the moment).
- Tomcat9 is not in the standard reposity for Ubuntu 24.04, so requires manual install/config.
Copying files downloaded to your local machine to the remote VM:
gcm scp # Yields: # CommandType Name Version Source # ----------- ---- ------- ------ # Application scp.exe 9.5.2.1 C:\Windows\System32\OpenSSH\scp.exe cd C:\Users\John\Downloads\Linux\Tomcat9 scp -i C:\Users\John\Downloads\john-lusk.amazon-aws-keypair.20250106.pem * ubuntu@3.84.44.43:/home/ubuntu/Downloads
Verifying SHA-512 checksums:
cd ~/Downloads/ sha512sum * if [ "xxx" == "xxx" ]; then echo "match"; else echo "NO MATCH"; fi
2 Getting Java 11 from AdoptOpenJDK.net
Follow the instructions at AdoptOpenJDK.net, except for Java whatever-version-you-want. You're basically adding a new repository to apt, and from then on, it should be pretty easy to keep it up to date (if the EC2 instance doesn't automaticaly keep itself up to date).
2023: Installed Amazon Corretto via apt
.
3 DONE Installing via apt
(aptitude)
When installing via apt
, tomcat9 seems to land in:
- /etc/tomcat9
- /etc/default/tomcat9 (shell variables)
- /etc/logrotate.d/tomcat9
- /lib/systemd/system/tomcat9.service (Q: so, no need for explicit systemd config shown below? A: apparently not.
The only thing I had to do was modify
/etc/default/tomcat9
to setJAVA_HOME=/usr
after installing Corretto.) – Looks like this one sets- CATALINA_HOME to
/usr/share/tomcat9
, but - CATALINA_BASE to
/var/lib/tomcat9
.
- CATALINA_HOME to
- /usr/share/tomcat9
- /usr/share/tomcat9-root
- /var/lib/tomcat9
4 Download
(2025: See 2025 Restart (Update) to learn about copying files from your local machine using scp
. That way you can
download with a nice web browser on your local machine and you don't have to install elinks
or whatever on the VM.)
Not available via apt
. At least, not without adding another repository from somewhere
(Apache?).
Downloaded with wget:
ip-172-30-0-82$ pwd /home/ubuntu/Downloads ip-172-30-0-82$ wget https://downloads.apache.org/tomcat/tomcat-9/v9.0.31/bin/apache-tomcat-9.0.31.tar.gz
5 Create tomcat
user and group
sudo groupadd tomcat sudo useradd -s /bin/false -g tomcat -d /opt/tomcat tomcat # Choose the right home directory.
Note that home directory should probably be updated if you ever update tomcat, since it'll probably be version-specific.
I chown
'd the /opt
directories (tomcat + docs) to tomcat:tomcat
recursively. I guess we'll see in future
whether some of that stuff should have remained owned by root.
6 Expand archive (if expanding/configuring manually, as opposed to using apt get
)
Into /usr/share/apache-tomcat-9.0.31
2025: note that Ubuntu 24.04 seems to come (from Amazon, at least) with an empty /opt
directory, so that's what I
used.
7 DONE 2025: systemd
HOWTOs
See:
For the above instruction, I modified systemd service unit pathnames to match actual pathnames on the system.
(service unit: /etc/systemd/system/tomcat.service
.)
I also modified the path for security to /dev/urandom
, because that entry seems to exist on my system.
/usr/lib/jvm/java-17-amazon-corretto
, maybe?)
JAVA_HOME
to /usr
instead of /usr/bin
.
A key thing is the JAVA_OPTS
environment variable, which was royally screwed up in the above hostinger page:
Environment='JAVA_OPTS=-Djava.awt.headless=true -Djava.security.egd=file:///dev/urandom'
7.1 Final service unit:
[Unit] Description=Apache Tomcat Web Application Container After=network.target [Service] Type=forking Environment=JAVA_HOME=/usr Environment=CATALINA_PID=/opt/apache-tomcat-9.0.98/temp/tomcat.pid Environment=CATALINA_Home=/opt/apache-tomcat-9.0.98 Environment=CATALINA_BASE=/opt/apache-tomcat-9.0.98 Environment='CATALINA_OPTS=-Xms512M -Xmx1024M -server -XX:+UseParallelGC' Environment='JAVA_OPTS=-Djava.awt.headless=true -Djava.security.egd=file:///dev/urandom' ExecStart=/opt/apache-tomcat-9.0.98/bin/startup.sh ExecStop=/opt/apache-tomcat-9.0.98/bin/shutdown.sh User=tomcat Group=tomcat UMask=0007 RestartSec=10 Restart=always [Install] WantedBy=multi-user.target
7.2 Useful systemctl
commands
sudo systemctl daemon-reload # Reload whatever edits you made to the service unit sudo systemctl enable tomcat # Cause service to start on boot sudo systemctl restart tomcat # Restart the thing systemctl status tomcat.service # Check status (duh) journalctl -xeu tomcat.service # Moar detailed status and startup log.
7.3 DONE Configure to start automatically after reboot
Turns out this is just one command away. See https://www.digitalocean.com/community/tutorials/how-to-use-systemctl-to-manage-systemd-services-and-units
sudo systemctl enable tomcat
Which results in output:
Created symlink /etc/systemd/system/multi-user.target.wants/tomcat.service → /etc/systemd/system/tomcat.service.
8 Configure Tomcat to start
Not super hard. Your basic goal is to set the environment variables JAVA_HOME
and
CATALINA_HOME
(at least) before firing up the supplied startup scripts. I basically copied my
tomcat8 /etc/default
script to a tomcat9 version and tinkered a bit.
2023: This section and the next (init) are replaced by a SystemD service file (2023: systemd).
9 Configure init
9.1 Prior to 2023
See https://www.rosehosting.com/blog/install-tomcat-9-on-an-ubuntu-16-04-vps/
Or https://javabirder.wordpress.com/2016/02/18/install-tomcat-9-ubuntu/
sudo useradd -r tomcat9 --shell /bin/false
(-r
is "system user", i.e., no home directory, low uid, etc.)
I used the javabirder example, but the rosehosting /etc/default/tomcat9
(I just copied the
tomcat8
version, changed user/group names in the script, pointed to a different JAVA_HOME
(AdoptOpenJDK 11), set a smaller max heap size (since I already have a JVM running on this
machine for my wiki).
I also had to change one port setting in the server.xml
config from 8005 to 8095, because I
guess it was colliding with my wiki tomcat instance.
It works (on port 8080), when testing locally (via elinks http://localhost:8080
), but I can't
hit that port from outside the machine, I guess because of the firewall rules.
9.2 DONE 2023: systemd
- CLOSING NOTE
This is done, but tomcat is failing b/c it can't open server.xml.
See also 2025: systemd
HOWTOs.
NOTE: This is not needed when you use apt
to install tomcat. See above (Installing via apt
(aptitude)).
Two different websites say to create a SystemD service unit (/etc/systemd/system/tomcat.service
):
- https://vegastack.com/tutorials/how-to-install-tomcat-9-on-ubuntu-22-04/
- https://www.rosehosting.com/blog/how-to-install-tomcat-on-ubuntu-22-04/
- https://www.hostinger.com/tutorials/how-to-install-tomcat-on-ubuntu/ (2025)
I think the following is required to allow tomcat to write the pid file to /usr/share/tomcat9/tomcat.pid
:
sudo chown -R tomcat:tomcat /usr/share/tomcat9 /usr/share/tomcat9-root
If you have problems, look at $CATALINA_OUT
(should be /var/log/tomcat/catalina.out
).
Service unit looks like this:
[Unit] Description=Apache Tomcat After=network.target [Service] Type=forking User=tomcat Group=tomcat Environment=JAVA_HOME=/usr Environment=CATALINA_PID=/usr/share/tomcat9/tomcat.pid Environment=CATALINA_HOME=/usr/share/tomcat9 Environment=CATALINA_OUT=/var/log/tomcat9/catalina.out # Environment="CATALINA_OPTS=-Xms512M -Xmx1024M -server -XX:+UseParallelGC" ExecStart=/usr/share/tomcat9/bin/startup.sh ExecStop=/usr/share/tomcat9/bin/shutdown.sh ExecReload=/bin/kill $MAINPID RemainAfterExit=yes [Install] WantedBy=multi-user.target
Currently stuck on server.xml
, but that's expected at this point.
10 DONE server.xml
- CLOSING NOTE
Purged everything and re-installed viaapt
, paying attention to where everything went. After that, it turns out we don't need a systemd service unit (at least, not one we have to create ourselves) and we don't (yet) need to worry aboutserver.xml
, since it seems to have been configured properly already. See the section above on installing withapt
.
Now that we've gotten the thing to start we need to it to not fail. It's looking for server.xml
with path
/usr/share/tomcat9/conf/server.xml
, because CATALINA_HOME
(configured for systemd) is /usr/share/tomcat9
.
11 Configure AWS firewall rules to allow connections on ports 8080, 8443
Looks I'll need to create a new security group that allows those ports, and then apply that group to the EC2 instance in question.
So: AWS Console | EC2 | Network & Security | Security Groups
Looks like there's something called "quicklaunch-1" that has what we want (plus another port, 9990, for whatever reason – is that a common experimental port?). Unfortunately, I can't attach it to the existing network interface for my instance. I guess I'd have to create a new network interface, but then I worry that my IP address would change and drive dyndns nuts (my DNS provider, dyn.com)
So, I just looked at my EC2 instance to see what networking security group was currently configured, and it turns out I can edit that group on the fly, and it works.
12 DONE Allow acccess to /docs url
/docs
is, by default, only accessible from localhost.
The access-denied error pages returned when I tried to access /docs
in the browser were actually helpful.
- Allow access from the web at large:
- Edit
webapps/docs/META-INF/context.xml
and comment out theValve
element that filters on remote address.
- Edit
This worked without restarting anything. I guess Tomcat is monitoring these files for changes.
13 DONE Require authentication/authorization to access manager GUIs
Looks like I need to enable access to the manager app, according to Google's AI Does not appear correct or helpful.
- Configure user with manager-gui role. This role is built in, so I didn't need to add the role itself, just a user
having the role.
- Edit
conf/tomcat-users.xml
to add a user withmanager-gui
access.
- Edit
14 DONE Allow access to manager GUIs from remote address
Update context.xml
for two webapps:
- manager (For Tomcat 9, requires
manager-gui
role assigned to user in/conf/tomcat-users.xml
) - host-manager (For Tomcat 9, requires
admin-gui
role assigned to user in/conf/tomcat-users.xml
)
Which, in my initial naive install, are in /usr/share/apache-tomcat-9.0.31/webapps
(in their
respective META-INF
subdirectories).
Just comment out the following lines in each:
<Valve className="org.apache.catalina.valves.RemoteAddrValve" allow="127\.\d+\.\d+\.\d+|::1|0:0:0:0:0:0:0:1" />
(Yikes! Regular expressions! Well, I guess it's better against IP addresses than domain names.)
15 DONE Allow tomcat to open ports < 1024
2025: Looks like authbind
is still the way to go. It lives in package authbind
, which you'll need to install.
Problems with authbind. Maybe put all tomcat users (8,9) in a "tomcat" group, and assign that group permission to open low-numbered ports via authbind?
Need to figure this out for tomcat9. Tomcat8 not having any trouble.
15.1 Add both tomcat users to a common group and give that group permission to open ports
I need a common group that multiple accounts can share, that allows opening of low-numbered ports.
Create a new group, say sysport
. (Could also have used www-data
, a pre-existing group, but, eh. No telling how
that's going to get repurposed by other software.)
sudo groupadd -r sysport # '-r' creates a "system" group, presumably with a lower gid. Not sure how # important that is, actually. sudo usermod tomcat9 -a -G sysport sudo usermod tomcat8 -a -G sysport
15.2 Authbind by group
/etc/authbind/byport/n
, where n is the port number (80, 90, 443, 453, 8080, 8443)
You can create empty files (with touch
), chgrp
them to sysport
, chmod
them to make them group-executable, and
you should be off to the races.
(Will also need to allow access in AWS, see Allow access to manager GUIs from remote address.)
15.2.1 Must use authbind
in invocation of process
CRUCIAL POINT that I somehow missed earlier: not only must you configure authbind properly, you need to actually run it. It does not automagically run somehow.
sudo -E -u tomcat9 /usr/bin/authbind --deep sh -x /usr/share/apache-tomcat-9.0.31/bin/startup.sh
(Note the invocation of authbind
.)
15.2.1.1 In systemd service unit:
ExecStart=/usr/bin/authbind --deep /opt/apache-tomcat-9.0.98/bin/startup.sh ExecStop=/usr/bin/authbind --deep /opt/apache-tomcat-9.0.98/bin/shutdown.sh
Note: no quotes and full pathnames.
15.3 Shutdown port 8095 conflict with Tomcat8
I have two instances of Tomcat running on my dinky AWS EC2 server, and both Tomcat8 and Tomcat9 want to grab port 8095 to receive the shutdown command (and others?).
It's not enough to change the config for the port in server.xml
(right? right?). Maybe I'm wrong, though?
This might shed some light: https://docs.openkm.com/kcenter/view/okm-6.4/configuring-tomcat-port.html
Documentation is here: https://tomcat.apache.org/tomcat-9.0-doc/config/server.html
I modified the shutdown port as follows:
<Server port="8105" shutdown="SHUTDOWN">
15.4 Might also need to configure an AJP port at some point in the future, but for now…
For now, we're good.
16 DONE Configure Tomcat to run on port 80 (or 90, say)
Need to adjust Connector setting in conf/server.xml
. Different port. Maybe copy the existing element, comment it
out, and make changes on the copy.
17 Make tomcat run at system startup (and shut down gracefully on halt/reboot)
So, there's this thing called update-rc.d
, and it writes all the /etc/rcN.d
scripts, where N
is a Unix "run level", given to the init
command when the system starts up or shuts down.
Run levels are documented here: https://en.wikipedia.org/wiki/Runlevel, but really, there's not a lot to know. The rc/N/.d scripts run when leaving a run level and entering a new one.
So, for example, when booting from power-off straight into run level 3 (normal multiuser w/no GUI), all the startup scripts in rc3.d will be run.
When running at run level 3, if somebody shuts down the system (run level 0), all the shutdown scripts in rc3.d will be run, and then all the startup scripts in rc0.d will be run (there probably won't be any).
It does get a little complicated when services depend on each other (like, say, a web server like Tomcat would depend on networking services being up; otherwise, what's the point?).
So, you can write an init script and put some header info in to specify when it should run, and
hand it off to update-rc.d
, which then populates the various rc/N/.d directories.
This is what's done in the javabirder site mentioned in Configure init, above.
Header info is as follows:
#!/bin/bash ### BEGIN INIT INFO # Provides: tomcat9 # Required-Start: $network # Required-Stop: $network # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Start/Stop Tomcat server ### END INIT INFO
So, we need the network to be up if the web server will be up (duh), and we'll run it all the multi-user run levels. (Not sure why we run it at runlevel 2, which is explicitly a "no network", but oh well. We'll probably never use that run level anyway.)
And, we'll stop it at run levels 0, 1, and 6.
When we run it, we get this:
ip-172-30-0-82# sudo update-rc.d -n tomcat9 defaults insserv: enable service ../init.d/tomcat9 -> /etc/init.d/../rc0.d/K01tomcat9 insserv: enable service ../init.d/tomcat9 -> /etc/init.d/../rc1.d/K01tomcat9 insserv: enable service ../init.d/tomcat9 -> /etc/init.d/../rc2.d/S01tomcat9 insserv: enable service ../init.d/tomcat9 -> /etc/init.d/../rc3.d/S01tomcat9 insserv: enable service ../init.d/tomcat9 -> /etc/init.d/../rc4.d/S01tomcat9 insserv: enable service ../init.d/tomcat9 -> /etc/init.d/../rc5.d/S01tomcat9 insserv: enable service ../init.d/tomcat9 -> /etc/init.d/../rc6.d/K01tomcat9 insserv: dryrun, not creating .depend.boot, .depend.start, and .depend.stop
…and when we reboot the system, Tomcat 9 comes back up! (Eventually.)
18 Configuring admin user able to access manager-gui and admin-gui (for deploying apps like JspWiki)
Need to edit /etc/tomcat9/tomcat-users.xml
to include a user having both the above roles.
19 Operations
19.1 Stop/start/restart tomcat
sudo /etc/init.d/tomcat[89] restart