Overview
Grafana is the open-source (AGPL-3.0) dashboarding and alerting front end for time-series and SQL data. This template deploys Grafana OSS as a stateless app tier backed by a bundled PostgreSQL app database — every dashboard, user, alert rule and saved datasource lives in that database, so the Grafana workload itself holds no state and can run more than one replica. It exists to visualize data you own: the catalog’s Prometheus, Thanos and Mimir installs, your own SQL databases, and external systems. It is not an observability stack in itself — it ships no collectors, no exporters and no data.What Gets Created
Prerequisites
Three secrets must exist before you install: a dictionary secret with the app-database credentials, and two opaque secrets for the admin password and the encryption key. Grafana’s admin account is a human-facing login, andpublicAccess.enabled defaults to true, so that account is reachable from the internet — none of these credentials is a Helm value, and none ever lands in the release. Secrets are org-level, so no --gvc flag is involved.
Create the app-database credentials secret
postgresHA.config.credentialsSecretName (HA, the default) or postgres.config.credentialsSecretName (single-instance) to this name. Secret names are org-wide, so give each release its own. PostgreSQL reads it only when its data directory is first initialized — see Rotating the Database Password.Create the admin password secret
plain. Grafana reads it only when the admin account is first created:admin.passwordSecretName to this name.Create the encryption key secret
admin.secretKeySecretName to this name.Installation
Once the three secrets exist, install the template into your GVC, naming the secrets:postgres.config.credentialsSecretName instead — see App Database.
UI
CLI
Terraform
Pulumi
Configuration
Grafana Instance
replicas of 2 or more forms an HA tier over the shared app database and requires redis.enabled: true — see Alerting High Availability. image must provide /bin/bash: the chart wraps Grafana’s own entrypoint in a short boot script, and the official image does. User signup, anonymous access and upstream analytics and update checks are disabled by the template and are not configurable.
Admin Credentials
admin.applyPassword: false the chart drops the password secret from both places it appeared — the container environment and the reveal policy grant — and the encryption-key reference is kept. The retirement sequence is under First Run.
Datasources as Code
datasources.definitions additionally lets you declare them as code: each entry is a standard Grafana datasource provisioning entry, rendered verbatim into a provisioning file that the workload mounts at startup. Provisioned datasources are read-only in the Grafana UI — change the value and upgrade the release to change one. For catalog services in the same GVC, use their internal endpoints, substituting both the workload name and the GVC:
Datasource Credentials
datasources.credentialSecrets holds the credentials Grafana authenticates to each datasource with — not user access to Grafana. The provisioning 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 of that name, the chart grants the Grafana identity reveal on exactly that secret, and Grafana interpolates the variable when it loads the file.
$PG_PASSWORD placeholder — the value stays in the secret. Every entry needs a name and a non-empty keys list; the chart refuses to render otherwise.
Control Plane Metrics as a Datasource
Control Plane’s own metrics store is Prometheus-compatible and holds more than the console’s built-in dashboards display, including custom metrics your workloads expose and cost-relevant series such asegress, cross_zone_traffic and volume_set_*. Adding it as a datasource complements the built-in workload dashboards rather than replacing them. Follow the centralized metrics management guide for the endpoint details, then wire the token in as a datasource credential:
Create a service account with readMetrics
readMetrics permission on the org and generate a key for it. A workload’s built-in CPLN_TOKEN does not authenticate against the metrics endpoint.Store the key in a dictionary secret
CPLN_METRICS_TOKEN — and list the secret under datasources.credentialSecrets.Add the datasource definition
prometheus-type datasource at https://metrics.cpln.io/metrics/org/YOUR_ORG and send the token as an Authorization header:SMTP
smtp.enabled: false if you use webhook, chat or other contact points instead. smtp.passwordSecretName is required whenever smtp.user is set — the chart refuses to render without it. The password stays in an opaque secret you create before installing, and the chart grants the Grafana identity reveal on exactly that secret:
Access
publicAccess.enabled— Serve the UI and API on the canonical*.cpln.appHTTPS endpoint (default). Everything behind the endpoint is gated by Grafana’s own login, and Grafana’sroot_urlis set to that endpoint automatically. Set it tofalsefor an internal-only instance — external requests are then refused while in-GVC callers still reach it perinternalAccess, and you reach the UI yourself withcpln port-forward(see Connecting).internalAccess.type— Internal firewall scope of the Grafana workload:
App Database
Exactly one of the two database modes must be enabled — the chart enforces this at render and refuses to install otherwise.username, password and database — see Prerequisites), named by the credentialsSecretName of the database you enable. Grafana is wired to the active database automatically: the HAProxy leader endpoint in HA mode, or the single instance directly. In HA mode the proxy must stay enabled — that endpoint is Grafana’s stable database address, and the chart refuses to render with it disabled.
Everything else the bundled databases can do — volume autoscaling, PgBouncer pooling, etcd tuning — is documented on the postgres-highly-available and postgres pages and is set under the same postgresHA.* / postgres.* prefixes. Their backup settings are covered under Backing Up.
Alerting High Availability
Grafana holds no local state, so scaling the app tier is a matter of raisingreplicas. Alert evaluation is the one thing that must be coordinated: without coordination every replica would evaluate the same rule and send its own notification.
- Coordination uses Grafana’s Redis Sentinel alerting HA mode against the Sentinel workload deployed by the bundled redis subchart (master name
mymaster), not gossip-based peer clustering. Each replica registers under its own peer name, and exactly one notification is delivered per firing alert. - The chart refuses to render
replicas >= 2withoutredis.enabled: true. - Redis and Sentinel ship without authentication — the same-GVC internal firewall is the boundary. Enabling Redis or Sentinel authentication under
redis.redis.authorredis.sentinel.authis rejected at render in this version. - Grafana Live push updates are per-instance in multi-replica mode; dashboard auto-refresh and alerting are unaffected.
Connecting
curl:
publicAccess.enabled: false there is no canonical endpoint; open a tunnel to the UI instead and browse to http://localhost:3000:
First Run
Sign in on the public endpoint asadmin.user (default admin) with the password from the admin password secret. Grafana ships no dashboards and no datasources — connect your own in the UI or as code. Grafana applies GF_SECURITY_ADMIN_PASSWORD only when the admin account is first created: on every later boot the secret’s value is ignored, so changing the secret never changes the login, and an upgrade never resets the password you set in the UI. Once you have logged in, the password secret is inert and you can retire it:
Log in once
admin.user with the password from the secret and confirm you can reach the UI. Change the password in the UI if you wish — that is the only place it changes from now on.Set applyPassword to false and upgrade
admin.applyPassword: false, the chart drops the password secret from both the container environment and the reveal policy grant. The encryption-key reference is retained.Delete the secret
Operations
Backing Up
Database backups are optional and disabled by default. They cover the app database — the dashboards, users, alert rules and saved datasources that make up your Grafana. Enable them on whichever database is active and complete the storage setup for your provider before installing.postgresHA.backup.mode selects logical (a scheduled pg_dump run by the RELEASE_NAME-postgres-ha-backup cron workload) or wal-g (continuous WAL archiving from the database workload itself, with no separate backup workload). The single-instance database takes scheduled logical dumps only. Both come from the bundled postgres-highly-available and postgres subcharts. The steps below name the keys under postgresHA.backup.*; in single-instance mode set the same keys under postgres.backup.*.
- AWS S3
- Google Cloud Storage
- MinIO / S3-compatible
Create a bucket
postgresHA.backup.aws.bucket and postgresHA.backup.aws.region to match.Set up a Cloud Account
postgresHA.backup.aws.cloudAccountName to its name.Create a bucket-scoped IAM policy
YOUR_BUCKET), then set postgresHA.backup.aws.policyName to the policy’s name. No broad managed policy is required.Restoring a Backup
A restore goes into the database, not into Grafana: everything that makes up your Grafana lives there, so restoring it brings back dashboards, users, datasources and alert rules together. The restore procedures belong to the bundled database — follow Restoring a Backup on thepostgres-highly-available page in HA mode, or Restoring a Backup on the postgres page in single-instance mode, substituting this release’s database address (RELEASE_NAME-postgres-ha-proxy.GVC_NAME.cpln.local or RELEASE_NAME-postgres.GVC_NAME.cpln.local, port 5432) and the credentials from your database credentials secret:
admin.secretKeySecretName decrypts the datasource credentials stored in the dump, and a different key makes every saved datasource credential unreadable.
Rotating the Database Password
PostgreSQL keeps the password it was initialized with, and a running workload keeps the values it started with, so in single-instance mode a rotation takes three steps in order (in HA mode follow Rotating the Database Password in HA Mode instead):-
Change it in the database:
ALTER ROLE DB_USERNAME PASSWORD 'NEW_PASSWORD';(DB_USERNAMEis the secret’susername; connect as that user from inside the GVC). -
Update the secret — export it, change only
password, and apply it: -
Right away, redeploy Grafana:
ALTER ROLE breaks Grafana’s login to the database.
Rotating the Database Password in HA Mode
The HA database (postgresHA) needs its own order. The replication role repl shares the password, and a member that restarts with the old password can be promoted to leader, which splits the cluster. Move the leader to member -0 first, so it restarts last, after every other member already has the new password. pgx runs a command in one member (LOCATION is your GVC’s location, DB_USERNAME the secret’s username):
RELEASE_NAME-grafana, which keeps the old password until it restarts. The database drops connections briefly at each switchover, and Grafana cannot open new connections from the ALTER ROLE until its own redeploy finishes, so rotate in a quiet window. If a member stays starting, or running without streaming, re-copy it from the leader. This copies the whole database, so it costs network traffic in proportion to its size:
Upgrading from 1.1.0 or Earlier
Version 1.2.0 moved the admin password and the encryption key out of Helm values (admin.password, admin.secretKey) into the two prerequisite opaque secrets described under Prerequisites, and stopped creating the chart-owned admin secret those values used to feed. Version 1.1.0 had already renamed resources.cpu and resources.memory to resources.maxCpu and resources.maxMemory. The old keys are not rejected at render — they are simply ignored — so remove them deliberately.
Create the encryption key secret with the key the install already uses
admin.secretKey. Any other value makes every datasource credential already saved in the app database undecryptable:admin.secretKeySecretName to this name.Decide what to do with the admin password
admin.applyPassword: false and create no password secret at all (the chart requires admin.passwordSecretName only while applyPassword is true), or create one as under Prerequisites and retire it later.Remove the old keys and upgrade
admin.password and admin.secretKey from your values (and rename resources.cpu / resources.memory if you still carry them), then upgrade. Every later migration note below also applies.Upgrading from 1.2.0 or Earlier
Template versions before 1.2.1 did not compact the etcd cluster inside the bundled highly available database, so etcd’s backend grows with time alone and eventually goes read-only when it reaches its quota — taking PostgreSQL failover with it. Only installs running the HA database are affected (postgresHA.enabled, the default). See etcd History Compaction for the mechanism and symptoms. Upgrading to 1.2.1 or later turns compaction on; that stops further growth but cannot shrink a backend that has already grown, and a cluster that has already raised a NOSPACE alarm needs operator recovery rather than an upgrade.
Upgrading from 1.2.1 or Earlier
Version 1.3.0 removed the single-instance database credential keyspostgres.config.username, postgres.config.password and postgres.config.database. postgres.backup.minio.accessKey and postgres.backup.minio.secretKey were replaced by postgres.backup.minio.credentialsSecretName at the same time. Carrying the old keys stops the upgrade at render, before anything is applied:
postgres.config.credentialsSecretName to its name, and remove the three keys. If you back up to MinIO, create the dictionary secret described under Backing Up and set postgres.backup.minio.credentialsSecretName. The HA path, Redis and the admin and encryption-key secrets are unchanged by this step.
Upgrading from 1.3.0
Version 1.4.0 made the same change on the HA path:postgresHA.postgres.username, postgresHA.postgres.password and postgresHA.postgres.database were removed, and the bundled highly available database now reads the dictionary secret named by postgresHA.config.credentialsSecretName. Carrying the old block stops the upgrade at render:
postgresHA.postgres.*, put the same values the cluster already has into the app-database credentials secret under Prerequisites, and set postgresHA.config.credentialsSecretName to its name. If you run several releases in one org, give each its own secret.
Upgrading from 1.4.0
Version 1.4.1 replacedpostgresHA.backup.minio.accessKey and postgresHA.backup.minio.secretKey with postgresHA.backup.minio.credentialsSecretName. Only installs backing up the HA database to MinIO or another S3-compatible endpoint are affected; the old keys stop the upgrade at render:
postgresHA.backup.minio.credentialsSecretName to its name, remove the two old keys, and upgrade.
Upgrading from 1.4.1
Version 1.5.0 removedpostgres.credentials: the database credentials are now a prerequisite secret, the same one the bundled database template requires on its own. Carrying the old key forward fails the render.
1.4.1 created the database secret itself, so this upgrade deletes it. Before upgrading, create a secret under a new name holding the values your database was initialized with, not new ones, or Grafana loses its database connection:
postgres.credentials removed and the credentialsSecretName of the mode you use (postgresHA.config.credentialsSecretName by default) set to that name. Your current values are in postgres.credentials of the 1.4.1 release, and also in the old secret (cpln secret reveal my-grafana-db-credentials -o yaml, or the name your release set in credentialsSecretName) until the upgrade deletes it.
The upgrade restarts the bundled PostgreSQL too (it now reads the new secret), so Grafana can briefly lose its database; it reconnects by itself. The new version of a workload can also wait several minutes with is not allowed to reveal the secret in get-deployments while access to the new secret propagates; the previous version keeps serving meanwhile, and it clears by itself.
Upgrading from 1.5.0 or Earlier
1.5.1 moves the HA database (postgresHA) to controlplanecorporation/patroni-postgres:0.8, which carries pgvector 0.8.7 (CVE-2026-103484). The fix takes effect when the upgrade restarts the Patroni members; existing data needs nothing. Nothing limits that rollout, so expect a brief interruption of writes while the leader changes. Grafana itself restarts without a gap (the new replica serves before the old one stops); while the leader changes, Grafana can briefly answer valid users with 401. Upgrade in a quiet window. If you pinned postgresHA.image in your values, set it to the 0.8 tag: tags 0.1 to 0.7 are refused at render. Grafana does not use the vector extension; if you created it yourself in a database, \dx keeps reporting the old version until you run ALTER EXTENSION vector UPDATE; there, which only updates that record. Single-instance mode (postgres) is unchanged.
Scaling and Availability
replicasscales the Grafana tier;2or more requiresredis.enabled: true— see Alerting High Availability. Database replicas are set separately withpostgresHA.replicas; the single-instance database must stay at one replica.- The first
helm upgradeafter an install can restart the bundled database even when no database value changed, making the UI unavailable briefly; later upgrades that only touch the Grafana workload roll without downtime in multi-replica mode. - Rotating a prerequisite secret does not restart anything. After changing the value of a datasource-credential or SMTP secret, run
cpln workload force-redeployment RELEASE_NAME-grafana --gvc GVC_NAMEso the replicas pick it up. Never rotate the encryption key, and change the admin password in the UI rather than in its secret. - Confirm an upgrade is live from the deployment, not from the Helm output —
cpln workload get-deployments RELEASE_NAME-grafana --gvc GVC_NAME -o yaml— especially after adding a new secret reference (enabling SMTP with a password, addingdatasources.credentialSecrets, turning on backups). - Uninstall deletes every volume set the chart created — the database and, in HA mode, the consensus store — so the data does not survive a reinstall. The GVC is yours and is left alone, and the prerequisite secrets you created survive. Enable backups if the data matters.
Troubleshooting
The Grafana workload sits at 0 replicas after install while helm reported success
The Grafana workload sits at 0 replicas after install while helm reported success
cpln helm install succeeded, but RELEASE_NAME-grafana shows no containers and cpln logs returns nothing.Cause: A prerequisite secret named in your values does not exist — the app-database credentials, the admin password, the encryption key, a datasource-credential secret or the SMTP password. 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.Logins return 401 and /api/health returns 503 right after an upgrade
Logins return 401 and /api/health returns 503 right after an upgrade
helm upgrade — typically the one that sets admin.applyPassword: false — every login fails with 401 and /api/health returns 503.Cause: The first upgrade after an install can restart the bundled app database even when no database value changed, and Grafana cannot authenticate anyone while its database is unreachable. Nothing was reset — the default admin credentials fail in the same way.Fix: Wait for /api/health to return 200 and log in again. Do not roll back and do not recreate the password secret.Render fails with replicas >= 2 requires redis.enabled=true
Render fails with replicas >= 2 requires redis.enabled=true
helm install or helm upgrade stops before applying anything with grafana: replicas >= 2 requires redis.enabled=true — Redis Sentinel coordinates alerting HA; without it every replica evaluates alerts independently and sends duplicate notifications.Cause: replicas is 2 or more and redis.enabled is false.Fix: Set redis.enabled: true alongside replicas — see Alerting High Availability — or return replicas to 1.Render fails with enable exactly one database
Render fails with enable exactly one database
grafana: enable exactly one database — set either postgresHA.enabled or postgres.enabled to true, not both, or the variant naming postgresHA.enabled (production) or postgres.enabled (dev/lightweight).Cause: Both postgresHA.enabled and postgres.enabled are true, or both are false.Fix: Enable exactly one — see App Database. To use the single-instance database, set postgresHA.enabled: false together with postgres.enabled: true.Saved datasources stop working after the encryption key secret changed
Saved datasources stop working after the encryption key secret changed
admin.secretKeySecretName was changed, or the secret was deleted and recreated with a different value. GF_SECURITY_SECRET_KEY is read on every boot to decrypt stored datasource credentials; a different key cannot decrypt them.Fix: Restore the original key from your backup of it and run cpln workload force-redeployment RELEASE_NAME-grafana --gvc GVC_NAME. If the original key is gone, re-enter the credentials of every saved datasource.Links in alert emails point at localhost
Links in alert emails point at localhost
localhost.Cause: publicAccess.enabled is false, so the chart does not derive Grafana’s root_url from a canonical endpoint and Grafana falls back to its default.Fix: Set publicAccess.enabled: true and upgrade, or use contact points that do not rely on absolute links.Upgrade fails with a key that was REMOVED in postgres 3.4.0 or 2.5.0
Upgrade fails with a key that was REMOVED in postgres 3.4.0 or 2.5.0
helm upgrade stops at render with config.username was REMOVED in postgres 3.4.0, the postgres block was REMOVED in 2.5.0, backup.minio.accessKey and backup.minio.secretKey were REMOVED, or a message that postgres.credentials was removed.Cause: Your values still carry database or MinIO credential keys that moved in template versions 1.3.0, 1.4.0, 1.4.1 or 1.5.0. The existing release is left untouched and running.Fix: Follow the matching migration note above — Upgrading from 1.2.1 or Earlier, Upgrading from 1.3.0, Upgrading from 1.4.0 or Upgrading from 1.4.1 — keeping the credentials the database already uses.Important Notes
- Create the app-database credentials, admin-password and encryption-key secrets before installing. A missing prerequisite secret does not fail the install — the workload sits at zero replicas waiting on it, and
status.versions[].messagefromcpln workload get-deployments RELEASE_NAME-grafana --gvc GVC_NAME -o yamlnames it. - Rotating the database password needs
ALTER ROLEplus a forced redeployment of Grafana (and a leader switchover first in HA mode) — see Rotating the Database Password. - The encryption-key secret is permanent: never delete it, never rotate it. Back the key up outside Control Plane.
- The admin password applies on first boot only. Change it in the Grafana UI; then set
admin.applyPassword: false, upgrade, and delete the password secret — see First Run. - This template ships no dashboards and no datasources. Control Plane’s console already provides built-in workload-metrics dashboards; this is the pane for data you own.
replicas >= 2requiresredis.enabled: true— the chart refuses to render multi-replica without the Sentinel tier that keeps one firing alert from producing one notification per replica.- Dashboards, users and alert rules live in the app database and survive Grafana restarts and replica replacement. Uninstall deletes the database volume sets — enable backups if the data matters.
- Give each release its own app-database credentials secret — secret names are org-wide.
- Prerequisite secrets you create are not owned by the release — they survive
helm uninstalland must be deleted manually if you no longer need them. - Keep
publicAccess.enabled: trueif you rely on links in alert emails — an internal-only instance has no derivedroot_urland links point at localhost. - This template ships Grafana OSS only — Enterprise features such as fine-grained RBAC, reporting and query caching are not available.