Implement Tomcat application monitoring with JMX and Grafana dashboards

Intermediate 45 min May 28, 2026 106 views
Ubuntu 24.04 Debian 12 AlmaLinux 9 Rocky Linux 9

Set up comprehensive Apache Tomcat 10 monitoring using JMX metrics, Prometheus JMX Exporter, and Grafana dashboards to track application performance, memory usage, and request metrics in real-time.

Prerequisites

  • Root or sudo access
  • Java 11 or higher
  • 4GB RAM minimum
  • Port 8080, 9090, and 3000 available

What this solves

Monitoring Apache Tomcat applications effectively requires collecting JMX (Java Management Extensions) metrics and visualizing them in a centralized dashboard. This tutorial shows you how to configure Tomcat with JMX monitoring, deploy the Prometheus JMX Exporter, and create Grafana dashboards for tracking memory usage, request processing, thread pools, and application performance metrics.

Step-by-step installation

Install and configure Java Development Kit

Tomcat requires Java 11 or higher. Install OpenJDK and verify the installation.

sudo apt update
sudo apt install -y openjdk-11-jdk
sudo dnf install -y java-11-openjdk java-11-openjdk-devel
java -version
export JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64
echo 'export JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64' >> ~/.bashrc

Install Apache Tomcat 10

Download and install Tomcat 10 with proper user permissions and directory structure.

sudo useradd -r -m -U -d /opt/tomcat -s /bin/false tomcat
sudo wget -O /tmp/tomcat-10.1.25.tar.gz https://archive.apache.org/dist/tomcat/tomcat-10/v10.1.25/bin/apache-tomcat-10.1.25.tar.gz
sudo tar -xzf /tmp/tomcat-10.1.25.tar.gz -C /opt/tomcat --strip-components=1
sudo chown -R tomcat:tomcat /opt/tomcat
sudo chmod +x /opt/tomcat/bin/*.sh

Configure Tomcat JMX monitoring

Enable JMX remote monitoring by configuring authentication and SSL settings for secure metrics collection.



  
  
  
  
  
jmx-user readonly
monitor readwrite
jmx-user jmx_password_456
monitor monitor_password_789
sudo chown tomcat:tomcat /opt/tomcat/conf/jmxremote.*
sudo chmod 600 /opt/tomcat/conf/jmxremote.password
sudo chmod 644 /opt/tomcat/conf/jmxremote.access

Configure Tomcat startup with JMX options

Set JMX system properties to enable remote monitoring on port 9999 with authentication.

#!/bin/bash
JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote"
JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote.port=9999"
JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote.rmi.port=9999"
JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote.ssl=false"
JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote.authenticate=true"
JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote.password.file=/opt/tomcat/conf/jmxremote.password"
JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote.access.file=/opt/tomcat/conf/jmxremote.access"
JAVA_OPTS="$JAVA_OPTS -Djava.rmi.server.hostname=127.0.0.1"
JAVA_OPTS="$JAVA_OPTS -Xms512m -Xmx2048m"
sudo chmod +x /opt/tomcat/bin/setenv.sh
sudo chown tomcat:tomcat /opt/tomcat/bin/setenv.sh

Create Tomcat systemd service

Configure Tomcat to run as a system service with proper resource limits and monitoring capabilities.

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

[Service]
Type=forking
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 -Xmx2048M -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

User=tomcat
Group=tomcat
UMask=0007
RestartSec=10
Restart=always

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable tomcat
sudo systemctl start tomcat

Install Prometheus JMX Exporter

Download and configure the Prometheus JMX Exporter to collect Tomcat metrics and expose them for Prometheus scraping.

sudo mkdir -p /opt/prometheus
cd /opt/prometheus
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 /opt/prometheus/jmx_prometheus_javaagent-0.20.0.jar
rules:
  # Tomcat metrics
  - pattern: 'Catalina<>(.+):'
    name: tomcat_$3_total
    labels:
      port: "$1"
      protocol: "$2"
    help: Tomcat global $3
    type: COUNTER
  - pattern: 'Catalina[-a-zA-Z0-9+&@#/%=~_|]), name=([-a-zA-Z0-9+/$%~_-|!.]), J2EEApplication=none, J2EEServer=none><>(requestCount|maxTime|processingTime|errorCount):'
    name: tomcat_servlet_$3_total
    labels:
      module: "$1"
      servlet: "$2"
    help: Tomcat servlet $3 total
    type: COUNTER
  - pattern: 'Catalina<>(.+):'
    name: tomcat_threads_$3_total
    labels:
      port: "$1"
      protocol: "$2"
    help: Tomcat $3
    type: GAUGE
  - pattern: 'Catalina[-a-zA-Z0-9+&@#/%=~_|]), context=([-a-zA-Z0-9+/$%~_-|!.])><>(processingTime|sessionCounter|rejectedSessions|expiredSessions):'
    name: tomcat_session_$3_total
    labels:
      host: "$1"
      context: "$2"
    help: Tomcat session $3 total
  # JVM metrics
  - pattern: 'java.lang<>HeapMemoryUsage.(.+):'
    name: jvm_memory_heap_$1
    type: GAUGE
    help: JVM heap memory usage
  - pattern: 'java.lang<>NonHeapMemoryUsage.(.+):'
    name: jvm_memory_nonheap_$1
    type: GAUGE
    help: JVM non-heap memory usage
  - pattern: 'java.lang<>(.+):'
    name: jvm_gc_$2
    labels:
      collector: "$1"
    type: GAUGE
    help: JVM garbage collection
sudo chown tomcat:tomcat /opt/prometheus/tomcat-config.yaml

Update Tomcat configuration for JMX Exporter

Modify the Tomcat service to include the JMX Exporter Java agent for automatic metrics collection.

#!/bin/bash
JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote"
JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote.port=9999"
JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote.rmi.port=9999"
JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote.ssl=false"
JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote.authenticate=true"
JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote.password.file=/opt/tomcat/conf/jmxremote.password"
JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote.access.file=/opt/tomcat/conf/jmxremote.access"
JAVA_OPTS="$JAVA_OPTS -Djava.rmi.server.hostname=127.0.0.1"
JAVA_OPTS="$JAVA_OPTS -javaagent:/opt/prometheus/jmx_prometheus_javaagent-0.20.0.jar=8080:/opt/prometheus/tomcat-config.yaml"
JAVA_OPTS="$JAVA_OPTS -Xms512m -Xmx2048m"
sudo systemctl restart tomcat
sudo systemctl status tomcat

Install Prometheus

Install Prometheus to collect metrics from the JMX Exporter endpoint running on port 8080.

sudo apt install -y prometheus
sudo dnf install -y prometheus2
global:
  scrape_interval: 15s
  evaluation_interval: 15s

rule_files:
  # - "first_rules.yml"
  # - "second_rules.yml"

scrape_configs:
  - job_name: 'prometheus'
    static_configs:
      - targets: ['localhost:9090']

  - job_name: 'tomcat-jmx'
    static_configs:
      - targets: ['localhost:8080']
    scrape_interval: 10s
    metrics_path: /metrics
    params:
      format: [prometheus]
sudo systemctl restart prometheus
sudo systemctl enable prometheus

Install and configure Grafana

Install Grafana for creating monitoring dashboards and visualization of Tomcat metrics.

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 -a /etc/apt/sources.list.d/grafana.list
sudo apt update
sudo apt install -y grafana
sudo dnf install -y https://dl.grafana.com/oss/release/grafana-10.2.2-1.x86_64.rpm
sudo systemctl enable grafana-server
sudo systemctl start grafana-server

Configure Grafana data source

Add Prometheus as a data source in Grafana to enable querying Tomcat metrics for dashboard creation.

curl -X POST \
  http://admin:admin@localhost:3000/api/datasources \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "Prometheus",
    "type": "prometheus",
    "url": "http://localhost:9090",
    "access": "proxy",
    "basicAuth": false,
    "isDefault": true
  }'

Create Tomcat monitoring dashboard

Import a comprehensive Tomcat dashboard that displays JVM memory, thread pools, request metrics, and garbage collection statistics.

{
  "dashboard": {
    "id": null,
    "title": "Tomcat JMX Monitoring",
    "tags": ["tomcat", "jmx", "monitoring"],
    "timezone": "browser",
    "panels": [
      {
        "id": 1,
        "title": "JVM Heap Memory Usage",
        "type": "stat",
        "targets": [
          {
            "expr": "jvm_memory_heap_used / jvm_memory_heap_max * 100",
            "legendFormat": "Heap Usage %"
          }
        ],
        "gridPos": {"h": 8, "w": 6, "x": 0, "y": 0}
      },
      {
        "id": 2,
        "title": "Active Threads",
        "type": "graph",
        "targets": [
          {
            "expr": "tomcat_threads_currentThreadCount_total",
            "legendFormat": "Current Threads"
          },
          {
            "expr": "tomcat_threads_currentThreadsBusy_total",
            "legendFormat": "Busy Threads"
          }
        ],
        "gridPos": {"h": 8, "w": 12, "x": 6, "y": 0}
      },
      {
        "id": 3,
        "title": "Request Processing Time",
        "type": "graph",
        "targets": [
          {
            "expr": "rate(tomcat_processingTime_total[5m])",
            "legendFormat": "Processing Time Rate"
          }
        ],
        "gridPos": {"h": 8, "w": 12, "x": 0, "y": 8}
      },
      {
        "id": 4,
        "title": "Request Count",
        "type": "stat",
        "targets": [
          {
            "expr": "tomcat_requestCount_total",
            "legendFormat": "Total Requests"
          }
        ],
        "gridPos": {"h": 8, "w": 6, "x": 12, "y": 8}
      }
    ],
    "time": {
      "from": "now-1h",
      "to": "now"
    },
    "refresh": "30s"
  }
}
curl -X POST \
  http://admin:admin@localhost:3000/api/dashboards/db \
  -H 'Content-Type: application/json' \
  -d @/tmp/tomcat-dashboard.json

Configure firewall access

Open the required ports for Tomcat, Prometheus, and Grafana services while maintaining security.

sudo ufw allow 8080/tcp comment 'Tomcat JMX Exporter'
sudo ufw allow 8009/tcp comment 'Tomcat AJP'
sudo ufw allow 9090/tcp comment 'Prometheus'
sudo ufw allow 3000/tcp comment 'Grafana'
sudo ufw reload
sudo firewall-cmd --permanent --add-port=8080/tcp
sudo firewall-cmd --permanent --add-port=8009/tcp
sudo firewall-cmd --permanent --add-port=9090/tcp
sudo firewall-cmd --permanent --add-port=3000/tcp
sudo firewall-cmd --reload

Verify your setup

Test the monitoring stack by checking service status and verifying metrics collection.

# Check Tomcat status and JMX metrics endpoint
sudo systemctl status tomcat
curl -s http://localhost:8080/metrics | head -20

Verify Prometheus is scraping Tomcat metrics

curl -s 'http://localhost:9090/api/v1/query?query=up{job="tomcat-jmx"}' | jq '.data.result[0].value[1]'

Check Grafana service

sudo systemctl status grafana-server

Test JMX direct connection

echo "Available MBeans:" | jconsole localhost:9999

Access the web interfaces:

  • Tomcat Manager: http://your-server-ip:8080/manager (admin/secure_password_123)
  • Prometheus: http://your-server-ip:9090
  • Grafana: http://your-server-ip:3000 (admin/admin)

Common issues

Symptom Cause Fix
JMX Exporter returns 404 Agent not loaded or wrong port Check /opt/tomcat/logs/catalina.out for startup errors
Prometheus shows target down Firewall blocking or service not running sudo systemctl restart tomcat and check port 8080
No metrics in Grafana Data source misconfigured Verify Prometheus URL in Grafana data source settings
High memory usage JVM heap size too small Increase -Xmx value in setenv.sh and restart Tomcat
Permission denied errors Incorrect file ownership sudo chown -R tomcat:tomcat /opt/tomcat

Next steps

Running this in production?

Want this handled for you? Setting this up once is straightforward. Keeping it patched, monitored, backed up and performant across environments is the harder part. See how we run infrastructure like this for European teams.

Automated install script

Run this to automate the entire setup

Need help?

Don't want to manage this yourself?

We handle managed devops services for businesses that depend on uptime. From initial setup to ongoing operations.