Skip to main content

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.
This template ships zero dashboards and zero datasources. A fresh install has an empty dashboard list and an empty datasource list — you connect your own datasources (in the UI or as code) and build or import your own dashboards. Control Plane’s console already provides built-in workload-metrics dashboards for every workload; this template is for the data those dashboards do not cover.
This template deploys into a GVC you already have — it does not create, provision or manage one. Every workload below runs in each location of that GVC. For a Grafana spread across several locations behind one georouted endpoint, use the Grafana Multi-Location template instead.

What Gets Created

The admin password and the encryption key are prerequisite secrets you create yourself; the chart references them by name and never creates, modifies or deletes them. The app-database credentials secret is the opposite: the chart creates it from postgres.credentials.*, so there is nothing for you to create for the database.

Prerequisites

Two opaque secrets must exist before you install. Grafana’s admin account is a human-facing login, and publicAccess.enabled defaults to true, so that account is reachable from the internet — neither credential is a Helm value, and neither ever lands in the release. Secrets are org-level, so no --gvc flag is involved.
1

Create the admin password secret

Choose your own strong password for the admin login and store it as the secret’s payload with encoding plain. Grafana reads it only when the admin account is first created:
Set admin.passwordSecretName to this name.
2

Create the encryption key secret

Generate a random key — Grafana encrypts every datasource credential it stores with it — and back the key up somewhere safe outside Control Plane. It is read on every boot for the life of the install and can never be rotated:
Set admin.secretKeySecretName to this name.
3

Choose the app-database password

The bundled database’s credentials are internal plumbing: this chart writes postgres.credentials.username, postgres.credentials.password and postgres.credentials.database into a dictionary secret it creates, and the database reads that secret. Nothing to create — but the password ships as the placeholder change-me-grafana-db-password, so set postgres.credentials.password to a value of your own before installing. This applies in both database modes.
A missing prerequisite secret wedges the install rather than failing it. cpln helm install reports success while RELEASE_NAME-grafana sits at zero replicas with no containers and therefore no logs. The only place the missing secret is named is status.versions[].message:
The message reads The secret <name> no longer exists. Workload updates are paused until the secret is added or the reference to the secret removed. Create the missing secret and the deployment recovers on its own after several minutes, or run cpln workload force-redeployment RELEASE_NAME-grafana --gvc GVC_NAME to skip the wait.
The two secrets have different lifecycles — read Admin Credentials before you delete or rotate either one. Optional features each need something created before you install:

Installation

Once both secrets exist, install the template into your GVC, naming the secrets and replacing the database password placeholder:
Prefer a values file (--values) over --set for the database password if you do not want it in your shell history. If you run more than one release of this template in the same org, also give each its own postgresHA.config.credentialsSecretName (or postgres.config.credentialsSecretName in single-instance mode) — see App Database.

UI

Browse, install, and manage templates visually

CLI

Manage templates from your terminal

Terraform

Declare templates in your Terraform configurations

Pulumi

Declare templates in your Pulumi programs

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

Both credentials live in opaque secrets you create before installing (see Prerequisites); neither value ever passes through Helm. They sit next to each other but their lifecycles are opposites, and deleting the wrong one is destructive: With 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.
The encryption-key secret is permanent. Deleting it wedges the workload on a missing secret reference, and changing its payload makes every datasource credential already saved in the app database undecryptable. Back the key up outside Control Plane instead of rotating it.

Datasources as Code

You can add datasources in the UI at any time. 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.
The provisioning file itself only ever contains the literal $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 as egress, 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:
1

Create a service account with readMetrics

Create a service account granted the 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.
2

Store the key in a dictionary secret

Put the key in a dictionary secret — for example under the key CPLN_METRICS_TOKEN — and list the secret under datasources.credentialSecrets.
3

Add the datasource definition

Point a prometheus-type datasource at https://metrics.cpln.io/metrics/org/YOUR_ORG and send the token as an Authorization header:

SMTP

Grafana sends alert notification emails through the SMTP server you configure; leave 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.app HTTPS endpoint (default). Everything behind the endpoint is gated by Grafana’s own login, and Grafana’s root_url is set to that endpoint automatically. Set it to false for an internal-only instance — external requests are then refused while in-GVC callers still reach it per internalAccess, and you reach the UI yourself with cpln port-forward (see Connecting).
  • internalAccess.type — Internal firewall scope of the Grafana workload:
Allow several minutes after changing an access knob before concluding it did not apply.
With publicAccess.enabled: false, Grafana’s root_url is not derived from a canonical endpoint, so absolute links in alert emails from an internal-only instance point at localhost. Keep public access on if you rely on links in email notifications.

App Database

Exactly one of the two database modes must be enabled — the chart enforces this at render and refuses to install otherwise.
In both modes the database credentials come from postgres.credentials.* — the chart writes them into the dictionary secret named by postgresHA.config.credentialsSecretName or postgres.config.credentialsSecretName (whichever database is enabled), and the database reads that secret. Change postgres.credentials.password before installing even when postgres.enabled is false. 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. Secret names are org-wide, so a second release of this template left on the default credentialsSecretName is refused at install with cannot be updated because it is being managed by a different release and creates nothing — the first release is unaffected. Give each release its own name. 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 raising replicas. 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 >= 2 without redis.enabled: true.
  • Redis and Sentinel ship without authentication — the same-GVC internal firewall is the boundary. Enabling Redis or Sentinel authentication under redis.redis.auth or redis.sentinel.auth is 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

Verify the instance from inside its own container — the Grafana image ships curl:
With publicAccess.enabled: false there is no canonical endpoint; open a tunnel to the UI instead and browse to http://localhost:3000:
The Grafana HTTP API is available on the same addresses for scripted dashboard, datasource and alert-rule management.

First Run

Sign in on the public endpoint as admin.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:
1

Log in once

Sign in as 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.
2

Set applyPassword to false and upgrade

With 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.
3

Delete the secret

Nothing references it any more, so the secret can be deleted. Your admin login keeps working; the account lives in the app database.
After that upgrade, logins may return 401 and /api/health may return 503 for a short while. The first helm upgrade after an install can restart the bundled app database even when no database value changed, and Grafana is unreachable while it comes back. This is not the toggle breaking authentication or resetting your password — do not roll back and do not recreate the password secret. Wait for /api/health to report 200 and log in again.

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.
In HA mode 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.*.
1

Create a bucket

Create an S3 bucket. Set postgresHA.backup.aws.bucket and postgresHA.backup.aws.region to match.
2

Set up a Cloud Account

If you do not have one, create a Cloud Account for the AWS account holding the bucket. Set postgresHA.backup.aws.cloudAccountName to its name.
3

Create a bucket-scoped IAM policy

Create an IAM policy with the JSON below (replace 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 the postgres-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 the chart-created secret:
Neither restore path has been rehearsed through this template. The logical path (a dump replayed with psql) and the WAL-G path are documented and verified on the database templates’ own pages; the WAL-G path there ends in a manual volume-set swap that this chart does not expose. Run a logical restore from a client that has psql, your cloud CLI and network access to the database address — a workload in the same GVC, for example — not from the Grafana container, which has neither tool. Rehearse the procedure before you rely on it.
Restore into a release that uses the same encryption key: admin.secretKeySecretName decrypts the datasource credentials stored in the dump, and a different key makes every saved datasource credential unreadable.

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.
1

Create the encryption key secret with the key the install already uses

The value must be identical to your current admin.secretKey. Any other value makes every datasource credential already saved in the app database undecryptable:
Set admin.secretKeySecretName to this name.
2

Decide what to do with the admin password

The admin account already exists, and Grafana only applies the password when the account is first created, so the password secret’s content changes nothing for an existing install. Either set 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.
3

Remove the old keys and upgrade

Delete 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 moved the single-instance database credentials from postgres.config.username, postgres.config.password and postgres.config.database to postgres.credentials.username, postgres.credentials.password and postgres.credentials.database, named by the new postgres.config.credentialsSecretName. 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:
Move the three keys to postgres.credentials.* using the values the database was initialised with — PostgreSQL enforces the credentials written when the volume was first created, and different values just leave Grafana unable to authenticate. Ignore the message’s advice to create the secret yourself: this template creates it from postgres.credentials.*. 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 — which this chart creates from postgres.credentials.*, exactly as in single-instance mode. Carrying the old block stops the upgrade at render:
Delete postgresHA.postgres.*, and set postgres.credentials.username, postgres.credentials.password and postgres.credentials.database to the same values the cluster already has (the message’s instruction to create the secret yourself does not apply — this template creates it). Leaving postgres.enabled: false is correct; the postgres.credentials.* values feed both database modes. If you run several releases in one org, give each its own postgresHA.config.credentialsSecretName.

Upgrading from 1.4.0

Version 1.4.1 replaced postgresHA.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:
Create the dictionary secret described under Backing Up with the same access key and secret key, set postgresHA.backup.minio.credentialsSecretName to its name, remove the two old keys, and upgrade.

Scaling and Availability

  • replicas scales the Grafana tier; 2 or more requires redis.enabled: true — see Alerting High Availability. Database replicas are set separately with postgresHA.replicas; the single-instance database must stay at one replica.
  • The first helm upgrade after 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_NAME so 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, adding datasources.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

Symptom: 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 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 message reads 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.
Symptom: Immediately after a 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.
Symptom: 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.
Symptom: The install stops with 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.
Symptom: Installing a second release of this template in the same org fails with cannot be updated because it is being managed by a different release, naming the database credentials secret, and creates nothing.Cause: Secret names are org-wide, and both releases are trying to create the same credentialsSecretName (default my-grafana-db-credentials). The first release owns it.Fix: Give the new release its own postgresHA.config.credentialsSecretName (or postgres.config.credentialsSecretName in single-instance mode) and install again. The first release is unaffected.
Symptom: Datasources that worked before now fail their connection test, and Grafana cannot read the credentials it stored for them.Cause: The payload of the secret named by 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.
Symptom: helm upgrade stops at render with config.username was REMOVED in postgres 3.4.0, the postgres block was REMOVED in 2.5.0, or backup.minio.accessKey and backup.minio.secretKey were REMOVED.Cause: Your values still carry database or MinIO credential keys that moved in template versions 1.3.0, 1.4.0 or 1.4.1. 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 or Upgrading from 1.4.0 — keeping the credentials the database already uses.

Important Notes

  • Create the admin-password and encryption-key secrets before installing, and change postgres.credentials.password from its change-me placeholder. A missing prerequisite secret does not fail the install — the workload sits at zero replicas waiting on it.
  • 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 >= 2 requires redis.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 credentialsSecretName — secret names are org-wide, and a second release on the default name is refused at install.
  • Prerequisite secrets you create are not owned by the release — they survive helm uninstall and must be deleted manually if you no longer need them.
  • Keep publicAccess.enabled: true if you rely on links in alert emails — an internal-only instance has no derived root_url and links point at localhost.
  • This template ships Grafana OSS only — Enterprise features such as fine-grained RBAC, reporting and query caching are not available.

External References

Grafana Documentation

Official Grafana documentation

Provisioning Datasources

Reference for the datasource provisioning entries used by this template

Alerting High Availability

How Grafana coordinates alert evaluation across replicas

Configuration Reference

Every Grafana setting and its environment-variable name

Grafana Template

View the source files, default values, and chart definition