Monitor MySQL performance with Prometheus and Grafana dashboards

Intermediate 45 min Apr 27, 2026 37 views
Ubuntu 24.04 Debian 12 AlmaLinux 9 Rocky Linux 9

Set up comprehensive MySQL monitoring with Prometheus MySQL Exporter and Grafana dashboards. Track query performance, connections, replication lag, and resource usage with automated alerts for production database health.

Prerequisites

  • MySQL server running
  • Root access to server
  • Basic familiarity with MySQL administration
  • At least 2GB RAM available

What this solves

MySQL performance monitoring becomes critical as your database grows beyond basic development setups. Without proper metrics collection, you'll miss slow queries, connection pool exhaustion, replication lag, and resource bottlenecks until they cause outages. This tutorial sets up Prometheus MySQL Exporter to collect database metrics and configures Grafana dashboards with automated alerts for proactive database health monitoring.

Step-by-step installation

Update system packages

Start by updating your package manager to ensure you get the latest versions of all components.

sudo apt update && sudo apt upgrade -y
sudo dnf update -y

Create MySQL monitoring user

Create a dedicated MySQL user for the Prometheus exporter with minimal required privileges for security.

mysql -u root -p

Run these SQL commands to create the monitoring user and grant necessary permissions:

CREATE USER 'mysqld_exporter'@'localhost' IDENTIFIED BY 'StrongExporterPassword123!';
GRANT PROCESS, REPLICATION CLIENT ON . TO 'mysqld_exporter'@'localhost';
GRANT SELECT ON performance_schema.* TO 'mysqld_exporter'@'localhost';
GRANT SELECT ON information_schema.* TO 'mysqld_exporter'@'localhost';
FLUSH PRIVILEGES;
EXIT;

Install MySQL Exporter

Download and install the latest MySQL Exporter binary from the official GitHub releases.

cd /tmp
wget https://github.com/prometheus/mysqld_exporter/releases/download/v0.15.1/mysqld_exporter-0.15.1.linux-amd64.tar.gz
tar xzf mysqld_exporter-0.15.1.linux-amd64.tar.gz
sudo mv mysqld_exporter-0.15.1.linux-amd64/mysqld_exporter /usr/local/bin/
sudo chmod +x /usr/local/bin/mysqld_exporter

Create MySQL Exporter user and directories

Create a system user and directories for the MySQL Exporter service with proper security isolation.

sudo useradd --no-create-home --shell /bin/false mysqld_exporter
sudo mkdir -p /etc/mysqld_exporter
sudo chown mysqld_exporter:mysqld_exporter /etc/mysqld_exporter

Configure MySQL Exporter credentials

Create a secure configuration file with MySQL connection details for the exporter.

[client]
host=localhost
port=3306
user=mysqld_exporter
password=StrongExporterPassword123!

Set proper permissions to protect the credentials file:

sudo chown mysqld_exporter:mysqld_exporter /etc/mysqld_exporter/.my.cnf
sudo chmod 600 /etc/mysqld_exporter/.my.cnf

Create MySQL Exporter systemd service

Configure MySQL Exporter as a systemd service for automatic startup and proper process management.

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

[Service]
User=mysqld_exporter
Group=mysqld_exporter
Type=simple
ExecStart=/usr/local/bin/mysqld_exporter --config.my-cnf=/etc/mysqld_exporter/.my.cnf --collect.global_status --collect.info_schema.innodb_metrics --collect.auto_increment.columns --collect.info_schema.processlist --collect.binlog_size --collect.info_schema.tablestats --collect.global_variables --collect.info_schema.query_response_time --collect.info_schema.userstats --collect.info_schema.tables --collect.perf_schema.tablelocks --collect.perf_schema.file_events --collect.perf_schema.eventswaits --collect.perf_schema.indexiowaits --collect.perf_schema.tableiowaits --collect.slave_status --web.listen-address=0.0.0.0:9104
Restart=always

[Install]
WantedBy=multi-user.target

Start and enable MySQL Exporter

Enable the MySQL Exporter service to start on boot and verify it's running correctly.

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

Install Prometheus

Install Prometheus to collect metrics from the MySQL Exporter and other system components.

cd /tmp
wget https://github.com/prometheus/prometheus/releases/download/v2.47.2/prometheus-2.47.2.linux-amd64.tar.gz
tar xzf prometheus-2.47.2.linux-amd64.tar.gz
sudo mv prometheus-2.47.2.linux-amd64/prometheus /usr/local/bin/
sudo mv prometheus-2.47.2.linux-amd64/promtool /usr/local/bin/
sudo chmod +x /usr/local/bin/prometheus /usr/local/bin/promtool
cd /tmp
wget https://github.com/prometheus/prometheus/releases/download/v2.47.2/prometheus-2.47.2.linux-amd64.tar.gz
tar xzf prometheus-2.47.2.linux-amd64.tar.gz
sudo mv prometheus-2.47.2.linux-amd64/prometheus /usr/local/bin/
sudo mv prometheus-2.47.2.linux-amd64/promtool /usr/local/bin/
sudo chmod +x /usr/local/bin/prometheus /usr/local/bin/promtool

Create Prometheus user and directories

Set up dedicated user and directories for Prometheus with proper permissions for data storage and configuration.

sudo useradd --no-create-home --shell /bin/false prometheus
sudo mkdir -p /etc/prometheus /var/lib/prometheus
sudo chown prometheus:prometheus /etc/prometheus /var/lib/prometheus

Configure Prometheus to scrape MySQL metrics

Create Prometheus configuration to collect metrics from MySQL Exporter and system node exporter.

global:
  scrape_interval: 15s
  evaluation_interval: 15s

rule_files:
  - "/etc/prometheus/mysql_rules.yml"

alerting:
  alertmanagers:
    - static_configs:
        - targets:
          - localhost:9093

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

  - job_name: 'mysql'
    static_configs:
      - targets: ['localhost:9104']
    scrape_interval: 5s
    metrics_path: /metrics

  - job_name: 'node'
    static_configs:
      - targets: ['localhost:9100']

Set proper ownership for the configuration file:

sudo chown prometheus:prometheus /etc/prometheus/prometheus.yml

Create MySQL alerting rules

Define alert rules for common MySQL performance and availability issues.

groups:
  - name: mysql_alerts
    rules:
      - alert: MySQLDown
        expr: mysql_up == 0
        for: 0m
        labels:
          severity: critical
        annotations:
          summary: "MySQL instance is down"
          description: "MySQL database is down on {{ $labels.instance }}"

      - alert: MySQLTooManyConnections
        expr: max_over_time(mysql_global_status_threads_connected[1m]) / mysql_global_variables_max_connections * 100 > 80
        for: 2m
        labels:
          severity: warning
        annotations:
          summary: "MySQL connection usage is high"
          description: "More than 80% of MySQL connections are in use on {{ $labels.instance }}"

      - alert: MySQLHighQPS
        expr: rate(mysql_global_status_questions[5m]) > 1000
        for: 2m
        labels:
          severity: warning
        annotations:
          summary: "MySQL high queries per second"
          description: "MySQL is executing {{ $value }} queries per second on {{ $labels.instance }}"

      - alert: MySQLSlowQueries
        expr: increase(mysql_global_status_slow_queries[1m]) > 0
        for: 2m
        labels:
          severity: warning
        annotations:
          summary: "MySQL slow queries detected"
          description: "MySQL has {{ $value }} slow queries in the last minute on {{ $labels.instance }}"

      - alert: MySQLInnoDBLogWaits
        expr: rate(mysql_global_status_innodb_log_waits[15m]) > 10
        for: 0m
        labels:
          severity: warning
        annotations:
          summary: "MySQL InnoDB log writes are stalling"
          description: "MySQL InnoDB log writes are waiting for disk at a rate of {{ $value }} per second on {{ $labels.instance }}"

      - alert: MySQLReplicationLag
        expr: mysql_slave_lag_seconds > 30
        for: 1m
        labels:
          severity: critical
        annotations:
          summary: "MySQL replication lag is high"
          description: "MySQL slave is {{ $value }} seconds behind master on {{ $labels.instance }}"

      - alert: MySQLReplicationSQLThreadNotRunning
        expr: mysql_slave_sql_running == 0
        for: 0m
        labels:
          severity: critical
        annotations:
          summary: "MySQL replication SQL thread is not running"
          description: "MySQL replication SQL thread is not running on {{ $labels.instance }}"

      - alert: MySQLReplicationIOThreadNotRunning
        expr: mysql_slave_io_running == 0
        for: 0m
        labels:
          severity: critical
        annotations:
          summary: "MySQL replication IO thread is not running"
          description: "MySQL replication IO thread is not running on {{ $labels.instance }}"

      - alert: MySQLTableLockWaitsHigh
        expr: rate(mysql_global_status_table_locks_waited[15m]) / rate(mysql_global_status_table_locks_immediate[15m]) > 0.02
        for: 1m
        labels:
          severity: warning
        annotations:
          summary: "MySQL table lock waits are high"
          description: "MySQL table lock wait rate is {{ $value }} on {{ $labels.instance }}"

      - alert: MySQLInnoDBBufferPoolEfficiency
        expr: mysql_global_status_innodb_buffer_pool_reads / mysql_global_status_innodb_buffer_pool_read_requests > 0.02
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "MySQL InnoDB buffer pool efficiency is low"
          description: "MySQL InnoDB buffer pool read miss ratio is {{ $value }} on {{ $labels.instance }}"

Set ownership for the rules file:

sudo chown prometheus:prometheus /etc/prometheus/mysql_rules.yml

Install Node Exporter for system metrics

Install Node Exporter to collect system-level metrics that complement MySQL monitoring.

cd /tmp
wget https://github.com/prometheus/node_exporter/releases/download/v1.6.1/node_exporter-1.6.1.linux-amd64.tar.gz
tar xzf node_exporter-1.6.1.linux-amd64.tar.gz
sudo mv node_exporter-1.6.1.linux-amd64/node_exporter /usr/local/bin/
sudo chmod +x /usr/local/bin/node_exporter

Create Node Exporter systemd service

Configure Node Exporter as a systemd service for system metrics collection.

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

[Service]
User=node_exporter
Group=node_exporter
Type=simple
ExecStart=/usr/local/bin/node_exporter --web.listen-address=0.0.0.0:9100
Restart=always

[Install]
WantedBy=multi-user.target

Create the node_exporter user and start the service:

sudo useradd --no-create-home --shell /bin/false node_exporter
sudo systemctl daemon-reload
sudo systemctl enable --now node_exporter

Create Prometheus systemd service

Configure Prometheus as a systemd service for metrics collection and alerting.

[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 \
    --storage.tsdb.retention.time=15d
Restart=always

[Install]
WantedBy=multi-user.target

Start Prometheus service

Enable and start Prometheus to begin collecting MySQL and system metrics.

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

Install Grafana

Install Grafana for visualizing MySQL performance metrics with professional dashboards.

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

Configure basic Grafana settings for security and MySQL monitoring integration.

[server]
http_addr = 0.0.0.0
http_port = 3000
domain = example.com
root_url = http://example.com:3000/

[security]
admin_user = admin
admin_password = StrongGrafanaPassword123!
secret_key = SW2YcwTIb9zpOOhoPsMm

[users]
allow_sign_up = false
default_theme = dark

[auth.anonymous]
enabled = false

[log]
mode = console file
level = info

Start Grafana service

Enable and start Grafana for web-based MySQL performance visualization.

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

Configure firewall rules

Open necessary ports for Prometheus, Grafana, and MySQL Exporter access.

sudo ufw allow 3000/tcp
sudo ufw allow 9090/tcp
sudo ufw allow 9104/tcp
sudo ufw allow 9100/tcp
sudo firewall-cmd --permanent --add-port=3000/tcp
sudo firewall-cmd --permanent --add-port=9090/tcp
sudo firewall-cmd --permanent --add-port=9104/tcp
sudo firewall-cmd --permanent --add-port=9100/tcp
sudo firewall-cmd --reload

Add Prometheus data source to Grafana

Configure Grafana to use Prometheus as a data source for MySQL metrics visualization.

Access Grafana at http://your-server-ip:3000 and log in with your admin credentials. Then add the data source:

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

Import MySQL dashboard

Import a comprehensive MySQL dashboard template for immediate performance monitoring visualization.

curl -X POST \
  http://admin:StrongGrafanaPassword123!@localhost:3000/api/dashboards/import \
  -H 'Content-Type: application/json' \
  -d '{
    "dashboard": {
      "id": null,
      "title": "MySQL Performance Dashboard",
      "tags": ["mysql", "prometheus"],
      "timezone": "browser",
      "panels": [
        {
          "id": 1,
          "title": "MySQL Status",
          "type": "stat",
          "targets": [{
            "expr": "mysql_up",
            "refId": "A"
          }],
          "gridPos": {"h": 4, "w": 6, "x": 0, "y": 0}
        },
        {
          "id": 2,
          "title": "Connections",
          "type": "timeseries",
          "targets": [{
            "expr": "mysql_global_status_threads_connected",
            "refId": "A",
            "legendFormat": "Connected"
          }, {
            "expr": "mysql_global_variables_max_connections",
            "refId": "B",
            "legendFormat": "Max Connections"
          }],
          "gridPos": {"h": 8, "w": 12, "x": 6, "y": 0}
        },
        {
          "id": 3,
          "title": "Queries Per Second",
          "type": "timeseries",
          "targets": [{
            "expr": "rate(mysql_global_status_questions[5m])",
            "refId": "A",
            "legendFormat": "QPS"
          }],
          "gridPos": {"h": 8, "w": 12, "x": 0, "y": 8}
        },
        {
          "id": 4,
          "title": "InnoDB Buffer Pool Usage",
          "type": "timeseries",
          "targets": [{
            "expr": "mysql_global_status_innodb_buffer_pool_pages_data / mysql_global_status_innodb_buffer_pool_pages_total * 100",
            "refId": "A",
            "legendFormat": "Buffer Pool Usage %"
          }],
          "gridPos": {"h": 8, "w": 12, "x": 12, "y": 8}
        },
        {
          "id": 5,
          "title": "Slow Queries",
          "type": "timeseries",
          "targets": [{
            "expr": "rate(mysql_global_status_slow_queries[5m])",
            "refId": "A",
            "legendFormat": "Slow Queries/sec"
          }],
          "gridPos": {"h": 8, "w": 12, "x": 0, "y": 16}
        },
        {
          "id": 6,
          "title": "MySQL Uptime",
          "type": "stat",
          "targets": [{
            "expr": "mysql_global_status_uptime",
            "refId": "A"
          }],
          "gridPos": {"h": 4, "w": 6, "x": 12, "y": 16}
        }
      ],
      "time": {
        "from": "now-1h",
        "to": "now"
      },
      "refresh": "5s"
    }
  }'

Install Alertmanager for notifications

Install and configure Alertmanager to handle alert notifications from Prometheus MySQL rules.

cd /tmp
wget https://github.com/prometheus/alertmanager/releases/download/v0.26.0/alertmanager-0.26.0.linux-amd64.tar.gz
tar xzf alertmanager-0.26.0.linux-amd64.tar.gz
sudo mv alertmanager-0.26.0.linux-amd64/alertmanager /usr/local/bin/
sudo mv alertmanager-0.26.0.linux-amd64/amtool /usr/local/bin/
sudo chmod +x /usr/local/bin/alertmanager /usr/local/bin/amtool

Configure Alertmanager

Set up Alertmanager with email notifications for MySQL performance alerts.

sudo useradd --no-create-home --shell /bin/false alertmanager
sudo mkdir -p /etc/alertmanager /var/lib/alertmanager
sudo chown alertmanager:alertmanager /etc/alertmanager /var/lib/alertmanager
global:
  smtp_smarthost: 'localhost:587'
  smtp_from: 'alerts@example.com'
  smtp_auth_username: 'alerts@example.com'
  smtp_auth_password: 'your_email_password'
  smtp_require_tls: true

route:
  group_by: ['alertname']
  group_wait: 10s
  group_interval: 10s
  repeat_interval: 1h
  receiver: 'web.hook'

receivers:
  - name: 'web.hook'
    email_configs:
      - to: 'admin@example.com'
        subject: 'MySQL Alert: {{ range .Alerts }}{{ .Annotations.summary }}{{ end }}'
        body: |
          {{ range .Alerts }}
          Alert: {{ .Annotations.summary }}
          Description: {{ .Annotations.description }}
          Instance: {{ .Labels.instance }}
          Severity: {{ .Labels.severity }}
          {{ end }}

inhibit_rules:
  - source_match:
      severity: 'critical'
    target_match:
      severity: 'warning'
    equal: ['alertname', 'dev', 'instance']

Set proper ownership:

sudo chown alertmanager:alertmanager /etc/alertmanager/alertmanager.yml

Create Alertmanager systemd service

Configure Alertmanager as a systemd service for reliable alert processing and notification delivery.

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

[Service]
User=alertmanager
Group=alertmanager
Type=simple
ExecStart=/usr/local/bin/alertmanager \
    --config.file /etc/alertmanager/alertmanager.yml \
    --storage.path /var/lib/alertmanager/ \
    --web.listen-address=0.0.0.0:9093
Restart=always

[Install]
WantedBy=multi-user.target

Start the Alertmanager service:

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

Configure Grafana dashboards

Access Grafana web interface

Open your web browser and navigate to Grafana to complete the dashboard setup for MySQL monitoring.

Visit http://your-server-ip:3000 and log in with username admin and the password you configured.

Create custom MySQL performance dashboard

Build comprehensive dashboards for different aspects of MySQL performance monitoring.

In Grafana, create new dashboards with these key panels:

  • MySQL Status Panel: mysql_up - Shows if MySQL is running
  • Connection Usage: (mysql_global_status_threads_connected / mysql_global_variables_max_connections) * 100
  • Query Performance: rate(mysql_global_status_questions[5m])
  • Slow Query Rate: rate(mysql_global_status_slow_queries[5m])
  • InnoDB Buffer Pool Efficiency: (1 - mysql_global_status_innodb_buffer_pool_reads / mysql_global_status_innodb_buffer_pool_read_requests) * 100
  • Table Locks: rate(mysql_global_status_table_locks_waited[5m])
  • Replication Lag: mysql_slave_lag_seconds (if using replication)

Set up alert notifications in Grafana

Configure Grafana alerts to complement Prometheus alerting for comprehensive MySQL monitoring coverage.

Create notification channels in Grafana for:

  • Email notifications for critical MySQL issues
  • Slack integration for team alerts
  • Webhook notifications for automated responses

Verify your setup

Test all components of your MySQL monitoring stack to ensure proper data collection and alerting.

# Check MySQL Exporter metrics
curl http://localhost:9104/metrics | grep mysql_up

Verify Prometheus is collecting MySQL metrics

curl http://localhost:9090/api/v1/query?query=mysql_up

Test Prometheus rules

prometheus-tool query mysql_global_status_threads_connected

Check Grafana API

curl -u admin:StrongGrafanaPassword123! http://localhost:3000/api/health

Verify all services are running

sudo systemctl status mysqld_exporter prometheus node_exporter grafana-server alertmanager

Access your monitoring interfaces:

  • Prometheus: http://your-server-ip:9090
  • Grafana: http://your-server-ip:3000
  • Alertmanager: http://your-server-ip:9093
  • MySQL Exporter metrics: http://your-server-ip:9104/metrics

Common issues

Symptom Cause Fix
MySQL Exporter shows mysql_up 0 MySQL connection failed or wrong credentials Check MySQL user permissions and password in /etc/mysqld_exporter/.my.cnf
No data in Grafana dashboards Prometheus not scraping MySQL Exporter Verify prometheus.yml targets and check http://localhost:9090/targets
Permission denied errors Incorrect file ownership or permissions Run sudo chown mysqld_exporter:mysqld_exporter /etc/mysqld_exporter/.my.cnf
Alerts not firing Alert rules not loaded or thresholds too high Check Prometheus rules with promtool check rules /etc/prometheus/mysql_rules.yml
Grafana cannot connect to Prometheus Prometheus not running or wrong URL Verify Prometheus is running on port 9090 and check data source URL
Email alerts not working SMTP configuration incorrect Test SMTP settings and check Alertmanager logs with journalctl -u alertmanager
High memory usage by Prometheus Too many metrics or long retention Adjust --storage.tsdb.retention.time or filter metrics in scrape configs
Missing InnoDB metrics MySQL Exporter collectors not enabled Add --collect.info_schema.innodb_metrics to exporter service
Security Note: Never use chmod 777 on configuration files. MySQL credentials should be readable only by the mysqld_exporter user (chmod 600). If you have permission issues, fix ownership with chown rather than opening permissions.

Next steps

Running this in production?

Need comprehensive database monitoring? Setting up MySQL monitoring once is straightforward. Keeping it tuned, correlating metrics across environments, managing alert fatigue, and responding to incidents 24/7 is the harder operational challenge. See how we run database infrastructure like this for European SaaS and fintech 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.