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.

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.

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:
// ====================
// 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:
// 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:
Recommended labels
Add these labels to organize and filter databases:
Label consistency
Labels must match between loki.relabel and discovery.relabel:
// 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:
- Add the appropriate
cloud_providerblock from the examples below inside your existingdatabase_observability.mysqlordatabase_observability.postgrescomponent, or update it if already present. - 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 MySQL, AWS Aurora MySQL, AWS RDS PostgreSQL, AWS Aurora PostgreSQL
- Azure Database for MySQL, Azure Database for PostgreSQL
- Google Cloud SQL MySQL, Google Cloud SQL PostgreSQL
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.
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
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
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 inproject:region:instanceformat (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:
- Preconfigured dashboards and alerts
- Preconfigured dashboards and alerts for Azure metrics
- Google Cloud Platform observability
Troubleshoot multi-database setups
When databases don’t appear or data doesn’t correlate correctly, start with these multi-database-specific checks:
- Verify that every database has a unique
instancelabel. - Confirm the
instancevalue matches exactly between theloki.relabelanddiscovery.relabelblocks for the same database. - Check that
jobis exactlyintegrations/db-o11yon every component.
For detailed troubleshooting steps, refer to the dedicated guides:
Related documentation
- Tune Alloy collection: Optimize per-database settings
- Labels reference: Complete label documentation
- Verify telemetry status: Validate configuration health per database


