Grafana Cloud

Monitor multiple databases

As your infrastructure grows beyond a single database, you need to monitor multiple database instances. This guide covers deployment patterns, configuration strategies, and labeling best practices for managing multiple databases with Database Observability.

Before you begin

Complete setup for at least one database:

You also need:

  • Grafana Alloy running and connected to Grafana Cloud
  • Grafana Cloud remote write credentials for Prometheus and Loki

Choose a deployment pattern

Choose a deployment pattern based on your infrastructure and scale requirements.

Single Alloy instance

One Alloy instance monitors all databases.

This diagram shows one Grafana Alloy instance connecting to three databases labeled mysql-1, mysql-2, and postgresql, and sending data to Grafana Cloud.

Advantages:

  • Simple to deploy and manage
  • Single configuration file
  • Lower resource overhead

Disadvantages:

  • Single point of failure
  • Network latency to remote databases
  • Scaling limits

Best for:

  • Small to medium deployments (< 20 databases)
  • Databases in same network/region
  • Development and staging environments

Distributed Alloy instances

Deploy Alloy alongside each database or group of databases.

This diagram shows Alloy deployed next to a database in each of three regions, all sending data to Grafana Cloud.

Advantages:

  • High availability (one Alloy failure doesn’t affect others)
  • Low latency to local databases
  • Scales horizontally
  • Aligns with infrastructure boundaries

Disadvantages:

  • More instances to manage
  • Distributed configuration
  • Higher total resource usage

Best for:

  • Large deployments (20+ databases)
  • Multi-region infrastructure
  • Production environments requiring high availability

Configure multiple databases

Single Alloy with multiple databases

Add a component block for each database:

Alloy
// ====================
// Database 1: MySQL orders
// ====================

local.file "mysql_orders_secret" {
  filename  = "/var/lib/alloy/mysql_orders_dsn"
  is_secret = true
}

prometheus.exporter.mysql "orders" {
  data_source_name  = local.file.mysql_orders_secret.content
  enable_collectors = ["perf_schema.eventsstatements"]
}

database_observability.mysql "orders" {
  data_source_name = local.file.mysql_orders_secret.content
  forward_to       = [loki.relabel.orders.receiver]
  targets          = prometheus.exporter.mysql.orders.targets
}

loki.relabel "orders" {
  forward_to = [loki.write.default.receiver]

  rule {
    target_label = "instance"
    replacement  = "orders.db.example.com:3306"
  }
  rule {
    target_label = "job"
    replacement  = "integrations/db-o11y"
  }
  rule {
    target_label = "app"
    replacement  = "order-service"
  }
}

discovery.relabel "orders" {
  targets = database_observability.mysql.orders.targets

  rule {
    target_label = "job"
    replacement  = "integrations/db-o11y"
  }
  rule {
    target_label = "instance"
    replacement  = "orders.db.example.com:3306"
  }
  rule {
    target_label = "app"
    replacement  = "order-service"
  }
}

prometheus.scrape "orders" {
  targets    = discovery.relabel.orders.output
  forward_to = [prometheus.remote_write.default.receiver]
}

// ====================
// Database 2: PostgreSQL users
// ====================

local.file "pg_users_secret" {
  filename  = "/var/lib/alloy/pg_users_dsn"
  is_secret = true
}

prometheus.exporter.postgres "users" {
  data_source_names  = [local.file.pg_users_secret.content]
  enabled_collectors = ["stat_statements"]
  autodiscovery {
    enabled = true
  }
}

database_observability.postgres "users" {
  data_source_name = local.file.pg_users_secret.content
  forward_to       = [loki.relabel.users.receiver]
  targets          = prometheus.exporter.postgres.users.targets
}

loki.relabel "users" {
  forward_to = [loki.write.default.receiver]

  rule {
    target_label = "instance"
    replacement  = "users.db.example.com:5432"
  }
  rule {
    target_label = "job"
    replacement  = "integrations/db-o11y"
  }
  rule {
    target_label = "app"
    replacement  = "user-service"
  }
}

discovery.relabel "users" {
  targets = database_observability.postgres.users.targets

  rule {
    target_label = "job"
    replacement  = "integrations/db-o11y"
  }
  rule {
    target_label = "instance"
    replacement  = "users.db.example.com:5432"
  }
  rule {
    target_label = "app"
    replacement  = "user-service"
  }
}

prometheus.scrape "users" {
  targets    = discovery.relabel.users.output
  forward_to = [prometheus.remote_write.default.receiver]
}

// ====================
// Shared outputs
// ====================

prometheus.remote_write "default" {
  endpoint {
    url = "https://prometheus-prod-01-us-east-0.grafana.net/api/prom/push"
    basic_auth {
      username = sys.env("GRAFANA_CLOUD_PROMETHEUS_USERNAME")
      password = sys.env("GRAFANA_CLOUD_API_KEY")
    }
  }
}

loki.write "default" {
  endpoint {
    url = "https://logs-prod-us-east-0.grafana.net/loki/api/v1/push"
    basic_auth {
      username = sys.env("GRAFANA_CLOUD_LOKI_USERNAME")
      password = sys.env("GRAFANA_CLOUD_API_KEY")
    }
  }
}

Mixed database types

You can monitor both MySQL and PostgreSQL from the same Alloy instance:

Alloy
// MySQL databases
database_observability.mysql "mysql_1" { /* ... */ }
database_observability.mysql "mysql_2" { /* ... */ }

// PostgreSQL databases
database_observability.postgres "pg_1" { /* ... */ }
database_observability.postgres "pg_2" { /* ... */ }

Each component is independent and can have different configurations.

Choose a labeling strategy

Consistent labels help you filter and organize databases in Database Observability dashboards.

Required labels

These labels must be set correctly for Database Observability to function:

LabelRequirementExample
instanceUnique per databaseorders.db.example.com:3306
jobMust be integrations/db-o11yintegrations/db-o11y

Add these labels to organize and filter databases:

LabelPurposeExample values
appApplication nameorder-service, user-api
teamOwning teamcheckout, platform
environmentDeployment stageproduction, staging, dev
regionGeographic regionus-east-1, eu-west-1
clusterDatabase clusterorders-primary, orders-replica
providerCloud provideraws, gcp, azure, self-managed

Label consistency

Labels must match between loki.relabel and discovery.relabel:

Alloy
// Logs labels
loki.relabel "mydb" {
  rule {
    target_label = "instance"
    replacement  = "mydb.example.com:3306"
  }
  rule {
    target_label = "environment"
    replacement  = "production"
  }
}

// Metrics labels - must match!
discovery.relabel "mydb" {
  rule {
    target_label = "instance"
    replacement  = "mydb.example.com:3306"  // Same value
  }
  rule {
    target_label = "environment"
    replacement  = "production"  // Same value
  }
}

For complete label guidance including best practices, environment variables, and troubleshooting, refer to the Labels reference.

Add cloud provider integrations

Connect database telemetry with infrastructure metrics from AWS CloudWatch, Azure Monitor, or Google Cloud Monitoring using Cloud Provider Observability.

Before you begin, configure Cloud Provider Observability to collect metrics for your database’s cloud resource.

If you edit your Alloy configuration directly:

  1. Add the appropriate cloud_provider block from the examples below inside your existing database_observability.mysql or database_observability.postgres component, or update it if already present.
  2. Apply the updated configuration to your Alloy instance.

You can also generate your configuration from the Database Observability Configuration > Setup page. For guided setup instructions, Helm values, or complete Alloy configuration examples, refer to your platform’s setup guide:

AWS RDS / Aurora

Note

For an Aurora cluster, configure Grafana Alloy separately for each DB instance and connect to its instance endpoint. Do not use the cluster (writer), reader, or custom endpoint because these endpoints can route connections to different instances and break metric correlation.

Alloy
database_observability.mysql "mysql_<DB_NAME>" {
  data_source_name = local.file.mysql_secret_<DB_NAME>.content
  forward_to      = [loki.relabel.database_observability_mysql_<DB_NAME>.receiver]
  targets         = prometheus.exporter.mysql.mysql_<DB_NAME>.targets

  // Add this block inside your existing database_observability.mysql or database_observability.postgres component.
  cloud_provider {
    aws {
      arn = "<AWS_RDS_INSTANCE_ARN>"
    }
  }
}

Replace the placeholders:

  • AWS_RDS_INSTANCE_ARN: AWS RDS instance ARN for cloud provider integration (for example, arn:aws:rds:us-east-1:123456789012:db:mydb).

Azure Database

Alloy
database_observability.mysql "mysql_<DB_NAME>" {
  data_source_name = local.file.mysql_secret_<DB_NAME>.content
  forward_to      = [loki.relabel.database_observability_mysql_<DB_NAME>.receiver]
  targets         = prometheus.exporter.mysql.mysql_<DB_NAME>.targets

  // Add this block inside your existing database_observability.mysql or database_observability.postgres component.
  cloud_provider {
    azure {
      subscription_id = "<AZURE_SUBSCRIPTION_ID>"
      resource_group  = "<AZURE_RESOURCE_GROUP>"
      server_name     = "<AZURE_SERVER_NAME>"
    }
  }
}

Replace the placeholders:

  • AZURE_RESOURCE_GROUP: Azure resource group containing your database server.
  • AZURE_SUBSCRIPTION_ID: ID of the Azure subscription containing your database server.
  • AZURE_SERVER_NAME: Name of your Azure database server (optional).

Google Cloud SQL

Alloy
database_observability.mysql "mysql_<DB_NAME>" {
  data_source_name = local.file.mysql_secret_<DB_NAME>.content
  forward_to      = [loki.relabel.database_observability_mysql_<DB_NAME>.receiver]
  targets         = prometheus.exporter.mysql.mysql_<DB_NAME>.targets

  // Add this block inside your existing database_observability.mysql or database_observability.postgres component.
  cloud_provider {
    gcp {
      connection_name = "<CLOUDSQL_CONNECTION_NAME>"
    }
  }
}

Replace the placeholders:

  • CLOUDSQL_CONNECTION_NAME: Cloud SQL instance connection name in project:region:instance format (for example, my-project:us-central1:my-db). You can find it on the instance details page in the Google Cloud console.

For navigation between query performance and cloud infrastructure metrics, refer to Access Database Observability in the Cloud Provider Observability documentation for your platform:

Troubleshoot multi-database setups

When databases don’t appear or data doesn’t correlate correctly, start with these multi-database-specific checks:

  1. Verify that every database has a unique instance label.
  2. Confirm the instance value matches exactly between the loki.relabel and discovery.relabel blocks for the same database.
  3. Check that job is exactly integrations/db-o11y on every component.

For detailed troubleshooting steps, refer to the dedicated guides: