Integrate Apache Tomcat 11 with Prometheus and Grafana monitoring for comprehensive performance tracking

Intermediate 45 min Apr 28, 2026 572 views
Ubuntu 24.04 Debian 12 AlmaLinux 9 Rocky Linux 9

Set up comprehensive monitoring for Apache Tomcat 11 using JMX Prometheus Java agent, configure Prometheus to scrape metrics, create detailed Grafana dashboards, and implement alerting rules for performance tracking and health monitoring.

Prerequisites

  • Root or sudo access
  • At least 4GB RAM
  • Java 11 or higher

What this solves

Apache Tomcat serves millions of Java applications but lacks built-in monitoring dashboards. This tutorial integrates Tomcat 11 with Prometheus and Grafana to provide real-time visibility into JVM memory usage, thread pools, request throughput, and application health. You'll get production-ready dashboards and alerts that help you identify performance bottlenecks before they impact users.

Step-by-step configuration

Install required packages

Update your system and install Java 11 or higher, which is required for Tomcat 11.

sudo apt update
sudo apt install -y openjdk-11-jdk wget curl
sudo dnf update -y
sudo dnf install -y java-11-openjdk-devel wget curl

Download and install Apache Tomcat 11

Create a dedicated tomcat user and download the latest Tomcat 11 release.

sudo useradd -r -m -U -d /opt/tomcat -s /bin/false tomcat
cd /tmp
wget https://archive.apache.org/dist/tomcat/tomcat-11/v11.0.0/bin/apache-tomcat-11.0.0.tar.gz
sudo tar xf apache-tomcat-11.0.0.tar.gz -C /opt/tomcat --strip-components=1
sudo chown -R tomcat:tomcat /opt/tomcat
sudo chmod +x /opt/tomcat/bin/*.sh

Download JMX Prometheus Java agent

The JMX Prometheus Java agent exports JVM metrics in Prometheus format via HTTP endpoint.

cd /opt/tomcat/lib
sudo wget https://repo1.maven.org/maven2/io/prometheus/jmx/jmx_prometheus_javaagent/0.20.0/jmx_prometheus_javaagent-0.20.0.jar
sudo chown tomcat:tomcat jmx_prometheus_javaagent-0.20.0.jar

Create JMX exporter configuration

Configure which JMX metrics to export and how to transform them for Prometheus.

rules:
  # JVM Memory metrics
  - pattern: 'java.lang
sudo chown tomcat:tomcat /opt/tomcat/conf/jmx-exporter-config.yml

Configure Tomcat to use JMX exporter

Add the JMX Prometheus agent to Tomcat's JVM startup options.

#!/bin/bash
CATALINA_OPTS="$CATALINA_OPTS -javaagent:/opt/tomcat/lib/jmx_prometheus_javaagent-0.20.0.jar=8088:/opt/tomcat/conf/jmx-exporter-config.yml"
JAVA_OPTS="$JAVA_OPTS -Xmx2048m -Xms1024m -XX:+UseG1GC"
sudo chown tomcat:tomcat /opt/tomcat/bin/setenv.sh
sudo chmod +x /opt/tomcat/bin/setenv.sh

Create systemd service for Tomcat

Configure Tomcat to start automatically and run as the tomcat user.

[Unit]
Description=Apache Tomcat Web Application Container
After=network.target

[Service]
Type=forking
User=tomcat
Group=tomcat
Environment=JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64
Environment=CATALINA_PID=/opt/tomcat/temp/tomcat.pid
Environment=CATALINA_HOME=/opt/tomcat
Environment=CATALINA_BASE=/opt/tomcat
Environment="CATALINA_OPTS=-Xms512M -Xmx1024M -server -XX:+UseParallelGC"
Environment="JAVA_OPTS=-Djava.awt.headless=true -Djava.security.egd=file:/dev/./urandom"
ExecStart=/opt/tomcat/bin/startup.sh
ExecStop=/opt/tomcat/bin/shutdown.sh
RestartSec=10
Restart=always

[Install]
WantedBy=multi-user.target

Start Tomcat and verify JMX exporter

Enable and start the Tomcat service, then verify the metrics endpoint is working.

sudo systemctl daemon-reload
sudo systemctl enable --now tomcat
sudo systemctl status tomcat
curl http://localhost:8088/metrics | head -20
Note: If you're running this on a remote server, you may need to configure your firewall to allow access to port 8088 for Prometheus scraping.

Install Prometheus

Download and install Prometheus to scrape metrics from Tomcat.

sudo useradd -r -M -s /bin/false prometheus
sudo mkdir -p /etc/prometheus /var/lib/prometheus
cd /tmp
wget https://github.com/prometheus/prometheus/releases/download/v2.48.0/prometheus-2.48.0.linux-amd64.tar.gz
tar xf prometheus-2.48.0.linux-amd64.tar.gz
sudo cp prometheus-2.48.0.linux-amd64/prometheus /usr/local/bin/
sudo cp prometheus-2.48.0.linux-amd64/promtool /usr/local/bin/
sudo chown prometheus:prometheus /usr/local/bin/prometheus /usr/local/bin/promtool
sudo chown -R prometheus:prometheus /etc/prometheus /var/lib/prometheus

Configure Prometheus to scrape Tomcat metrics

Set up Prometheus configuration to collect metrics from the Tomcat JMX exporter.

global:
  scrape_interval: 15s
  evaluation_interval: 15s

rule_files:
  - "tomcat_alerts.yml"

alerting:
  alertmanagers:
    - static_configs:
        - targets: []

scrape_configs:
  - job_name: 'prometheus'
    static_configs:
      - targets: ['localhost:9090']
  
  - job_name: 'tomcat'
    static_configs:
      - targets: ['localhost:8088']
    scrape_interval: 5s
    metrics_path: '/metrics'
    scrape_timeout: 5s
sudo chown prometheus:prometheus /etc/prometheus/prometheus.yml

Create Prometheus alerting rules

Define alert conditions for Tomcat performance and health monitoring.

groups:
  - name: tomcat_alerts
    rules:
      - alert: TomcatDown
        expr: up{job="tomcat"} == 0
        for: 1m
        labels:
          severity: critical
        annotations:
          summary: "Tomcat instance is down"
          description: "Tomcat metrics endpoint has been unreachable for more than 1 minute."
          
      - alert: TomcatHighMemoryUsage
        expr: (jvm_memory_heap_bytes{area="heap"} / jvm_memory_heap_max_bytes{area="heap"}) * 100 > 85
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "Tomcat high heap memory usage"
          description: "Tomcat heap memory usage is above 85% for more than 5 minutes. Current usage: {{ $value }}%"
          
      - alert: TomcatHighThreadUsage
        expr: (tomcat_threads_busy / tomcat_threads_max) * 100 > 80
        for: 3m
        labels:
          severity: warning
        annotations:
          summary: "Tomcat high thread pool usage"
          description: "Tomcat thread pool usage is above 80% for connector {{ $labels.connector }}. Current usage: {{ $value }}%"
          
      - alert: TomcatHighResponseTime
        expr: rate(tomcat_processing_time_milliseconds_total[5m]) / rate(tomcat_requests_total[5m]) > 5000
        for: 2m
        labels:
          severity: warning
        annotations:
          summary: "Tomcat high response time"
          description: "Average response time for connector {{ $labels.connector }} is above 5 seconds: {{ $value }}ms"
          
      - alert: TomcatHighGCTime
        expr: rate(jvm_gc_collection_time_milliseconds_total[5m]) > 100
        for: 3m
        labels:
          severity: warning
        annotations:
          summary: "Tomcat high garbage collection time"
          description: "Garbage collection time rate is high for collector {{ $labels.gc }}: {{ $value }}ms/sec"
          
      - alert: TomcatNoActiveThreads
        expr: tomcat_threads_current - tomcat_threads_busy < 5
        for: 1m
        labels:
          severity: critical
        annotations:
          summary: "Tomcat low available threads"
          description: "Less than 5 threads available for connector {{ $labels.connector }}"
          
      - alert: TomcatHighErrorRate
        expr: rate(tomcat_servlet_errors_total[5m]) / rate(tomcat_requests_total[5m]) * 100 > 5
        for: 2m
        labels:
          severity: warning
        annotations:
          summary: "Tomcat high error rate"
          description: "Error rate is above 5% for the last 5 minutes: {{ $value }}%"
sudo chown prometheus:prometheus /etc/prometheus/tomcat_alerts.yml

Create Prometheus systemd service

Configure Prometheus to start automatically on system boot.

[Unit]
Description=Prometheus
Wants=network-online.target
After=network-online.target

[Service]
User=prometheus
Group=prometheus
Type=simple
ExecStart=/usr/local/bin/prometheus \
  --config.file /etc/prometheus/prometheus.yml \
  --storage.tsdb.path /var/lib/prometheus/ \
  --web.console.templates=/etc/prometheus/consoles \
  --web.console.libraries=/etc/prometheus/console_libraries \
  --web.listen-address=0.0.0.0:9090 \
  --web.enable-lifecycle
Restart=always

[Install]
WantedBy=multi-user.target

Start Prometheus service

Enable and start Prometheus, then verify it's collecting Tomcat metrics.

sudo systemctl daemon-reload
sudo systemctl enable --now prometheus
sudo systemctl status prometheus

Install Grafana

Add Grafana repository and install the latest version for dashboard visualization.

sudo apt install -y software-properties-common
wget -q -O - https://packages.grafana.com/gpg.key | sudo apt-key add -
echo "deb https://packages.grafana.com/oss/deb stable main" | sudo tee /etc/apt/sources.list.d/grafana.list
sudo apt update
sudo apt install -y grafana
sudo tee /etc/yum.repos.d/grafana.repo <

Configure Grafana datasource

Add Prometheus as a data source for Grafana dashboards.

apiVersion: 1

datasources:
  - name: Prometheus
    type: prometheus
    access: proxy
    url: http://localhost:9090
    isDefault: true
    editable: true

Create Tomcat dashboard configuration

Set up a comprehensive Grafana dashboard for Tomcat monitoring.

{
  "dashboard": {
    "id": null,
    "title": "Apache Tomcat Performance Dashboard",
    "tags": ["tomcat", "jvm", "performance"],
    "timezone": "browser",
    "panels": [
      {
        "id": 1,
        "title": "Tomcat Status",
        "type": "stat",
        "targets": [
          {
            "expr": "up{job=\"tomcat\"}",
            "legendFormat": "Status"
          }
        ],
        "fieldConfig": {
          "defaults": {
            "mappings": [
              {
                "options": {
                  "0": {"text": "DOWN", "color": "red"},
                  "1": {"text": "UP", "color": "green"}
                },
                "type": "value"
              }
            ],
            "thresholds": {
              "steps": [
                {"color": "red", "value": 0},
                {"color": "green", "value": 1}
              ]
            }
          }
        },
        "gridPos": {"h": 8, "w": 12, "x": 0, "y": 0}
      },
      {
        "id": 2,
        "title": "Heap Memory Usage",
        "type": "timeseries",
        "targets": [
          {
            "expr": "jvm_memory_heap_bytes{area=\"heap\"}",
            "legendFormat": "Used Heap Memory"
          },
          {
            "expr": "jvm_memory_heap_max_bytes{area=\"heap\"}",
            "legendFormat": "Max Heap Memory"
          }
        ],
        "fieldConfig": {
          "defaults": {
            "unit": "bytes",
            "custom": {
              "drawStyle": "line",
              "lineInterpolation": "linear",
              "barAlignment": 0,
              "lineWidth": 1,
              "fillOpacity": 10,
              "gradientMode": "none",
              "spanNulls": false,
              "insertNulls": false,
              "showPoints": "never",
              "pointSize": 5,
              "stacking": {"mode": "none", "group": "A"},
              "axisPlacement": "auto",
              "axisLabel": "",
              "axisColorMode": "text",
              "scaleDistribution": {"type": "linear"},
              "axisCenteredZero": false,
              "hideFrom": {"legend": false, "tooltip": false, "vis": false},
              "thresholdsStyle": {"mode": "off"}
            }
          }
        },
        "gridPos": {"h": 8, "w": 12, "x": 12, "y": 0}
      },
      {
        "id": 3,
        "title": "Thread Pool Usage",
        "type": "timeseries",
        "targets": [
          {
            "expr": "tomcat_threads_current",
            "legendFormat": "Current Threads - {{connector}}"
          },
          {
            "expr": "tomcat_threads_busy",
            "legendFormat": "Busy Threads - {{connector}}"
          },
          {
            "expr": "tomcat_threads_max",
            "legendFormat": "Max Threads - {{connector}}"
          }
        ],
        "gridPos": {"h": 8, "w": 12, "x": 0, "y": 8}
      },
      {
        "id": 4,
        "title": "Request Rate",
        "type": "timeseries",
        "targets": [
          {
            "expr": "rate(tomcat_requests_total[5m])",
            "legendFormat": "Requests/sec - {{connector}}"
          }
        ],
        "fieldConfig": {
          "defaults": {
            "unit": "reqps"
          }
        },
        "gridPos": {"h": 8, "w": 12, "x": 12, "y": 8}
      },
      {
        "id": 5,
        "title": "Average Response Time",
        "type": "timeseries",
        "targets": [
          {
            "expr": "rate(tomcat_processing_time_milliseconds_total[5m]) / rate(tomcat_requests_total[5m])",
            "legendFormat": "Avg Response Time - {{connector}}"
          }
        ],
        "fieldConfig": {
          "defaults": {
            "unit": "ms"
          }
        },
        "gridPos": {"h": 8, "w": 12, "x": 0, "y": 16}
      },
      {
        "id": 6,
        "title": "GC Activity",
        "type": "timeseries",
        "targets": [
          {
            "expr": "rate(jvm_gc_collection_time_milliseconds_total[5m])",
            "legendFormat": "GC Time/sec - {{gc}}"
          }
        ],
        "fieldConfig": {
          "defaults": {
            "unit": "ms"
          }
        },
        "gridPos": {"h": 8, "w": 12, "x": 12, "y": 16}
      }
    ],
    "time": {
      "from": "now-1h",
      "to": "now"
    },
    "refresh": "5s",
    "version": 1
  }
}

Enable dashboard provisioning

Configure Grafana to automatically load the Tomcat dashboard.

apiVersion: 1

providers:
  - name: 'tomcat-dashboards'
    orgId: 1
    folder: 'Tomcat'
    type: file
    disableDeletion: false
    updateIntervalSeconds: 10
    allowUiUpdates: true
    options:
      path: /etc/grafana/provisioning/dashboards

Start Grafana service

Enable and start Grafana to access the monitoring dashboard.

sudo systemctl enable --now grafana-server
sudo systemctl status grafana-server

Configure firewall rules

Allow access to Graf

Automated install script

Run this to automate the entire setup

Don't want to manage this yourself?

We handle infrastructure for businesses that depend on uptime. Fully managed, with one fixed contact who knows your setup.

You get one fixed contact who knows your setup

Rotterdam 07:22 · reachable in a message, no ticket form