Overview
Grafana Multi-Location deploys one logical Grafana whose UI/API instances run in every location you configure, behind a single georouted*.cpln.app endpoint. Every dashboard, user, org, session, alert rule and saved datasource lives in a bundled postgres-multi-location cluster stretched across the same locations, so Grafana itself holds no state — there is no volume, no session affinity and nothing to hand over when an instance is replaced.
Alerting comes in two shapes, chosen with alerting.highAvailability.enabled: a single dedicated evaluator pinned to one location (the default), or every instance evaluating with a stretched Redis tier making sure exactly one of them sends each notification. See Alert Evaluation before choosing.
global.locations must already be one of them. GVC locations you do not list are left alone: nothing from this release runs there. For a Grafana inside a single location, use the Grafana template instead.What Gets Created
Prerequisites
Have a GVC with at least 2 locations
global.locations one of them:spec.staticPlacement.locationLinks with your values. The database commits a write only when a majority of its members agree, and it runs one member per location, so the location count decides what survives:floor((N-1)/2) losses, so an even count buys nothing over the odd count below it. alerting.highAvailability.enabled: true requires at least 3 locations, and the chart refuses to render below that.Create the first-boot admin password
plain. Grafana reads it only when the admin account is first created:admin.passwordSecretName to this name.Create the encryption key
plain. Every instance in every location reads it on every boot to decrypt datasource credentials stored in the shared database — back it up outside Control Plane and never rotate it:admin.secretKeySecretName to this name.Create the app database credentials
username, password and database. Use plain identifiers for username and database — they are used unquoted when the database is created:postgresML.postgres.credentialsSecretName to this name.Installation
Once the three secrets exist, install the template into the GVC with the required secret names:global.locations list is aws-us-east-1, aws-eu-central-1 and aws-us-west-2. If your GVC’s locations differ, supply your own list in a values file (--values) alongside the command above, and keep alerting.location and postgresML.primaryLocation pointing at one of the locations you list.
UI
CLI
Terraform
Pulumi
Configuration
Locations
global so the bundled database, its consensus store and the optional Redis tier all receive the same list — there is never a second list to maintain. Removing a location from the list is the supported way to shrink the deployment; replicas must stay at least 1.
Both Grafana tiers read the GVC at boot and log a [grafana] WARNING: line naming any location in global.locations that the GVC does not have. See Troubleshooting for what that means for alerting.
Grafana UI Tier
image must provide /bin/bash — the chart wraps Grafana’s own entrypoint in a boot script; the default official image does. Signup, anonymous access and upstream analytics are disabled by the template and are not configurable. With alerting HA on, rule evaluation runs on this tier, so a large rule set may need resources.maxCpu raised.
Alerting
highAvailability — it multiplies your data-source query load by the number of Grafana instances. alerting.enabled: false combined with highAvailability.enabled: true fails at render time.
Admin Credentials and Encryption Key
Datasources as Code
definitions are standard Grafana datasource provisioning entries, rendered verbatim into a file that every Grafana instance applies at boot. The file is plaintext, so credentials never go into it: create a dictionary secret before installing, list it under credentialSecrets, and write $KEY in the definition. Each listed key becomes an environment variable the chart grants the identity reveal on, and Grafana interpolates it when it loads the file:
SMTP
smtp.passwordSecretName is required whenever smtp.user is set:
Access
publicAccess applies to the UI tier only; with it on, links in alert notifications point at the public endpoint. The dedicated alert evaluator is never reachable from the internet and honours internalAccess only. With alerting HA on there is no evaluator, and the Redis tier is reachable from inside the GVC only.
Allow several minutes after changing an access knob before concluding it did not apply.
App Database
postgresML block configures the bundled postgres-multi-location cluster: one primary, asynchronous replicas in the other locations, an HAProxy leader-routing tier in each, and automatic promotion in a surviving location. Everything else the database tier can do — pooling, per-member addressing, emergency quorum recovery — is documented on that template’s page, and its backup settings are covered under Backing Up.
primaryLocation is a preference: if that location is slow to start, another one bootstraps the cluster and the primary lands there. Grafana has no read/write splitting, so every query, including every dashboard load, goes to the single primary — set primaryLocation where most of your users are, because every other location pays one cross-region round trip per query.
Leave the etcd compaction settings alone: a consensus store that fills its backend goes read-only and takes the database’s failover with it. See Compaction and Backend Growth for the mechanism and accepted value formats.
Alerting HA Coordination
Rendered only whenalerting.highAvailability.enabled is true; ignored entirely otherwise.
redis-multi-location subchart: one Sentinel and replicasPerLocation Redis members per location. engine: valkey runs both tiers on valkeyImage (the BSD-licensed fork of Redis 7.2, with redis-server / redis-sentinel compatibility symlinks), at which point redis.image and sentinel.image are inert.
Authenticating the Redis Tier
Authentication is off by default: enabling alerting HA is one flag and needs no extra secrets, and the tier is reachable only from inside the GVC. That GVC is yours and may already hold other workloads, so weigh the default accordingly — write access to an authless Redis is enough to suppress your alert notifications, because a hostile or buggy neighbour can claim another peer already sent them. Turn authentication on unless you control everything in the GVC. Create either or both opaque secrets with encodingplain before installing, then name them:
Connecting
curl:
*.cpln.app endpoint in browsers, not a per-location hostname: Grafana’s root URL is the canonical endpoint, and on a per-location hostname the dashboard layout loads but panel data does not.
First Run
Sign in on the public endpoint asadmin.user with the password from the admin password secret. Grafana ships no dashboards and no datasources; connect your own in the UI or as code. Once you have signed in, change the password in the UI if you wish, then set admin.applyPassword: false and upgrade — Grafana only applies the secret when the account is first created, and dropping the reference lets you delete that secret.
Operations
Backing Up
Backups are off by default and cover the app database — the dashboards, users, alert rules and saved datasources that make up your Grafana. Enable them withpostgresML.backup.enabled: true, choose a mode, and complete the storage setup for your provider before installing.
logical runs a scheduled pg_dumpall as a cron workload in the one location named by postgresML.backup.location; wal-g archives continuously from whichever member is currently the primary. Both come from the postgres-multi-location subchart and were exercised in that template’s testing, not through this one.
- AWS S3
- Google Cloud Storage
- MinIO / S3-compatible
Create a bucket
postgresML.backup.aws.bucket and postgresML.backup.aws.region to match.Set up a Cloud Account
postgresML.backup.aws.cloudAccountName to its name.Create a bucket-scoped IAM policy
YOUR_BUCKET_NAME), then set postgresML.backup.aws.policyName to the policy’s name. No broad managed policy is required.Choose a prefix
postgresML.backup.aws.prefix to the folder path within the bucket.Restoring a Backup
A restore goes into the database, not into Grafana: everything that makes up your Grafana lives there, so restoring it moves dashboards, users, datasources, alert rules and alert state together. The restore procedures belong to the bundled database — follow Restoring a Backup on thepostgres-multi-location page, substituting this release’s proxy address, RELEASE_NAME-postgres-proxy.GVC_NAME.cpln.local:5432, and the credentials from your database credentials secret.
Two Grafana-specific rules apply to any restore:
- Restore into a cluster that uses the same encryption key.
admin.secretKeySecretNamedecrypts the datasource credentials stored in the dump; a different key makes every saved datasource credential unreadable. - A zero-length backup object is a failed run, not a backup. Check the object size before restoring from it — a real dump is kilobytes at minimum.
Migrating From Version 1
Every release before 2.0.0 created its own GVC. 2.0.0 creates none and deploys into the GVC you install it into. The refusal message usually namesetcd-multi-location or postgres-multi-location rather than this chart, because every chart in the stack carries the same guard on the same key and a subchart’s copy aborts the render first. The remedy is identical.
Install 2.0.0 as a new release against an existing GVC, restore the database into it, then remove the old release:
Back up the 1.x database
postgresML.backup.mode: logical on the old release, or run pg_dumpall against the old release’s RELEASE_NAME-postgres-proxy address from a client workload in that GVC. This one dump carries everything that makes up your Grafana.Rewrite your values
global.gvc.name and rename global.gvc.locations to global.locations. Every location you list must already exist in the GVC you are installing into.Install 2.0.0 as a new release into an existing GVC
admin.secretKeySecretName at the same secret the old release used.Restore the dump and move your users
Remove the old release
cpln helm uninstall the old release against the GVC you installed it into. That takes the GVC it created, and the old volume sets, with it.Alert Evaluation
alerting.enabled is the parent switch. Setting it to false creates no evaluator workload and turns rule execution off on every instance: alert rules and contact points can still be created and viewed, and nothing ever evaluates them. While it is on, alerting.highAvailability.enabled picks one of two shapes:
alerting.location; nothing is elected at runtime, and no value of replicas can produce a second evaluator. Losing that replica self-heals — the platform reschedules it — but the replacement re-sends whatever is firing when it comes back, so a restart costs duplicate notifications rather than missed ones. Losing the whole location does not self-heal: evaluation stops until you run helm upgrade with alerting.location set to a surviving location, and the UI gives no sign of it. Moving alerting.location restarts only the evaluator and never runs two at once. Silences are not expected to propagate from the UI tier to the evaluator (not exercised in testing); to be safe, manage silences against the evaluator directly, from a workload running inside alerting.location — the address returns 503 from other locations:
- Which location sends is not pinnable. The sender moves between rollouts and between installs of the same chart, so do not build routing, filtering or egress-IP allowlisting on the assumption that a particular location sends.
- An unhealthy Redis tier means duplicate notifications, never silence. Each instance falls back to sending its own copy and re-converges on its own when Redis returns. Any
helm upgradewith HA on — including one that changes nothing — also opens a brief duplicate window while the Redis tier is patched.
RELEASE_NAME-grafana-alerting and adds the Redis and Sentinel workloads in every location; disabling it does the reverse. Treat it as a planned change, not a toggle to flip during an incident.
Scaling and Availability
replicasscales the UI tier per location, in every location, with no alerting-related restriction. The only limit is the connection budget under Grafana UI Tier.global.locations[].replicasscales the database members instead.- Location loss. With 3 or more locations the database fails over automatically and the endpoint keeps serving from the surviving locations; the location count table under Prerequisites gives the arithmetic. What alerting does depends on the mode — see Alert Evaluation.
- Every
helm upgradethat touches the database tier is a planned outage. The database members restart together rather than one at a time, and while the database is down every Grafana instance reports unhealthy and the endpoint returns503. The first upgrade after any install re-applies every tier once, even when nothing changed; later upgrades that only touch the Grafana workloads roll without downtime. - An upgrade that adds a new secret reference (enabling
smtpwith a password, addingdatasources.credentialSecrets, turning on backups) can pause the rollout for several minutes while Helm reports success, withThe identity ... is not allowed to reveal the secret ...in the deployment status even though the grant is in place. It clears on its own; do not edit policies. Confirm the new version is live withcpln workload get-deployments RELEASE_NAME-grafana --gvc GVC_NAME -o yamlrather than trusting the Helm output. - Rotating a prerequisite secret does not restart anything. After changing a secret’s value, run
cpln workload force-redeployment RELEASE_NAME-grafana --gvc GVC_NAME(and the same forRELEASE_NAME-grafana-alertingin dedicated-evaluator mode) so the instances pick it up. Never rotate the encryption key. - Removing a location means removing it from
global.locationsand from the GVC. Never suspend a location on these workloads; use the values list. - Uninstall deletes every volume set the chart created — the database, the consensus store and, with alerting HA on, Redis and Sentinel. The GVC is yours and is left alone; the prerequisite secrets you created survive. Enable backups if the data matters.
Troubleshooting
A workload sits at 0 replicas after install while helm reported success
A workload sits at 0 replicas after install while helm reported success
cpln helm install succeeded, but RELEASE_NAME-grafana (and possibly the database or Redis tiers) show no containers and cpln logs returns nothing.Cause: A prerequisite secret named in your values does not exist — the admin password, the encryption key, the database credentials, or a redisML.*.passwordSecretName. The deployment pauses on the unresolved reference.Fix: Read the message naming the secret, then create it:The secret <name> no longer exists. Workload updates are paused until the secret is added or the reference to the secret removed. Once the secret exists the deployment recovers on its own, or run cpln workload force-redeployment RELEASE_NAME-grafana --gvc GVC_NAME to skip the wait.WARNING locations declared in global.locations are not in GVC
WARNING locations declared in global.locations are not in GVC
[grafana] WARNING: locations declared in global.locations are not in GVC '...', and a location you expected to serve traffic runs nothing.Cause: global.locations names a location the GVC does not have. The platform accepts and stores it without error, so nothing runs there and no deployment reports a failure. On a fresh install the database tier refuses to bootstrap in that state and crash-loops with a FATAL: line naming the location; on an install whose GVC lost a location later, the stack keeps serving and only this warning shows.Fix: Add the location to the GVC or remove it from global.locations, then upgrade. If the missing location is alerting.location, the RELEASE_NAME-grafana-alerting workload holds zero replicas everywhere and no alert rule is evaluated while every dashboard looks healthy — the warning says so explicitly. Check with:Alert rules never fire but dashboards are healthy
Alert rules never fire but dashboards are healthy
alerting.enabled is false, or you are in dedicated-evaluator mode and alerting.location is a location the GVC does not have (see the previous entry), or that location has been lost.Fix: Set alerting.enabled: true; repoint alerting.location to a surviving location listed in both your values and the GVC and upgrade; or enable alerting.highAvailability.enabled so evaluation runs in every location.Every alert is delivered twice with alerting HA on
Every alert is delivered twice with alerting HA on
RELEASE_NAME-redis or RELEASE_NAME-sentinel workloads are down, fewer than a majority of the configured locations exist in the GVC, or a workload-list firewall on the Redis tier does not include the Grafana workloads. Grafana logs Failed to look up position, falling back to position 0. It never goes silent, so the alert rules themselves are fine.Fix: Check those workloads with cpln workload get-deployments RELEASE_NAME-redis --gvc GVC_NAME -o yaml, restore the missing locations, or add the printed links to redisML.firewall.workloads. A brief duplicate window right after a helm upgrade is expected and clears on its own.Failed to lock database on a cold start
Failed to lock database on a cold start
Failed to lock database and are restarted.Cause: Grafana runs its schema migrations under a non-blocking lock; an instance arriving while another holds it exits, and the next attempt succeeds against the migrated schema. The chart staggers the instances so this is rare at one replica per location; two replicas in the same location can still race.Fix: None needed. If it keeps happening for minutes, check that the database primary landed in postgresML.primaryLocation — migrations run across regions otherwise and take longer.failed to send email unencrypted connection
failed to send email unencrypted connection
failed to send email: unencrypted connection.Cause: smtp.user is set and the relay does not offer STARTTLS or TLS. Grafana refuses to send credentials over an unencrypted connection and drops the notification.Fix: Use a relay that offers STARTTLS or TLS, or leave smtp.user empty to send unauthenticated against a plain relay.A provisioned datasource reports upstream connect error connection timeout
A provisioned datasource reports upstream connect error connection timeout
datasources.definitions show only upstream connect error ... connection timeout.Cause: Almost always an unsubstituted placeholder in the datasource URL — the workload name or the GVC segment of http://YOUR_WORKLOAD.YOUR_GVC.cpln.local:PORT was left literal. A name that does not resolve times out rather than failing fast, so it looks like a network problem.Fix: Correct datasources.definitions[].url and upgrade.Dashboards load but every panel is empty
Dashboards load but every panel is empty
*.cpln.app endpoint from status.canonicalEndpoint. It is georouted and already serves from the nearest location.Render fails with the global.gvc values key was REMOVED
Render fails with the global.gvc values key was REMOVED
helm install or helm upgrade aborts before touching anything, with a message naming etcd-multi-location, postgres-multi-location or this chart and the text the global.gvc values key was REMOVED.Cause: Your values file is from 1.x, which named a GVC under global.gvc.Fix: Do not retry as an upgrade. Follow Migrating From Version 1.Render fails because the database connection budget is exceeded
Render fails because the database connection budget is exceeded
the database connection budget is exceeded — N Grafana instances ... above the 80-connection ceiling.Cause: (replicas × locations + 1) × database.maxOpenConn is above 80.Fix: Lower database.maxOpenConn or lower replicas; the message spells out the arithmetic.Render fails because a subchart workload-list does not include the Grafana workloads
Render fails because a subchart workload-list does not include the Grafana workloads
postgresML.internalAccess.type is 'workload-list' but ... does not include or the redisML.firewall.internalAllowType equivalent.Cause: The database and Redis tiers keep their own firewall lists and do not add Grafana automatically; without the links Grafana could never reach its database or its alerting coordinator.Fix: Paste the links printed in the message into postgresML.internalAccess.workloads or redisML.firewall.workloads.Important Notes
- Create the admin password, encryption key and database credentials secrets before installing. Without them the deployment waits silently on secrets that do not exist while
helm installreports success. - There is no upgrade path from 1.x. A
helm upgradeacross the 2.0.0 boundary deletes the old GVC and everything in it. Follow Migrating From Version 1. - Every location in
global.locationsmust already exist in the GVC you install into, andalerting.locationandpostgresML.primaryLocationmust be among them. GVC locations you do not list run nothing. - Never rotate or delete the encryption key. Every stored datasource credential becomes unreadable in every location, and alert rules that query them fail.
- Treat every
helm upgradethat touches the database tier as a planned outage, and expect the first upgrade after any install to re-apply every tier. - Alert evaluation stops if you lose
alerting.location, and the UI will not show it. Repoint the knob and upgrade, or enable alerting HA — after weighing its query-load cost. - Alerting HA multiplies data-source query load by (locations × replicas) and is blocked below 3 locations on purpose.
- With alerting HA on, an unhealthy Redis tier means duplicate notifications, never silence — check the
RELEASE_NAME-redisandRELEASE_NAME-sentinelworkloads before suspecting your rules. - The alerting-HA Redis tier is unauthenticated by default. Authenticate it unless you control everything in the GVC — see Authenticating the Redis Tier.
- Setting a subchart firewall to
workload-listrequires listing the Grafana workloads in it; the chart refuses to render otherwise and prints the links. - Use the canonical
*.cpln.appendpoint, not a per-location hostname — panels stay empty otherwise. - Set
postgresML.primaryLocationwhere most of your users are. Grafana has no read/write splitting, so every other location pays a cross-region round trip per query. - After rotating a prerequisite secret, force a redeployment of the Grafana workloads; nothing picks the new value up on its own.
- Uninstall deletes the database volume sets. Enable backups if the data matters; your prerequisite secrets survive.
- This template ships Grafana OSS only — Enterprise features such as fine-grained RBAC, reporting and query caching are not available.