Ansible

Install Apache Tomcat Using Ansible on Ubuntu / Rocky Linux / Debian

Deploying Tomcat by hand on every server gets old fast, especially when you’re managing multiple environments across different Linux distributions. This Ansible role handles the entire process: Java installation, Tomcat download and extraction, systemd service setup, JVM tuning, firewall rules, and Manager UI credentials. One playbook run, and Tomcat is ready to serve.

Original content from computingforgeeks.com - post 19555

Tested April 2026 on Ubuntu 24.04, Ubuntu 22.04, Debian 12, Rocky Linux 9, AlmaLinux 9, and Fedora 41 with Tomcat 9.0.117, 10.1.54, and 11.0.21

The role supports Tomcat 9, 10, and 11 with a single variable change. It includes a version map that auto-resolves the latest tested release for each major version, so you only need to set tomcat_major_version and the role picks the right archive. The full source is on GitHub. If you’re new to Ansible, start with our Ansible installation guide for RHEL and Debian systems.

Supported Platforms

The role has been tested on these operating systems with all three Tomcat major versions.

OSVersionsStatus
Ubuntu24.04, 22.04Tested
Debian12Tested
Rocky Linux9Tested
AlmaLinux9Tested
Fedora41, 40Tested
RHEL9Compatible

Prerequisites

  • A control node with Ansible >= 2.15 installed
  • One or more target servers running a supported OS with SSH access
  • sudo or root privileges on target servers
  • Internet access on targets (to download the Tomcat archive from Apache)

Verify Ansible is installed on your control node.

ansible --version

The output confirms the installed version.

ansible [core 2.18.4]
  config file = /etc/ansible/ansible.cfg
  configured module search path = ['/root/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python3/dist-packages/ansible
  ansible collection location = /root/.ansible/collections:/usr/share/ansible/collections
  executable location = /usr/bin/ansible
  python version = 3.12.3 (main, Feb  4 2025, 14:48:35) [GCC 13.3.0] (/usr/bin/python3)
  jinja version = 3.1.2
  libyaml = True

Clone the Ansible Role

Clone the repository and install the required Ansible collection.

git clone https://github.com/jmutai/tomcat-ansible.git
cd tomcat-ansible
ansible-galaxy collection install -r requirements.yml

The ansible.posix collection is needed for firewall management on RHEL-based systems.

Configure Inventory

Edit the hosts file and add your target server IP addresses.

vim hosts

Add one server per line under the [tomcat_nodes] group.

[tomcat_nodes]
10.0.1.50
10.0.1.51

If your SSH user differs from root, specify it in the inventory.

[tomcat_nodes]
10.0.1.50 ansible_user=ubuntu
10.0.1.51 ansible_user=rocky

Role Variables

The role ships with sensible defaults. You only need to override what you want to change. All variables use the tomcat_ prefix.

VariableDefaultDescription
tomcat_major_version"10"Major version: 9, 10, or 11
tomcat_version"10.1.54"Full version (auto-resolved from major)
tomcat_install_dir"/opt/tomcat"Installation directory
tomcat_java_version"17"Java version: 11, 17, or 21
tomcat_port8080HTTP connector port
tomcat_manager_user"manager"Manager UI username
tomcat_manager_password"changeme"Manager UI password
tomcat_admin_user"admin"Admin UI username
tomcat_admin_password"changeme"Admin UI password
tomcat_jvm_memory_min"512M"Initial JVM heap size
tomcat_jvm_memory_max"1024M"Maximum JVM heap size
tomcat_manager_allowed_ips".*"Regex for allowed Manager IPs
tomcat_configure_firewalltrueOpen port in firewalld (RedHat only)

The role includes a built-in version map, so you only need to set tomcat_major_version. The correct release is resolved automatically: 9 maps to 9.0.117, 10 to 10.1.54, and 11 to 11.0.21. Check for newer releases at the Apache Tomcat download page.

Deploy Tomcat with Ansible

The simplest deployment uses the defaults, which installs Tomcat 10 with Java 17.

ansible-playbook tomcat-setup.yml \
  -e "tomcat_manager_password=Str0ngP@ss1 tomcat_admin_password=Str0ngP@ss2"

For Tomcat 9, just change the major version. The role auto-resolves to 9.0.117.

ansible-playbook tomcat-setup.yml \
  -e "tomcat_major_version=9 tomcat_manager_password=Str0ngP@ss1 tomcat_admin_password=Str0ngP@ss2"

Tomcat 11 works the same way.

ansible-playbook tomcat-setup.yml \
  -e "tomcat_major_version=11 tomcat_manager_password=Str0ngP@ss1 tomcat_admin_password=Str0ngP@ss2"

If you need password authentication instead of SSH keys, add --ask-pass. For sudo users, add --ask-become-pass.

ansible-playbook tomcat-setup.yml --ask-pass --ask-become-pass \
  -e "tomcat_manager_password=Str0ngP@ss1 tomcat_admin_password=Str0ngP@ss2"

Playbook Execution Output

A successful run on Ubuntu 24.04 with Tomcat 10 produces output like this.

PLAY [Tomcat deployment playbook] **********************************************

TASK [Gathering Facts] *********************************************************
ok: [10.0.1.50]

TASK [tomcat : Preflight checks] ***********************************************
included: roles/tomcat/tasks/preflight.yml for 10.0.1.50

TASK [tomcat : Validate tomcat_major_version] **********************************
ok: [10.0.1.50] => All assertions passed

TASK [tomcat : Validate tomcat_java_version] ***********************************
ok: [10.0.1.50] => All assertions passed

TASK [tomcat : Validate OS family] *********************************************
ok: [10.0.1.50] => All assertions passed

TASK [tomcat : Install Tomcat] *************************************************
included: roles/tomcat/tasks/install.yml for 10.0.1.50

TASK [tomcat : Install Java (Debian family)] ***********************************
changed: [10.0.1.50]

TASK [tomcat : Create tomcat user] *********************************************
changed: [10.0.1.50]

TASK [tomcat : Download Tomcat archive] ****************************************
changed: [10.0.1.50]

TASK [tomcat : Extract Tomcat archive] *****************************************
changed: [10.0.1.50]

TASK [tomcat : Configure Tomcat] ***********************************************
included: roles/tomcat/tasks/configure.yml for 10.0.1.50

TASK [tomcat : Deploy systemd service file] ************************************
changed: [10.0.1.50]

TASK [tomcat : Start and enable Tomcat service] ********************************
changed: [10.0.1.50]

TASK [tomcat : Verify Tomcat installation] *************************************
included: roles/tomcat/tasks/verify.yml for 10.0.1.50

TASK [tomcat : Wait for Tomcat port to be available] ***************************
ok: [10.0.1.50]

TASK [tomcat : Verify Tomcat is responding] ************************************
ok: [10.0.1.50]

TASK [tomcat : Display Tomcat status] ******************************************
ok: [10.0.1.50] => {
    "msg": "Tomcat 10.1.54 is running on port 8080"
}

PLAY RECAP *********************************************************************
10.0.1.50                  : ok=27   changed=14   unreachable=0    failed=0    skipped=5    rescued=0    ignored=0

The role includes a built-in health check that waits for Tomcat to start and verifies an HTTP 200 response. No need to manually check if the service came up.

Verify Tomcat Installation

Open your browser and navigate to http://10.0.1.50:8080. The Tomcat welcome page confirms a successful deployment.

The Manager UI is available at http://10.0.1.50:8080/manager/html using the credentials you passed during deployment. The Host Manager is at http://10.0.1.50:8080/host-manager/html.

Secure Credentials with Ansible Vault

Passing passwords on the command line works for testing, but production deployments should use Ansible Vault to encrypt sensitive data. Create a vault file with your credentials.

ansible-vault create vault.yml

Add the password variables inside the vault file.

tomcat_manager_password: YourSecureManagerPass
tomcat_admin_password: YourSecureAdminPass

Then reference it when running the playbook.

ansible-playbook tomcat-setup.yml -e @vault.yml --ask-vault-pass

JVM Memory Tuning

The role deploys a setenv.sh script to the Tomcat bin/ directory, which is the recommended way to configure JVM options. By default, Tomcat gets 512M initial heap and 1024M max heap. For production workloads, adjust these values.

ansible-playbook tomcat-setup.yml \
  -e "tomcat_jvm_memory_min=1024M tomcat_jvm_memory_max=2048M" \
  -e @vault.yml --ask-vault-pass

For full control over JVM flags, override tomcat_catalina_opts directly.

ansible-playbook tomcat-setup.yml \
  -e 'tomcat_catalina_opts="-Xms1024M -Xmx2048M -XX:+UseG1GC -Djava.awt.headless=true"' \
  -e @vault.yml --ask-vault-pass

Restrict Manager UI Access

By default, the Manager and Host Manager apps are accessible from any IP. In production, restrict access to specific addresses using the tomcat_manager_allowed_ips variable. This sets a RemoteAddrValve in the Tomcat context configuration.

ansible-playbook tomcat-setup.yml \
  -e 'tomcat_manager_allowed_ips=127\.0\.0\.1|10\.0\.1\..*' \
  -e @vault.yml --ask-vault-pass

This restricts access to localhost and the 10.0.1.0/24 subnet. Anyone else gets a 403 Forbidden when trying to access the Manager interface.

What the Role Does

The role is structured into five task phases that run in sequence. Understanding each phase helps when troubleshooting or customizing the deployment. You can learn more about Ansible role structure and best practices if you’re building your own roles.

  • Preflight validates inputs (supported OS, valid Java and Tomcat versions) and warns if passwords are still set to the default “changeme”
  • Install installs Java, creates a dedicated tomcat system user, downloads the archive from Apache, and extracts it to the configured directory
  • Configure deploys the systemd service file, setenv.sh for JVM tuning, tomcat-users.xml with Manager credentials, and context.xml with IP restrictions
  • Firewall (RedHat only) installs and enables firewalld, then opens the Tomcat port
  • Verify waits for the port to open and sends an HTTP request to confirm Tomcat returns a 200 response

The role is idempotent. Running it a second time produces zero changes if nothing has been modified, which makes it safe to include in regular configuration management runs.

Using Group Variables for Multiple Environments

For managing multiple environments (dev, staging, production), use Ansible group variables instead of command line overrides. Create a file at group_vars/tomcat_nodes/main.yml with your settings. The Ansible playbook tutorial covers variable precedence in detail.

mkdir -p group_vars/tomcat_nodes

Add your environment-specific configuration.

vim group_vars/tomcat_nodes/main.yml
tomcat_major_version: "11"
tomcat_java_version: "21"
tomcat_jvm_memory_max: "2048M"
tomcat_manager_allowed_ips: '127\.0\.0\.1'

Now a plain ansible-playbook tomcat-setup.yml picks up all the settings automatically.

Tomcat Version Comparison

Each Tomcat major version targets a different Jakarta EE (or Java EE) specification level. Pick the version that matches your application’s requirements.

FeatureTomcat 9Tomcat 10Tomcat 11
Servlet Spec4.0 (javax.*)6.0 (jakarta.*)6.1 (jakarta.*)
Java MinimumJava 8Java 11Java 17
Namespacejavax.servletjakarta.servletjakarta.servlet
Default Version9.0.11710.1.5411.0.21
Use CaseLegacy javax appsJakarta EE 9/10 appsLatest Jakarta EE 11

Tomcat 9 is the right choice if your application uses the javax.servlet namespace. Tomcat 10 and 11 require migrated applications using jakarta.servlet. This is the most common migration gotcha when upgrading.

Related Articles

AlmaLinux Install Dagger on Rocky Linux 10 / AlmaLinux 10 Automation How To Create AWS EFS Filesystem With CloudFormation Automation Migrate from Earthly to Dagger: Complete Guide Ansible How To Install Ansible AWX on CentOS 7 / RHEL 7

Leave a Comment

Press ESC to close