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
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<>HeapMemoryUsage'
name: jvm_memory_heap_bytes
type: GAUGE
labels:
area: "heap"
help: "Heap memory usage in bytes"
- pattern: 'java.lang<>NonHeapMemoryUsage'
name: jvm_memory_nonheap_bytes
type: GAUGE
labels:
area: "nonheap"
help: "Non-heap memory usage in bytes"
# Thread metrics
- pattern: 'java.lang<>ThreadCount'
name: jvm_threads_current
type: GAUGE
help: "Current thread count"
- pattern: 'java.lang<>PeakThreadCount'
name: jvm_threads_peak
type: GAUGE
help: "Peak thread count"
# Tomcat connector metrics
- pattern: 'Catalina<>currentThreadCount'
name: tomcat_threads_current
type: GAUGE
labels:
connector: "$1"
help: "Current thread count for connector"
- pattern: 'Catalina<>currentThreadsBusy'
name: tomcat_threads_busy
type: GAUGE
labels:
connector: "$1"
help: "Current busy thread count for connector"
- pattern: 'Catalina<>maxThreads'
name: tomcat_threads_max
type: GAUGE
labels:
connector: "$1"
help: "Maximum thread count for connector"
# Request metrics
- pattern: 'Catalina<>requestCount'
name: tomcat_requests_total
type: COUNTER
labels:
connector: "$1"
help: "Total request count"
- pattern: 'Catalina<>processingTime'
name: tomcat_processing_time_milliseconds_total
type: COUNTER
labels:
connector: "$1"
help: "Total processing time in milliseconds"
- pattern: 'Catalina<>bytesReceived'
name: tomcat_bytes_received_total
type: COUNTER
labels:
connector: "$1"
help: "Total bytes received"
- pattern: 'Catalina<>bytesSent'
name: tomcat_bytes_sent_total
type: COUNTER
labels:
connector: "$1"
help: "Total bytes sent"
# Session metrics
- pattern: 'Catalina<>activeSessions'
name: tomcat_sessions_active
type: GAUGE
labels:
host: "$1"
context: "$2"
help: "Active session count"
- pattern: 'Catalina<>sessionCounter'
name: tomcat_sessions_created_total
type: COUNTER
labels:
host: "$1"
context: "$2"
help: "Total sessions created"
# Garbage Collection metrics
- pattern: 'java.lang<>CollectionCount'
name: jvm_gc_collections_total
type: COUNTER
labels:
gc: "$1"
help: "Total garbage collections"
- pattern: 'java.lang<>CollectionTime'
name: jvm_gc_collection_time_milliseconds_total
type: COUNTER
labels:
gc: "$1"
help: "Total garbage collection time in milliseconds"
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
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
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 Grafana, Prometheus, and Tomcat metrics endpoints.
sudo ufw allow 3000/tcp comment "Grafana"
sudo ufw allow 9090/tcp comment "Prometheus"
sudo ufw allow 8080/tcp comment "Tomcat"
sudo ufw allow 8088/tcp comment "Tomcat Metrics"
Verify your setup
Test that all components are working correctly and collecting metrics.
# Check Tomcat is running and metrics are available
sudo systemctl status tomcat
curl -s http://localhost:8088/metrics | grep "jvm_memory_heap_bytes" | head -5
Check Prometheus is scraping Tomcat
sudo systemctl status prometheus
curl -s "http://localhost:9090/api/v1/query?query=up{job='tomcat'}" | jq '.data.result[0].value[1]'
Check Grafana is running
sudo systemctl status grafana-server
curl -s http://localhost:3000/api/health
Access your monitoring setup:
- Grafana dashboard:
http://your-server-ip:3000(admin/admin) - Prometheus interface:
http://your-server-ip:9090 - Tomcat web interface:
http://your-server-ip:8080 - Tomcat metrics endpoint:
http://your-server-ip:8088/metrics
Configure advanced monitoring features
Add custom JVM metrics
Extend monitoring to include additional JVM performance indicators.
# Add these additional rules to your existing configuration
# Class loading metrics
- pattern: 'java.lang<>LoadedClassCount'
name: jvm_classes_loaded
type: GAUGE
help: "Number of classes currently loaded"
- pattern: 'java.lang<>TotalLoadedClassCount'
name: jvm_classes_loaded_total
type: COUNTER
help: "Total number of classes loaded since JVM start"
# Operating System metrics
- pattern: 'java.lang<>SystemCpuLoad'
name: system_cpu_load
type: GAUGE
help: "System CPU load"
- pattern: 'java.lang<>ProcessCpuLoad'
name: process_cpu_load
type: GAUGE
help: "Process CPU load"
# Connection pool metrics (if using JNDI datasources)
- pattern: 'Catalina<>numActive'
name: tomcat_datasource_connections_active
type: GAUGE
labels:
host: "$1"
context: "$2"
datasource: "$3"
help: "Active database connections"
- pattern: 'Catalina<>maxActive'
name: tomcat_datasource_connections_max
type: GAUGE
labels:
host: "$1"
context: "$2"
datasource: "$3"
help: "Maximum database connections"
Create application-specific dashboards
Set up monitoring for specific web applications deployed to Tomcat.
{
"dashboard": {
"title": "Tomcat Applications Dashboard",
"panels": [
{
"id": 10,
"title": "Active Sessions by Application",
"type": "timeseries",
"targets": [
{
"expr": "tomcat_sessions_active",
"legendFormat": "{{host}}/{{context}}"
}
],
"gridPos": {"h": 8, "w": 12, "x": 0, "y": 0}
},
{
"id": 11,
"title": "Database Connection Usage",
"type": "timeseries",
"targets": [
{
"expr": "tomcat_datasource_connections_active",
"legendFormat": "Active - {{datasource}}"
},
{
"expr": "tomcat_datasource_connections_max",
"legendFormat": "Max - {{datasource}}"
}
],
"gridPos": {"h": 8, "w": 12, "x": 12, "y": 0}
}
],
"time": {"from": "now-1h", "to": "now"},
"refresh": "30s"
}
}
Set up alert notification channels
Configure Grafana to send alerts via email or Slack when thresholds are exceeded.
notifiers:
- name: email-alerts
type: email
uid: email001
org_id: 1
is_default: true
settings:
addresses: admin@example.com
subject: "Grafana Alert - {{ .CommonLabels.alertname }}"
secure_settings:
password: your_email_password
Similar to our MySQL monitoring setup, you can expand this configuration to monitor additional application metrics and set up more sophisticated alerting rules.
Performance tuning recommendations
Optimize Prometheus retention and storage
Configure data retention policies to balance storage usage with monitoring history.
# Update the ExecStart line to include retention settings
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 \
--storage.tsdb.retention.time=30d \
--storage.tsdb.retention.size=10GB
sudo systemctl daemon-reload
sudo systemctl restart prometheus
Configure log rotation for metrics
Set up log rotation to prevent disk space issues with monitoring logs.
/opt/tomcat/logs/*.log {
weekly
missingok
rotate 4
compress
notifempty
create 644 tomcat tomcat
postrotate
systemctl reload tomcat
endscript
}
/var/log/grafana/*.log {
weekly
missingok
rotate 4
compress
notifempty
create 644 grafana grafana
}
Common issues
| Symptom | Cause | Fix |
|---|---|---|
| JMX metrics not appearing | JMX agent not loaded or wrong port | curl http://localhost:8088/metrics and check /opt/tomcat/logs/catalina.out |
| Prometheus shows target down | Firewall blocking port 8088 | Configure firewall rules or check network connectivity |
| Grafana dashboard empty | Wrong datasource URL or no data | Verify Prometheus URL in datasource config and check metrics availability |
| High memory usage | Too frequent scraping or retention too long | Increase scrape intervals or reduce retention time in Prometheus config |
| Tomcat slow startup | JMX agent adding overhead | Increase JVM memory allocation or optimize JMX exporter rules |
| Missing thread pool metrics | Connector names changed in Tomcat config | Update JMX exporter pattern matching in configuration file |
Next steps
- Configure Tomcat database connection pooling for comprehensive database monitoring
- Implement Grafana advanced alerting to integrate with your incident response workflow
- Monitor NGINX performance with Prometheus and Grafana if using a reverse proxy
- Set up Tomcat clustering with load balancing for high availability deployments
- Configure Tomcat SSL certificates and security hardening for production environments
Running this in production?
Automated install script
Run this to automate the entire setup
#!/usr/bin/env bash
set -euo pipefail
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
# Configuration
TOMCAT_VERSION="11.0.0"
PROMETHEUS_JMX_VERSION="0.20.0"
PROMETHEUS_VERSION="2.45.0"
GRAFANA_VERSION="10.0.0"
# Function to print colored output
print_status() {
echo -e "${GREEN}[INFO]${NC} $1"
}
print_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
print_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# Usage message
usage() {
echo "Usage: $0 [OPTIONS]"
echo "Options:"
echo " -h, --help Show this help message"
echo " --metrics-port Port for JMX metrics (default: 9090)"
echo ""
echo "Example: $0 --metrics-port 9091"
exit 1
}
# Parse arguments
METRICS_PORT=9090
while [[ $# -gt 0 ]]; do
case $1 in
-h|--help)
usage
;;
--metrics-port)
METRICS_PORT="$2"
shift 2
;;
*)
echo "Unknown option: $1"
usage
;;
esac
done
# Cleanup function
cleanup() {
print_error "Installation failed. Cleaning up..."
systemctl stop tomcat 2>/dev/null || true
systemctl stop prometheus 2>/dev/null || true
systemctl stop grafana-server 2>/dev/null || true
rm -rf /opt/tomcat /opt/prometheus /tmp/tomcat-install-*
}
trap cleanup ERR
# Check if running as root
if [[ $EUID -ne 0 ]]; then
print_error "This script must be run as root"
exit 1
fi
print_status "[1/12] Detecting distribution and package manager..."
# Auto-detect distro
if [ -f /etc/os-release ]; then
. /etc/os-release
case "$ID" in
ubuntu|debian)
PKG_MGR="apt"
PKG_UPDATE="apt update"
PKG_INSTALL="apt install -y"
JAVA_PACKAGE="openjdk-11-jdk"
;;
almalinux|rocky|centos|rhel|ol|fedora)
PKG_MGR="dnf"
PKG_UPDATE="dnf update -y"
PKG_INSTALL="dnf install -y"
JAVA_PACKAGE="java-11-openjdk-devel"
;;
amzn)
PKG_MGR="yum"
PKG_UPDATE="yum update -y"
PKG_INSTALL="yum install -y"
JAVA_PACKAGE="java-11-amazon-corretto-devel"
;;
*)
print_error "Unsupported distribution: $ID"
exit 1
;;
esac
else
print_error "Cannot detect distribution"
exit 1
fi
print_status "Detected: $PRETTY_NAME using $PKG_MGR"
print_status "[2/12] Updating system packages..."
$PKG_UPDATE
print_status "[3/12] Installing required packages..."
$PKG_INSTALL $JAVA_PACKAGE wget curl tar
print_status "[4/12] Creating tomcat user..."
if ! id tomcat &>/dev/null; then
useradd -r -m -U -d /opt/tomcat -s /bin/false tomcat
fi
print_status "[5/12] Downloading and installing Apache Tomcat $TOMCAT_VERSION..."
cd /tmp
TOMCAT_TARBALL="apache-tomcat-${TOMCAT_VERSION}.tar.gz"
if [ ! -f "$TOMCAT_TARBALL" ]; then
wget "https://archive.apache.org/dist/tomcat/tomcat-11/v${TOMCAT_VERSION}/bin/${TOMCAT_TARBALL}"
fi
mkdir -p /opt/tomcat
tar xf "$TOMCAT_TARBALL" -C /opt/tomcat --strip-components=1
chown -R tomcat:tomcat /opt/tomcat
chmod +x /opt/tomcat/bin/*.sh
print_status "[6/12] Downloading JMX Prometheus Java agent..."
cd /opt/tomcat/lib
wget -O jmx_prometheus_javaagent-${PROMETHEUS_JMX_VERSION}.jar \
"https://repo1.maven.org/maven2/io/prometheus/jmx/jmx_prometheus_javaagent/${PROMETHEUS_JMX_VERSION}/jmx_prometheus_javaagent-${PROMETHEUS_JMX_VERSION}.jar"
chown tomcat:tomcat jmx_prometheus_javaagent-${PROMETHEUS_JMX_VERSION}.jar
print_status "[7/12] Creating JMX exporter configuration..."
cat > /opt/tomcat/conf/jmx-config.yml << 'EOF'
rules:
# JVM Memory metrics
- pattern: 'java.lang<type=Memory><HeapMemoryUsage>(\w+)'
name: jvm_memory_heap_bytes
type: GAUGE
labels:
area: "heap"
type: "$1"
# Thread metrics
- pattern: 'java.lang<type=Threading><>ThreadCount'
name: jvm_threads_current
type: GAUGE
# GC metrics
- pattern: 'java.lang<type=GarbageCollector, name=(\w+)><>CollectionCount'
name: jvm_gc_collections_total
type: COUNTER
labels:
gc: "$1"
# Tomcat connector metrics
- pattern: 'Catalina<type=ThreadPool, name="([^"]+)"><>currentThreadCount'
name: tomcat_threads_current
type: GAUGE
labels:
connector: "$1"
- pattern: 'Catalina<type=GlobalRequestProcessor, name="([^"]+)"><>requestCount'
name: tomcat_requests_total
type: COUNTER
labels:
connector: "$1"
EOF
chown tomcat:tomcat /opt/tomcat/conf/jmx-config.yml
chmod 644 /opt/tomcat/conf/jmx-config.yml
print_status "[8/12] Configuring Tomcat with JMX agent..."
cat > /opt/tomcat/bin/setenv.sh << EOF
CATALINA_OPTS="\$CATALINA_OPTS -javaagent:/opt/tomcat/lib/jmx_prometheus_javaagent-${PROMETHEUS_JMX_VERSION}.jar=${METRICS_PORT}:/opt/tomcat/conf/jmx-config.yml"
CATALINA_OPTS="\$CATALINA_OPTS -Dcom.sun.management.jmxremote"
CATALINA_OPTS="\$CATALINA_OPTS -Dcom.sun.management.jmxremote.port=9999"
CATALINA_OPTS="\$CATALINA_OPTS -Dcom.sun.management.jmxremote.authenticate=false"
CATALINA_OPTS="\$CATALINA_OPTS -Dcom.sun.management.jmxremote.ssl=false"
EOF
chown tomcat:tomcat /opt/tomcat/bin/setenv.sh
chmod 755 /opt/tomcat/bin/setenv.sh
print_status "[9/12] Creating Tomcat systemd service..."
cat > /etc/systemd/system/tomcat.service << EOF
[Unit]
Description=Apache Tomcat Web Application Container
After=network.target
[Service]
Type=forking
Environment=JAVA_HOME=/usr/lib/jvm/java-11-openjdk
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
User=tomcat
Group=tomcat
UMask=0007
RestartSec=10
Restart=always
[Install]
WantedBy=multi-user.target
EOF
print_status "[10/12] Installing Prometheus..."
cd /tmp
PROMETHEUS_TARBALL="prometheus-${PROMETHEUS_VERSION}.linux-amd64.tar.gz"
if [ ! -f "$PROMETHEUS_TARBALL" ]; then
wget "https://github.com/prometheus/prometheus/releases/download/v${PROMETHEUS_VERSION}/${PROMETHEUS_TARBALL}"
fi
useradd -r -s /bin/false prometheus 2>/dev/null || true
mkdir -p /opt/prometheus /var/lib/prometheus
tar xf "$PROMETHEUS_TARBALL" -C /opt/prometheus --strip-components=1
chown -R prometheus:prometheus /opt/prometheus /var/lib/prometheus
print_status "[11/12] Configuring Prometheus..."
cat > /opt/prometheus/prometheus.yml << EOF
global:
scrape_interval: 15s
scrape_configs:
- job_name: 'tomcat'
static_configs:
- targets: ['localhost:${METRICS_PORT}']
scrape_interval: 5s
metrics_path: /metrics
EOF
chown prometheus:prometheus /opt/prometheus/prometheus.yml
# Create Prometheus systemd service
cat > /etc/systemd/system/prometheus.service << EOF
[Unit]
Description=Prometheus
After=network.target
[Service]
User=prometheus
Group=prometheus
Type=simple
ExecStart=/opt/prometheus/prometheus --config.file=/opt/prometheus/prometheus.yml --storage.tsdb.path=/var/lib/prometheus --web.console.templates=/opt/prometheus/consoles --web.console.libraries=/opt/prometheus/console_libraries --web.listen-address=0.0.0.0:9091
[Install]
WantedBy=multi-user.target
EOF
print_status "[12/12] Installing Grafana..."
case "$ID" in
ubuntu|debian)
wget -q -O - https://packages.grafana.com/gpg.key | apt-key add -
echo "deb https://packages.grafana.com/oss/deb stable main" > /etc/apt/sources.list.d/grafana.list
apt update
apt install -y grafana
;;
*)
cat > /etc/yum.repos.d/grafana.repo << EOF
[grafana]
name=grafana
baseurl=https://packages.grafana.com/oss/rpm
repo_gpgcheck=1
enabled=1
gpgcheck=1
gpgkey=https://packages.grafana.com/gpg.key
sslverify=1
sslcacert=/etc/pki/tls/certs/ca-bundle.crt
EOF
$PKG_INSTALL grafana
;;
esac
# Configure firewall
if command -v firewall-cmd &> /dev/null; then
firewall-cmd --permanent --add-port=8080/tcp
firewall-cmd --permanent --add-port=${METRICS_PORT}/tcp
firewall-cmd --permanent --add-port=9091/tcp
firewall-cmd --permanent --add-port=3000/tcp
firewall-cmd --reload
elif command -v ufw &> /dev/null; then
ufw allow 8080/tcp
ufw allow ${METRICS_PORT}/tcp
ufw allow 9091/tcp
ufw allow 3000/tcp
fi
# Start services
systemctl daemon-reload
systemctl enable tomcat prometheus grafana-server
systemctl start tomcat
sleep 5
systemctl start prometheus
systemctl start grafana-server
print_status "Installation completed successfully!"
print_status "Services:"
print_status " Tomcat: http://localhost:8080"
print_status " Tomcat Metrics: http://localhost:${METRICS_PORT}/metrics"
print_status " Prometheus: http://localhost:9091"
print_status " Grafana: http://localhost:3000 (admin/admin)"
print_warning "Please change the default Grafana password on first login"
# Verification
sleep 10
if curl -s http://localhost:${METRICS_PORT}/metrics > /dev/null; then
print_status "✓ Tomcat metrics endpoint is responding"
else
print_error "✗ Tomcat metrics endpoint is not responding"
fi
if curl -s http://localhost:9091/-/healthy > /dev/null; then
print_status "✓ Prometheus is healthy"
else
print_error "✗ Prometheus is not responding"
fi
if curl -s http://localhost:3000/api/health > /dev/null; then
print_status "✓ Grafana is responding"
else
print_error "✗ Grafana is not responding"
fi
Review the script before running. Execute with: bash install.sh