---
title: "Monitor multiple databases | Database Observability documentation"
description: "Configure Database Observability to monitor multiple database instances with deployment patterns and labeling strategies."
---

> For a curated documentation index, see [llms.txt](/llms.txt). For the complete documentation index, see [llms-full.txt](/llms-full.txt).

# 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:

- [Set up MySQL](/docs/grafana-cloud/monitor-applications/database-observability/set-up/mysql/)
- [Set up PostgreSQL](/docs/grafana-cloud/monitor-applications/database-observability/set-up/postgres/)

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 ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```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 (&lt; 20 databases)
- Databases in same network/region
- Development and staging environments

### Distributed Alloy instances

Deploy Alloy alongside each database or group of databases.

text ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```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 ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```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 ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```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:

Expand table

| Label      | Requirement                    | Example                      |
|------------|--------------------------------|------------------------------|
| `instance` | Unique per database            | `orders.db.example.com:3306` |
| `job`      | Must be `integrations/db-o11y` | `integrations/db-o11y`       |

### Recommended labels

Add these labels to organize and filter databases:

Expand table

| Label         | Purpose           | Example values                        |
|---------------|-------------------|---------------------------------------|
| `app`         | Application name  | `order-service`, `user-api`           |
| `team`        | Owning team       | `checkout`, `platform`                |
| `environment` | Deployment stage  | `production`, `staging`, `dev`        |
| `region`      | Geographic region | `us-east-1`, `eu-west-1`              |
| `cluster`     | Database cluster  | `orders-primary`, `orders-replica`    |
| `provider`    | Cloud provider    | `aws`, `gcp`, `azure`, `self-managed` |

### Label consistency

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

Alloy ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```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](/docs/grafana-cloud/monitor-applications/database-observability/reference/labels/).

## 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 ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```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 ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```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:

- [Troubleshoot Alloy](/docs/grafana-cloud/monitor-applications/database-observability/troubleshoot/alloy/)
- [Troubleshoot MySQL](/docs/grafana-cloud/monitor-applications/database-observability/troubleshoot/mysql/)
- [Troubleshoot PostgreSQL](/docs/grafana-cloud/monitor-applications/database-observability/troubleshoot/postgres/)

## Related documentation

- [Tune Alloy collection](/docs/grafana-cloud/monitor-applications/database-observability/configure/tune-alloy-collection/): Optimize per-database settings
- [Labels reference](/docs/grafana-cloud/monitor-applications/database-observability/reference/labels/): Complete label documentation
- [Verify telemetry status](/docs/grafana-cloud/monitor-applications/database-observability/configure/verify-telemetry-status/): Validate configuration health per database
