Installs the tomcat application server. https://tomcat.apache.org
Go to file
Andrea Dell'Amico 5e26279d0a
Fix all ansible-lint violations (109 → 0 failures).
- handlers/main.yml: convert free-form service calls to ansible.builtin.service with dict syntax; capitalize handler names to satisfy name[casing] (Tomcat restart, Enable tomcat, Disable tomcat, Tomcat letsencrypt copy*).
- tasks/main.yml: add FQCN (ansible.builtin.import_tasks) and name: to every import statement, fixing fqcn[action-core] and name[missing].
- tasks/access_log.yml, tomcat-admin.yml, tomcat-jmx.yml, tomcat-log4j-logging.yml, tomcat-logger-logging.yml, not_pgsql_jdbc.yml, pgsql_jdbc.yml: convert all free-form module calls to FQCN + dict syntax.
- tasks/tomcat-javamelody.yml: use community.general.maven_artifact with dict syntax; fix block indentation; split long lines.
- tasks/tomcat-pkgs.yml, tomcat-certs.yml: rename notify references to 'Tomcat restart' to match the renamed handler.
- meta/main.yml: quote min_ansible_version to fix schema[meta]; add role_name/namespace; remove trailing blank line.
- tests/test.yml: add play name; fix role reference; add trailing newline.
- vars/main.yml: fix stale comment; add trailing newline.
- defaults/main.yml: use >- block scalar for tomcat_proxy_opts andtomcat_catalina_shared_loader_path to fix yaml[line-length].
2026-03-06 18:52:36 +01:00
defaults Fix all ansible-lint violations (109 → 0 failures). 2026-03-06 18:52:36 +01:00
files Customization of catalina.properties. 2022-07-05 15:29:37 +02:00
handlers Fix all ansible-lint violations (109 → 0 failures). 2026-03-06 18:52:36 +01:00
meta Fix all ansible-lint violations (109 → 0 failures). 2026-03-06 18:52:36 +01:00
tasks Fix all ansible-lint violations (109 → 0 failures). 2026-03-06 18:52:36 +01:00
templates Add SSL/TLS certificate support and improve connector configuration. 2026-03-06 18:28:10 +01:00
tests Fix all ansible-lint violations (109 → 0 failures). 2026-03-06 18:52:36 +01:00
vars Fix all ansible-lint violations (109 → 0 failures). 2026-03-06 18:52:36 +01:00
.gitignore Fix all ansible-lint violations (109 → 0 failures). 2026-03-06 18:52:36 +01:00
LICENSE Initial commit 2020-05-28 15:03:22 +02:00
README.md Add SSL/TLS certificate support and improve connector configuration. 2026-03-06 18:28:10 +01:00

README.md

Tomcat

Installs and configures Apache Tomcat on Debian/Ubuntu systems. The Tomcat version is selected automatically based on the Ubuntu release (7 on Trusty, 8 on Bionic, 10 on Noble) or can be pinned explicitly. The role manages packages, the systemd unit, JVM options, server.xml, catalina.properties, logging, the optional Manager web application, JMX, and SSL/TLS certificates issued by either Lets Encrypt (via the letsencrypt-acme-sh-client role) or a private CA (via the mkcert role).

Requirements

  • The openjdk role must be applied before this one (declared as a dependency in meta/main.yml).
  • Ubuntu Focal (20.04), Jammy (22.04), or Noble (24.04). Bionic (18.04) is supported but reaches EOL.
  • For SSL with Lets Encrypt: the letsencrypt-acme-sh-client role.
  • For SSL with a private CA: the mkcert role.

Role Variables

Tomcat version

The version is detected automatically from the Ubuntu release. Override with:

# Pin a specific major version regardless of the OS release
tomcat_fixed_version: 9

Packages and service

# Package installation state (present / latest / absent)
tomcat_pkg_state: present

# Whether the service should be enabled and started
tomcat_service_enabled: true

# Use a custom systemd unit (true on Ubuntu >= 18.04)
tomcat_use_systemd_unit: true

# Enable systemd security hardening directives in the unit file
tomcat_systemd_security_enhanced: false

JVM options

tomcat_min_heap_size: 2048m
tomcat_heap_size: '{{ tomcat_min_heap_size }}'
tomcat_permgen_size: 512m           # used only with JDK <= 7
tomcat_file_encoding: 'UTF-8'

# Base JVM memory flags (override to add extra flags)
tomcat_java_opts: "-Xms{{ tomcat_min_heap_size }} -Xmx{{ tomcat_heap_size }}"

# Extra options appended to JAVA_OPTS
tomcat_other_java_opts: ""

# Outbound HTTP/HTTPS proxy for Tomcat's own connections
tomcat_proxy_enabled: false
tomcat_proxy_http_host: 'localhost'
tomcat_proxy_http_port: '3128'
tomcat_proxy_https_host: '{{ tomcat_proxy_http_host }}'
tomcat_proxy_https_port: '{{ tomcat_proxy_http_port }}'

Connector thread pool (Executor)

A single named <Executor> (tomcatThreadPool) is shared by all active connectors (HTTP, HTTPS, or both).

tomcat_max_threads: 200
tomcat_executor_min_spare_threads: 10

# Maximum length of the pending-connection queue when all threads are busy
tomcat_executor_accept_count: 100

# Maximum depth of the internal executor work queue
# tomcat_max_queue_size: 32767    # default shown; set in host/group vars if needed

HTTP connector

# Enable the plain HTTP connector
tomcat_http_enabled: true

# Automatically disable the HTTP connector when the HTTPS connector is active.
# Set to true to force HTTPS-only operation.
tomcat_http_disable_when_ssl: false

tomcat_http_port: 8080
tomcat_http_address: 0.0.0.0

# Timeouts (milliseconds)
tomcat_connection_timeout: 20000    # time to receive the request URI line
tomcat_upload_timeout: 300000       # maximum time for a request body upload

# Request size limits
tomcat_max_post_size: 1000000       # bytes; -1 to disable
tomcat_max_http_header_size: 8192   # bytes

AJP connector

Disabled by default. Enable when Tomcat is fronted by Apache httpd via mod_jk or mod_proxy_ajp.

tomcat_ajp_enabled: false
tomcat_ajp_port: 8009
tomcat_ajp_address: 127.0.0.1

Reverse proxy integration

When Tomcat sits behind a reverse proxy, set these so that Tomcat reports the correct public hostname and port in redirects and request.getServerName().

tomcat_reverse_proxy_name_enabled: false
tomcat_reverse_proxy_name: '{{ ansible_fqdn }}'
tomcat_reverse_proxy_port: '{{ http_port | default(80) }}'

# Set to true to log the real client IP from X-Forwarded-For instead of the
# proxy address. The RemoteIpValve is activated automatically when AJP is on.
tomcat_direct_access: false

Paths

These are derived from tomcat_version and rarely need to be overridden.

tomcat_catalina_home_dir: '/usr/share/tomcat{{ tomcat_version }}'
tomcat_catalina_base_dir: '/var/lib/tomcat{{ tomcat_version }}'
tomcat_conf_dir:           '/etc/tomcat{{ tomcat_version }}'
tomcat_webapps_dir:        '{{ tomcat_catalina_base_dir }}/webapps'
tomcat_logdir:             '/var/log/tomcat{{ tomcat_version }}'
tomcat_tmp_dir:            '{{ tomcat_catalina_base_dir }}/tmp/tomcat'

# Override only if you need a custom CATALINA_TMPDIR
# catalina_tmp_directory: /some/other/path

tomcat_enable_catalina_shared_loader: true

Webapps

tomcat_webapps_autodeploy: false
tomcat_webapps_unpack: false

Shutdown port

A long-standing Tomcat bug can kill the process after ~50 days when the shutdown port is open. It is disabled by default.

tomcat_shutdown_port: -1            # -1 disables the shutdown port entirely
# tomcat_shutdown_port: 8005       # uncomment to re-enable
tomcat_shutdown_pwd: "<random>"     # generated via lookup('password', …)

Logging

tomcat_use_log4j: true
tomcat_install_the_log4j_properties: true
tomcat_log_level: INFO
tomcat_log_logger: CATALINA
tomcat_log_rotation_threshold: "ALL"
tomcat_log_max_file_size: "100MB"
tomcat_retain_old_logs: 30

tomcat_access_log_enabled: true
tomcat_access_log_rotation_freq: "daily"

# Forward logs to a Logstash / ELK stack
tomcat_send_to_logstash: false
tomcat_logstash_collector_host: logstash
tomcat_logstash_collector_socketappender_port: 4560
tomcat_logstash_collector_socketappender_reconndelay: 10000
# Use 'LOGSTASH' only to suppress local log files entirely
tomcat_logstash_logger: 'CATALINA, LOGSTASH'

Manager web application

tomcat_install_admin: false

tomcat_manager_gui_user_enabled: true
tomcat_manager_gui_user: guiadmin
# tomcat_manager_gui_pwd: <vault>

tomcat_manager_script_user_enabled: false
tomcat_manager_script_user: scriptadmin
# tomcat_manager_script_pwd: <vault>

tomcat_manager_jmx_user_enabled: false
tomcat_manager_jmx_user: jmxadmin
# tomcat_manager_jmx_pwd: <vault>

tomcat_manager_status_user_enabled: false
tomcat_manager_status_user: statusadmin
# tomcat_manager_status_pwd: <vault>

JMX

tomcat_jmx_enabled: false
tomcat_jmx_port: 8082
tomcat_jmx_auth_enabled: false
tomcat_jmx_use_ssl: false
tomcat_jmx_disable_additional_ports: true   # requires JDK >= 7.0.25
tomcat_jmx_localhost_only: false

# Required when tomcat_jmx_auth_enabled is true (store in vault)
# tomcat_jmx_monitorpass: <vault>
# tomcat_jmx_controlpass: <vault>

Remote debugging

tomcat_enable_remote_debugging: false
tomcat_remote_debugging_host: '0.0.0.0'
tomcat_remote_debugging_port: ':8100'

JavaMelody monitoring

tomcat_javamelody: false
tomcat_javamelody_version: 1.82.0

JDBC drivers

tomcat_install_jdbc: false
tomcat_install_pg_jdbc: '{{ tomcat_install_jdbc }}'   # PostgreSQL JDBC driver

SSL/TLS certificates

Setting tomcat_ssl_enabled: true does three things:

  1. Creates tomcat_ssl_cert_dir and copies the certificate and private key into it with permissions readable only by the tomcat group (0640, owned root:{{ tomcat_user }}).
  2. Adds an HTTPS connector to server.xml. On Tomcat 9+ this uses Http11NioProtocol with a nested <SSLHostConfig> element so that PEM files are read natively without APR. On Tomcat 7/8 it uses Http11AprProtocol with inline SSLCertificateFile/SSLCertificateKeyFile attributes.
  3. For Lets Encrypt: installs a renewal hook script at {{ tomcat_letsencrypt_hooks_dir }}/tomcat. The script is called by acme.sh (via /usr/local/bin/acme-services-hook) on every successful renewal. It compares each source file against its destination using cmp and restarts Tomcat only when at least one file has changed.

An Ansible handler listening on letsencrypt cert renewed is also provided. Any task in the same play can trigger a key copy and Tomcat restart by notifying that event:

- name: Install the certificate
  ...
  notify: letsencrypt cert renewed

Common SSL variables

tomcat_ssl_enabled: false

# Certificate source: 'letsencrypt' or 'mkcert'
tomcat_ssl_cert_source: letsencrypt

# Directory on the managed host where Tomcat reads the cert and key.
# Created with mode 0750, owned root:{{ tomcat_user }}.
tomcat_ssl_cert_dir: '{{ tomcat_conf_dir }}/ssl'

tomcat_ssl_port: 8443

# TLS protocol filter in Tomcat '+' syntax.
# Tomcat 9+ (SSLHostConfig):    TLSv1.2+TLSv1.3
# Tomcat 7/8 (APR SSLProtocol): TLSv1+TLSv1.1+TLSv1.2
tomcat_ssl_protocols: 'TLSv1.2+TLSv1.3'

tomcat_ssl_max_parameter_count: 1000

# Destination filenames inside tomcat_ssl_cert_dir
tomcat_ssl_fullchain_filename: 'fullchain.pem'
tomcat_ssl_key_filename: 'privkey.pem'

Lets Encrypt (letsencrypt-acme-sh-client role)

When tomcat_ssl_cert_source: letsencrypt, the role reads certificates from the directory installed by the letsencrypt-acme-sh-client role. tomcat_ssl_letsencrypt_cert_dir resolves automatically from letsencrypt_acme_sh_certificates_install_path when both roles run in the same play. The fallback value matches that roles documented default.

# Resolved from letsencrypt_acme_sh_certificates_install_path when available.
# Override only if the letsencrypt role is not part of the same play.
tomcat_ssl_letsencrypt_cert_dir: "/var/lib/acme/live/{{ ansible_fqdn }}"

# Source filenames as produced by the letsencrypt-acme-sh-client role
# (no file extension by design)
tomcat_ssl_letsencrypt_fullchain_src_filename: 'fullchain'
tomcat_ssl_letsencrypt_key_src_filename: 'privkey'

# Hook directory — resolved from letsencrypt_acme_services_scripts_dir when available.
tomcat_letsencrypt_hooks_dir: '/usr/lib/acme/hooks'

Private CA (mkcert role)

When tomcat_ssl_cert_source: mkcert, the role copies PEM files generated by the mkcert role. Point tomcat_ssl_mkcert_cert_dir at the directory containing the certificate files for the relevant domain.

tomcat_ssl_mkcert_domain: '{{ ansible_fqdn }}'
tomcat_ssl_mkcert_cert_dir: '/srv/mkcert-ca/certs/{{ tomcat_ssl_mkcert_domain }}'

# Source filenames in tomcat_ssl_mkcert_cert_dir
tomcat_ssl_mkcert_fullchain_src_filename: 'fullchain.pem'
tomcat_ssl_mkcert_key_src_filename: 'privkey.pem'

Example playbooks

HTTPS only with Lets Encrypt

- hosts: appservers
  roles:
    - openjdk
    - letsencrypt-acme-sh-client
    - tomcat
  vars:
    letsencrypt_acme_install: true
    letsencrypt_acme_sh_domains:
      - domain: '{{ ansible_fqdn }}'
        standalone: false

    tomcat_ssl_enabled: true
    tomcat_ssl_cert_source: letsencrypt
    tomcat_http_disable_when_ssl: true

HTTP and HTTPS with a private CA

- hosts: devservers
  roles:
    - openjdk
    - mkcert
    - tomcat
  vars:
    tomcat_ssl_enabled: true
    tomcat_ssl_cert_source: mkcert
    tomcat_ssl_mkcert_cert_dir: '/srv/mkcert-ca/certs/{{ ansible_fqdn }}'

HTTP only behind a reverse proxy

- hosts: appservers
  roles:
    - openjdk
    - tomcat
  vars:
    tomcat_reverse_proxy_name_enabled: true
    tomcat_ajp_enabled: true
    tomcat_http_enabled: false

Dependencies

  • openjdk role (declared in meta/main.yml), sourced from gitea-s2i2s.isti.cnr.it/ISTI-ansible-roles/ansible-role-openjdk.
  • letsencrypt-acme-sh-client role — optional, required when tomcat_ssl_cert_source: letsencrypt.
  • mkcert role — optional, required when tomcat_ssl_cert_source: mkcert.

License

EUPL-1.2

Author Information

Andrea DellAmico, andrea.dellamico@isti.cnr.it ISTI-CNR