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.

text
┌─────────────────┐
│  Grafana Alloy  │
│    ┌─────────┐  │     ┌──────────┐
│    │ MySQL 1 │──┼────▶│ mysql-1  │
│    ├─────────┤  │     └──────────┘
│    │ MySQL 2 │──┼────▶│ mysql-2  │
│    ├─────────┤  │     └──────────┘
│    │ PG 1    │──┼────▶│ postgres │
│    └─────────┘  │     └──────────┘
└────────┬────────┘
         │
         ▼
   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.

text
┌────────────┐    ┌────────────┐    ┌────────────┐
│  Region A  │    │  Region B  │    │  Region C  │
│ ┌────────┐ │    │ ┌────────┐ │    │ ┌────────┐ │
│ │ Alloy  │ │    │ │ Alloy  │ │    │ │ Alloy  │ │
│ │   ↓    │ │    │ │   ↓    │ │    │ │   ↓    │ │
│ │ MySQL  │ │    │ │ PG     │ │    │ │ MySQL  │ │
│ └────────┘ │    │ └────────┘ │    │ └────────┘ │
└─────┬──────┘    └─────┬──────┘    └─────┬──────┘
      │                 │                 │
      └─────────────────┼─────────────────┘
                        ▼
                  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

Add a cloud_provider block to correlate your database telemetry with infrastructure metrics from AWS CloudWatch or Azure Monitor. Cloud provider metadata is currently supported for AWS and Azure only.

AWS RDS / Aurora

Alloy
database_observability.mysql "rds" {
  data_source_name = local.file.rds_secret.content
  forward_to       = [loki.relabel.rds.receiver]
  targets          = prometheus.exporter.mysql.rds.targets
  
  cloud_provider {
    aws {
      arn = "arn:aws:rds:us-east-1:123456789012:db:mydb"
    }
  }
}

Azure Database

Alloy
database_observability.postgres "azure" {
  data_source_name = local.file.azure_secret.content
  forward_to       = [loki.relabel.azure.receiver]
  targets          = prometheus.exporter.postgres.azure.targets
  
  cloud_provider {
    azure {
      subscription_id = "your-subscription-id"
      resource_group  = "your-resource-group"
      server_name     = "your-server-name"
    }
  }
}

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: