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 highly available 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 multiple replicas. This template exists to visualize data you own: the catalog’s Prometheus, Thanos, and Grafana 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.

Architecture

  • Grafana — A stateless standard workload serving the UI and HTTP API on port 3000. The replicas knob controls how many instances run; all replicas share the same PostgreSQL app database.
  • PostgreSQL (HA, default) — The postgres-highly-available template as a subchart: 3× Patroni PostgreSQL, 3× etcd, and a HAProxy leader-routing endpoint Grafana connects through.
  • PostgreSQL (dev/lightweight, optional) — The single-instance postgres template instead, for lighter non-HA deployments.
  • Redis Sentinel (optional) — The redis template as a subchart in Sentinel mode. Grafana’s alerting HA uses it to coordinate alert evaluation across replicas, so exactly one notification is sent per alert. Required when replicas is 2 or more.
  • Hardened defaults — User signup, anonymous access, and upstream telemetry are all disabled. When public access is on, Grafana’s root_url is derived automatically from the canonical endpoint.

What Gets Created

  • Standard Grafana Workloadreplicas stateless instances serving the UI and API on port 3000.
  • Database Workloads — HA mode: a stateful Patroni PostgreSQL workload, a stateful etcd workload, and a standard HAProxy leader-routing workload. Single mode: one stateful PostgreSQL workload.
  • Redis Workloads (optional) — A Redis workload and a Sentinel workload (3 replicas each by default) when redis.enabled is true.
  • Volume Sets — The database subchart’s persistent volumes (10 GiB per replica by default), plus the etcd volumes in HA mode. Grafana itself has none.
  • Secrets — The database credentials created by the database subchart, and — only when datasources.definitions is set — an opaque secret holding the rendered datasource provisioning file. The admin password and the datasource-encryption key are prerequisite secrets you create yourself; the chart references them by name and never creates, modifies, or deletes them.
  • Identity & Policy — A least-privilege policy granting the Grafana identity reveal on exactly the secrets it uses, including the prerequisite admin-password, encryption-key, datasource-credential, and SMTP secrets you name.
  • Cron Backup Workload (optional) — When database backups are enabled.
This template does not create a GVC. You must deploy it into an existing GVC.

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 — a value in values.yaml would sit in the Helm release for the life of the install, so neither credential is a value. You create both secrets with encoding plain and name them in admin.passwordSecretName and admin.secretKeySecretName.
1

Create the admin password secret

Choose your own strong password for the admin login and store it as the secret’s payload:
Set admin.passwordSecretName to the name you used.
2

Create the encryption key secret

Generate a random key — Grafana encrypts every stored datasource credential with it — and back the key up somewhere safe outside Control Plane:
Set admin.secretKeySecretName to the name you used.
A missing prerequisite secret wedges the install rather than failing it. cpln helm install still exits 0 and reports success, but the Grafana workload never starts: it sits at zero replicas with the message The secret <name> no longer exists. Workload updates are paused until the secret is added or the reference to the secret removed. Create both secrets first, and after installing confirm with cpln workload get-deployments {release}-grafana --gvc {gvc} rather than trusting the Helm output.
The two secrets have different lifecycles — see Admin Credentials before you delete or rotate either one. Optional features each need a secret or a bucket created before you install: Change the app database password (postgresHA.postgres.password or postgres.config.password) before installing as well — it ships with a change-me placeholder default. Once both secrets exist, install the template using your preferred method:

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

Choosing a Database Mode

Exactly one of the two database modes must be enabled — the chart enforces this at render and fails the install with a clear message otherwise. In HA mode the full stack converges in roughly 6 minutes on a fresh install: etcd comes up first, then the PostgreSQL cluster and its HAProxy endpoint, then Grafana runs its schema migrations. Single mode is faster.

Configuration

The default values.yaml for this template:

Grafana Instance

  • image — The Grafana OSS container image.
  • replicas — Number of Grafana instances. 1 is a single instance; 2 or more forms an HA tier over the shared app database and requires redis.enabled: true — see High Availability.
  • resources — CPU and memory for the Grafana container: minCpu/minMemory are the reservation, maxCpu/maxMemory the limit.
  • admin.user — The admin login name. Not sensitive, so it stays a plain value. Applied when the account is first created.
  • admin.applyPassword / admin.passwordSecretName / admin.secretKeySecretName — The two prerequisite secrets and the toggle that retires one of them. See Admin Credentials.
Signup (GF_USERS_ALLOW_SIGN_UP), anonymous access, and upstream analytics/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 ever passes through Helm values. They sit next to each other in values.yaml but their lifecycles are opposites, and deleting the wrong one is destructive:
The encryption-key secret is permanent. Deleting it wedges the workload, 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.

Retiring the Admin Password Secret

After the account exists, the password secret is inert — Grafana ignores GF_SECURITY_ADMIN_PASSWORD on later boots, and the password can only be changed in the UI (or with grafana-cli admin reset-admin-password). So you can stop referencing it entirely:
1

Log in once

Sign in as admin.user with the password from the secret and confirm you can reach the UI.
2

Set applyPassword to false and upgrade

With admin.applyPassword: false, the chart drops the password secret from both places it appeared — 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 existing admin login keeps working; the account lives in the app database.
After that upgrade, expect logins to return 401 and /api/health to return 503 for a minute or two while the bundled app database restarts alongside the change. This is the database being briefly unreachable, 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.

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 to false for an internal-only instance — external requests are then refused at the edge while in-GVC callers still reach it per internalAccess.
  • internalAccess.type — Internal firewall scope of the Grafana workload:
Firewall changes applied by an upgrade take up to about 30 seconds to propagate.

App Database

Enable exactly one of postgresHA (production, default) or postgres (dev/lightweight) — see Choosing a Database Mode. In both modes, change the database password before installing (postgresHA.postgres.password / postgres.config.password). Grafana is wired to the active database automatically: the HAProxy leader endpoint in HA mode, or the single instance directly in dev mode. In HA mode postgresHA.proxy.enabled must stay true — that HAProxy endpoint is Grafana’s stable database address, and disabling it is rejected at render.
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 goes read-only once it reaches its 2 GiB quota — after roughly 110 days — taking PostgreSQL failover with it. Only installs running the HA database are affected (postgresHA.enabled, the default here); see etcd History Compaction for the mechanism and the symptoms. Upgrade to 1.2.1 or later to turn 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.

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 — the Sentinel workload deployed by the redis subchart, not gossip-based peer clustering. Each replica registers itself 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 auth is rejected at render in this version.
  • Multi-replica behavior verified in testing: a full rolling restart and a killed replica both served every request without a single non-200 response, and a killed replica was replaced in about 98 seconds.

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 password stays in the opaque secret — it never passes through Helm values — and the chart grants the Grafana identity reveal on exactly that secret.

Provisioning Datasources

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. To change one, change the value and upgrade the release.
For catalog services running in the same GVC, use their internal endpoints:

Datasource Credentials

Credentials for provisioned datasources never go into Helm values. Put them in a dictionary secret you create before installing, list the secret under datasources.credentialSecrets, and reference each key as $KEY in the provisioning entry. Every listed key is exposed to the container as an environment variable, Grafana interpolates it when it loads the provisioning file, and the chart grants the workload reveal on exactly that secret.
1

Create the dictionary secret

Create a dictionary secret — for example my-grafana-ds-credentials with the key PG_PASSWORDbefore installing.
2

List it under credentialSecrets

Add the secret name and the keys you want exposed to datasources.credentialSecrets.
3

Reference the key in the definition

Use $PG_PASSWORD wherever the credential belongs in the provisioning entry.
The provisioning file itself only ever contains the literal $PG_PASSWORD placeholder — the value stays in the secret.

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, cost-relevant series such as egress, cross_zone_traffic, and volume_set_*, and cron and stability counters. Adding it as a datasource complements the built-in workload dashboards rather than replacing them — reach for it when you need custom application metrics, cost views, alerting you own and route yourself, or a single pane that mixes platform metrics with your other datasources. 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.
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.
A workload’s built-in CPLN_TOKEN does not authenticate against the metrics endpoint. Use a service-account key with the readMetrics permission.

Connecting

Health and readiness are served at /api/health, which reports the Grafana version and the app-database status:
The Grafana HTTP API is available on the same endpoint for scripted dashboard, datasource, and alert-rule management.

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 instance. Enable them with postgresHA.backup.enabled or postgres.backup.enabled (matching your database mode), and complete the storage setup for your provider before installing. The values below are shown under backup.* — set them within the enabled database block.
1

Create a bucket

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

Set up a Cloud Account

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

Create a bucket-scoped IAM policy

Create an AWS IAM policy with the JSON below (replace YOUR_BUCKET), then set backup.aws.policyName to the policy’s name:
In HA mode, backup.mode selects logical (scheduled pg_dump via a cron workload) or wal-g (continuous WAL archiving). The single-instance mode takes scheduled logical dumps.

Important Notes

  • Create the admin-password and encryption-key secrets before installing, and change the database password. A missing prerequisite secret does not fail the install — helm install reports success and the workload then sits at zero replicas waiting on the secret.
  • The encryption-key secret is permanent: never delete it, never rotate it. It is read on every boot to decrypt stored datasource credentials; changing its payload makes every saved datasource credential undecryptable. Back the key up outside Control Plane.
  • After your first login you can set admin.applyPassword: false and delete the password secret — see Retiring the Admin Password Secret. Expect 401 logins and a 503 from /api/health for a minute or two after that upgrade while the app database restarts; this is not the toggle breaking authentication.
  • This template ships no dashboards and no datasources. It is the pane for data you own; Control Plane’s console already provides built-in workload-metrics dashboards, so there is no reason to recreate those here.
  • Scaling requires Redis — set replicas >= 2 together with redis.enabled: true. The chart refuses to render multi-replica without it, because Sentinel-coordinated alerting is what keeps one firing alert from producing one notification per replica.
  • Dashboards, users, and alert rules live in the app database and survive Grafana restarts, redeployments, and replica replacement. Uninstall deletes the database volume sets — enable backups if the data matters.
  • Prerequisite secrets you create are not owned by the release — the admin-password, encryption-key, datasource-credential, and SMTP secrets all survive helm uninstall and must be deleted manually if you no longer need them.
  • 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.
  • Grafana Live push updates are per-instance in multi-replica mode; dashboard auto-refresh and alerting are unaffected.
  • 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 HTTP API

Manage dashboards, datasources, and alert rules programmatically

Grafana Template

View the source files, default values, and chart definition