Skip to main content

Overview

Metabase is an open-source business intelligence platform — dashboards, a SQL editor, and scheduled report subscriptions. This template deploys the free open-source edition backed by a highly available PostgreSQL cluster by default. The bundled PostgreSQL is Metabase’s own app database (users, dashboards, saved connections); the databases you analyze are data sources you connect in the app after install — they are never installed or touched by this template. The admin account is created automatically on first boot, and the workload only starts receiving traffic once setup is complete, so there is never a publicly reachable setup page.

Architecture

  • Metabase — A single-replica standard workload serving the UI and API on port 3000. Stateless by design: all application state (questions, dashboards, users, saved connections) lives in the PostgreSQL app database, so Metabase itself has no volume set.
  • PostgreSQL (HA, default) — The postgres-highly-available template as a subchart: 3× Patroni PostgreSQL, 3× etcd, and a HAProxy leader-routing endpoint Metabase connects through.
  • PostgreSQL (dev/lightweight, optional) — The single-instance postgres template instead, for lighter non-HA deployments.

What Gets Created

  • Standard Metabase Workload — A single stateless replica 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.
  • Volume Sets — The database subchart’s persistent volumes (10 GiB per replica by default). Metabase itself has none.
  • Secrets — The start script that creates the admin account on first boot, plus the database credentials from the subchart. The admin login and the encryption key are not created by this template: you create both secrets yourself before installing and reference them by name (see Prerequisites).
  • Identity & Policy — A least-privilege policy granting the Metabase identity reveal on exactly the secrets it uses, including your pre-created encryption-key secret.
  • 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 secrets must exist before you install. Both hold credentials or long-lived key material, so neither passes through Helm values — a value would sit in plaintext in the release for the life of the install, and the admin login is on a public endpoint. The template creates neither of them.
1

Create the admin login secret

A dictionary secret holding exactly the keys email and password. Secrets are org-level, so no GVC flag is involved:
Set admin.secretName to the name you used.
2

Create the encryption-key secret

An opaque secret with encoding plain whose entire payload is a random string of at least 16 characters:
Set encryptionKey.secretName to the name you used, and store a copy of the key somewhere safe outside Control Plane.
3

Read either secret back later

-o yaml is required; without it the command prints the secret’s metadata table rather than its contents:
Neither the email nor the password may contain a double quote (") or a backslash (\). Both values are embedded in the JSON body of the first-boot setup call, and either character breaks it. The container checks this at startup and refuses to run setup instead of sending a malformed request — measured firing 32 seconds after install, with a log line that names the offending secret:
The workload then stays unready and its public endpoint answers 503, so a bad credential never results in a reachable, half-configured instance. Fix the secret’s contents and redeploy.
Losing or changing the encryption key means re-entering every saved database connection — Metabase can no longer decrypt them. Rotation is only possible offline, using Metabase’s rotate-encryption-key command. Back the key up before installing.
A missing prerequisite secret wedges the install rather than failing it. The install still exits 0 and reports success, every resource is created, and the workload then never starts. Because the container never ran, cpln logs returns zero lines, which reads as a broken platform rather than a missing prerequisite.The only diagnostic is status.versions[].message, which names the missing secret:
It is get-deployments — plain cpln workload get has no versions key at all. Creating the missing secret clears the wedge on its own, but slowly: recovery has measured between 5.5 and 10.5 minutes across the catalog, so poll rather than giving up. cpln workload force-redeployment RELEASE_NAME-metabase --gvc GVC_NAME shortcuts it to roughly 90 seconds.
For optional database backups, you also need a bucket and access setup for one of the supported providers — see Backing Up. 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. A fresh HA-mode install takes roughly 10–15 minutes to fully converge; single mode is ready in about 2 minutes. Metabase itself reached ready 261 seconds after a default HA install.
For the first few minutes of an HA-mode install, the Metabase container may log Java stack traces (An attempt by a client to checkout a Connection has timed out) and restart while the Patroni cluster elects a leader. It resolves itself once the database is routable — the traces are not a failed install.

Configuration

The default values.yaml for this template:

Metabase Instance

  • image — The Metabase open-source container image.
  • resources — CPU and memory for the Metabase container. The template caps the JVM at 75% of the container memory limit (JAVA_OPTS: -XX:MaxRAMPercentage=75.0), so raising resources.memory also raises the Java heap. The 2 GiB default is sized for the JVM — lowering it is not recommended.
  • encryptionKey.secretName — Name of your pre-created opaque secret holding the key that encrypts saved data-source credentials. See Prerequisites.
  • admin.secretName — Name of the dictionary secret you created in Prerequisites, holding the email and password of the admin account. The credentials never pass through values; the template references your secret and grants the workload reveal on exactly it.
  • admin.firstName / admin.lastName — The admin’s display name. These are ordinary values (not credentials) and, like the secret’s contents, may not contain double quotes or backslashes — these two are checked at render.
  • The account is created automatically on first boot against the local setup API, so there is no unauthenticated setup page at any point: the workload only becomes ready — and only starts receiving traffic — once setup is complete, and if setup fails it never becomes ready at all (fail-closed). Setup runs exactly once; editing the secret afterwards does not modify the existing account, so change the password in the Metabase UI instead.
  • siteName — The instance name shown in the UI and in emails Metabase sends.

Access

  • publicAccess.enabled — Serve the UI and API on the canonical *.cpln.app HTTPS endpoint. It is deliberately on by default: Metabase is a browser tool people open directly, everything behind the endpoint is gated by its own login, and that login is now a credential you created rather than a published default. Set to false for an internal-only instance (external requests are blocked at the edge; in-GVC callers still reach it per internalAccess). A firewall change takes roughly 30 seconds to a few minutes to propagate, so re-test rather than trusting the first response.
  • internalAccess.type — Internal firewall scope of the Metabase workload:

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 (postgres.credentials.password). Metabase is wired to the active database automatically — the HAProxy leader endpoint in HA mode, or the single instance directly in dev mode. If you run more than one release of this template in the same organization, give each its own postgres.config.credentialsSecretName. Secret names are organization-wide, so a second release left on the default name is refused at install and creates nothing — the first release is unaffected.
Template version 1.0.0 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.0.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.

Upgrading From 1.0.x

Version 1.1.0 moved the admin login out of Helm values. Earlier versions shipped an email and a working password as values, used exactly as written — a published default guarding a login form on the public endpoint, sitting in the Helm release for the life of the install.
An upgrade that still carries either removed key is rejected at render, before anything is applied. Each guard names its replacement, and there is no compatibility fallback — the version bump is the migration path. A real upgrade carrying an old key failed at render, created no Helm revision, and left the running release healthy and untouched:
Leaving admin.secretName empty is refused the same way.
An existing install’s admin password does not change on upgrade. The account already lives in the app database and first-boot setup never runs again, so the secret’s contents only matter to a fresh install. Change the password in the Metabase UI (account settings). If the install is still carrying the published 1.0.x default (change-me-metabase-1), treat that password as compromised and change it now.
To upgrade an existing install:
1

Create the admin login secret

Follow Prerequisites. Your existing encryption-key secret stays exactly as it is.
2

Drop the removed keys from your values

Remove admin.email and admin.password, and set admin.secretName instead. Leave admin.firstName, admin.lastName, and siteName alone.
3

Upgrade

The single Metabase replica restarts; questions, dashboards, and users are in the app database and are untouched.

Connecting

Adding Data Sources

The databases you analyze are added inside Metabase after install (Admin → Databases) — the template never installs or touches them. To analyze a database running on Control Plane, use its internal endpoint as the host, e.g. {workload}.{gvc}.cpln.local:5432. Any database Metabase can reach — inside or outside Control Plane — works as a data source. Saved connection credentials are encrypted at rest with your encryption key.

Backing Up

Database backups are optional and disabled by default. They cover the app database — the questions, dashboards, users, and saved connections that make up your Metabase 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

  • Back up the encryption-key secret — losing or changing it means re-entering every saved database connection; rotation is only possible offline via Metabase’s rotate-encryption-key command.
  • Create both prerequisite secrets before installing — a missing one leaves the workload waiting on something that does not exist, with zero log lines. See Prerequisites for how to diagnose it.
  • Change the database password before installing (postgres.credentials.password) — it is bundled plumbing, used exactly as given, and it remains a value by design.
  • A weak or malformed admin password keeps the workload unready by design — Metabase’s own check requires letters and digits, 8+ characters, and the container refuses to run setup if the email or password contains a double quote or a backslash. A failed bootstrap is fail-closed rather than exposing an open setup page.
  • Metabase is single-replica in this template — the default HA PostgreSQL backend removes the database as a failure point.
  • Upgrades restart the single replica — expect a few minutes of UI downtime per Helm upgrade; the HA app database keeps running and no data is lost.
  • Uninstall deletes the app-database volume sets — all questions, dashboards, and users. Enable backups if the data matters.
  • This template ships the open-source image only — Pro/Enterprise features (SSO, sandboxing, config-file init) are not available.

External References

Metabase Documentation

Official Metabase documentation

Environment Variables

Metabase environment variables reference

Encrypting Database Details

How the encryption key protects saved connection credentials

Metabase in Production

Upstream guidance on running Metabase in production

Metabase Template

View the source files, default values, and chart definition