Skip to main content

Overview

PocketBase is an open-source backend (MIT licensed) that ships as a single executable: an embedded SQLite database, an auto-generated REST API over your collections, user authentication, file uploads, realtime subscriptions, and a web admin dashboard. This template deploys one stateful workload with its data directory on a persistent volume, served over HTTPS on the canonical *.cpln.app endpoint on port 8090. It suits a mobile or single-page app backend, a prototype that needs a real API quickly, or a small internal tool — anywhere a full PostgreSQL plus API-server plus auth-service stack would be more than the team needs.

Architecture

  • PocketBase — A single-replica stateful workload running one container that serves the REST API, the realtime stream, and the /_/ dashboard on port 8090. The superuser account is created from your credentials secret before the server ever binds the port, so there is no unclaimed-admin window.
  • SQLite on a persistent volume — The database, uploaded files, and any locally-stored backup archives all live at /pb_data on the workload’s volume set. There is no external database dependency; PocketBase talks to nothing but its own disk.

What Gets Created

  • Stateful PocketBase Workload — One replica ({release}-pocketbase) serving the API, realtime, and dashboard on port 8090.
  • Volume Set — A 10 GiB persistent volume ({release}-pocketbase-data) mounted at /pb_data, holding the SQLite database, uploaded files, and local backup archives. Scheduled snapshots protect the data, and a final snapshot is taken on uninstall.
  • Identity & Policy — An identity bound to the workload, with a policy granting reveal on exactly one secret: the credentials secret you create. Nothing else.
This template does not create a GVC. You must deploy it into an existing GVC.

Prerequisites

PocketBase needs one required prerequisite dictionary secret that must exist before you install. It holds the superuser login and the key that encrypts settings at rest; none of these values pass through Helm values.
1

Create the credentials secret

The secret must have exactly these three keys:
openssl rand -hex 16 produces exactly the 32 characters PocketBase requires.
2

Point the template at it

Set credentials.secretName to the secret’s name (default my-pocketbase-credentials). Secrets are org-level, so no GVC flag is involved — give each release its own secret name if you run more than one.
3

Keep a copy of the encryption key

Store encryptionKey somewhere safe outside Control Plane. It cannot be changed later without orphaning everything it protects — see Important Notes.
Both credential rules are enforced by PocketBase itself, and a rejected value is a visible crash rather than a silent skip. A password under 8 characters is refused with password: Must be at least 8 character(s)., and an encryption key that is not a valid AES length fails with crypto/aes: invalid key size 31. Use openssl rand -hex 16 and the length is correct by construction.
Installing without the secret looks like a platform fault, not a missing step. cpln helm install reports complete success and creates all four resources, then the workload simply never becomes ready. cpln logs returns zero lines — the container never starts, so there is no output to collect — and the only diagnostic is in status.versions[].message:
Read it with cpln workload get-deployments {release}-pocketbase --gvc {gvc} -o yaml. Creating the secret afterwards does release the deployment on its own, but slowly — measured at 10 minutes 11 seconds — so poll rather than time-box it. To skip the wait, force a redeployment of the workload.
Once your credentials secret exists, 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

First Steps After Install

A default install reaches ready in well under a minute. The superuser account already exists — it is created from your secret before the server starts listening — so there is nothing to claim and no open signup window.
1

Sign in to the dashboard

Open https://<canonical>.cpln.app/_/ and sign in with the email and password from your credentials secret.
2

Set the Application URL

Go to Settings → Application → Application URL and set it to your public endpoint. Verification and password-reset emails build their links from this value, so until you set it those links point at the wrong host. It is a database setting, so the template cannot write it for you.
3

Configure SMTP and OAuth2 providers

Both live under Settings, in the database rather than in Helm values, and are encrypted at rest with your encryptionKey.
4

Create your collections and API rules

New collections are superuser-only until you write an API rule that opens them.

Configuration

The default values.yaml for this template:

PocketBase Instance

  • image — The PocketBase container image. See Image for why this is a community build and what an override must satisfy.
  • resources — CPU and memory bounds for the container. The block exposes both a reservation and a limit, so the limit is named maxCpu / maxMemory; the bare cpu and memory keys are rejected at render with a message telling you to rename them. On a stateful workload the platform caps the maxCpu to minCpu ratio at 4:1, which the defaults sit exactly on.
  • volumeset.capacity — Volume size in GiB (minimum 10) for /pb_data. Capacity is fixed when the volume set is created, so size it at install time.

Credentials

  • credentials.secretName — Name of your pre-created dictionary secret holding email, password, and encryptionKey (see Prerequisites). It must exist before you install, and it is the only secret the workload’s identity can reveal.
The superuser password is re-applied from this secret on every start, and rotating it needs one extra step — see Rotating Credentials.

Backup

Scheduled, crash-consistent snapshots of the data volume, managed by the platform — no cloud account or bucket is required. See Backing Up for how snapshots and restores work.
  • backup.enabled — Take periodic snapshots of the data volume (default true). Setting it to false removes the schedule; the final snapshot on uninstall is taken either way.
  • backup.schedule — Cron expression in UTC (default 0 3 * * *, daily at 03:00). The platform does not accept schedules more frequent than hourly.
  • backup.retention — How long each snapshot is kept, e.g. 7d, 720h, 30d.

API

  • cors.allowedOrigins — Browser origins allowed to call the API, passed to PocketBase as its --origins flag. The default ["*"] allows any origin. With a restricted list, a request from an origin that is not on it still returns 200 but comes back without an access-control-allow-origin header — which is correct CORS behavior, since the browser is what blocks the response.
A cors.allowedOrigins change takes effect only once the new replica is serving, measured at 110 seconds after the upgrade — and the workload reports ready: true almost immediately, against the outgoing replica. Verify a CORS change with an actual request rather than by polling readiness.

Access

  • publicAccess.enabled — Serve the REST API, realtime, and the /_/ dashboard on the canonical *.cpln.app HTTPS endpoint (default true). This is safe to leave on: the superuser is created from your secret before the server starts listening, so no unclaimed admin account is ever exposed, and new collections are superuser-only until you write an API rule. Set it to false for an internal-only instance — external requests are then refused at the edge with a 403.
  • internalAccess.type — Controls which workloads can reach PocketBase over the internal network:
Firewall changes are not instantaneous. Flipping publicAccess.enabled took 126 seconds to be enforced in testing. Re-test after a pause rather than concluding the knob did not work.
A private install stays fully operable — PocketBase binds 0.0.0.0, so the dashboard is reachable through a tunnel even with public access off and internalAccess.type: none. Forward the port and open http://localhost:8090/_/:

Image

No official PocketBase image exists. This template pins a well-used community build and overrides its entrypoint, running pocketbase superuser upsert to completion and then exec pocketbase serve directly. The chart therefore depends only on the binary at the pinned tag, not on an entrypoint script that is versioned by the image repository rather than by PocketBase.
The PB_ADMIN_EMAIL and PB_ADMIN_PASSWORD environment variables you will find in community documentation do nothing here. They are a feature of that image’s entrypoint.sh, which this chart replaces. PocketBase core reads no environment variables at all — anything you want configured is either a CLI flag or a dashboard setting. If you override image, it must run as root with pocketbase on PATH.

Connecting

Realtime Subscriptions

PocketBase delivers realtime updates over Server-Sent Events — one long-lived HTTP response, not a websocket. A client holds GET /api/realtime open and posts its subscriptions to POST /api/realtime.
Every realtime connection is closed after ten minutes. The platform cuts any HTTP request at the workload’s timeoutSeconds, which this template already sets to 600 — the maximum the platform allows. Server-Sent Events are not exempt the way an upgraded websocket connection is, so ten minutes is the longest a subscription can live here. This is a hard ceiling, not a tunable.A stream that goes quiet ends sooner: PocketBase itself disconnects an idle subscription after about five minutes, by its own design. So ten minutes is the ceiling for an active stream, not a floor.
What was measured over a full ten-minute stream, kept continuously active:
  • No events are lost while the stream is open — 10 of 10 record events were delivered, each carrying the mutation that triggered it.
  • The cut is a clean end-of-stream, not an error or a reset. The connection simply ends.
  • The cause is the platform timeout, proven causally. Lowering timeoutSeconds to 60 cut an otherwise identical stream at exactly 60 seconds.
The practical consequence depends on your client: If someone reports that realtime “keeps dropping” or “reconnects every ten minutes”, this is the reason and it is expected behavior.

Rotating Credentials

The superuser password is re-applied from the credentials secret on every start — the container runs pocketbase superuser upsert before the server binds. The secret is therefore the source of truth for the login, and changing the password in the dashboard is reverted at the next restart.
Updating the secret does not take effect on its own — you must force a redeployment. Measured: eleven minutes after rotating the password key, the container still held the old environment, the old password still authenticated, and the workload reported ready: true the entire time. There is no error and no warning, so a rotation looks like it succeeded while the old credential stays valid indefinitely.
1

Update the secret

Change the password entry of your credentials secret. Leave encryptionKey alone — see Important Notes.
2

Force a redeployment

3

Wait for the new replica to serve

The rotation applies once the new replica is serving, which costs the usual restart gap of about 85 seconds (see Restarts and Availability). Until then the old password continues to work. Afterwards the old password is rejected with 400 and the new one authenticates.

Restarts and Availability

PocketBase runs as exactly one replica, and there is deliberately no replicas knob. This is upstream’s design, not a limitation of this template — PocketBase scales vertically only (“Horizontal scaling? Only on a single server”), because the database is embedded SQLite with no clustering. On this platform each stateful replica would additionally get its own volume, so a second replica would serve a different, empty database behind the same endpoint. The practical consequence is that any restart is a full outage of about 85 seconds, during which requests fail with 503. The gap was measured three ways and landed in a tight band: Volume detach and reattach dominates that time, which is why an unplanned replica loss costs the same as a planned restart.
Data is never at risk during a restart. The same volume reattaches to the new replica — a marker record written before a replica was stopped was readable immediately after it came back, and encrypted settings still decrypted correctly. The difference between a single instance and a highly available one here is minutes of downtime, not data loss.

Backing Up

Backups are scheduled volume snapshots managed by the platform — no cloud account or bucket is required. On the configured cron schedule the platform takes a crash-consistent snapshot of the data volume; SQLite recovers cleanly from one via its write-ahead log. Snapshots are pruned automatically after backup.retention, and a final snapshot is always taken when the release is uninstalled.
  • Default schedule — Daily at 03:00 UTC (backup.schedule: "0 3 * * *"), kept for 7 days (backup.retention: 7d).
  • Minimum interval — The platform does not accept schedules more frequent than hourly.
  • Coverage — Everything under /pb_data: the SQLite database, uploaded files, and any backup archives PocketBase itself wrote locally.
List the snapshots for the release’s volume set:
Snapshots live in the platform storage layer alongside the volume, not off-site. They protect against data corruption and accidental changes, but losing the whole GVC would lose them too. For an off-platform copy, configure PocketBase’s own S3 backups under Settings → Backups in the dashboard — that is a database setting this template deliberately does not configure, so it is yours to set up.

Restoring a Backup

Restore is in-place on the release’s own volume set: the platform provisions a fresh volume from the chosen snapshot and swaps it in, then the workload restarts to remount it.
Restoring reverts the volume to the exact snapshot state — any records, uploads, or settings changed after that snapshot are lost — and restarts the single-replica workload, so PocketBase is unavailable during the swap.
1

Find the snapshot to restore

List snapshots and note the name, location, and volumeIndex of the one you want:
2

Restore it in place

Provision a fresh volume from the snapshot and swap it in. The workload restarts to remount it:
3

Verify

Once the workload is ready again, sign in to /_/ and confirm your collections, records, and uploaded files are present.

Important Notes

  • Create the credentials secret before installing. credentials.secretName must name an existing dictionary secret holding email, password, and encryptionKey. Without it the deployment wedges silently: cpln logs returns zero lines and the only diagnostic is status.versions[].message from cpln workload get-deployments. Recovery after creating the secret took 10 minutes 11 seconds unaided, or force a redeployment to skip the wait.
  • Never change encryptionKey after install. It encrypts the SMTP password, OAuth2 client secrets, and S3 backup credentials stored inside the database. Changing it orphans all of them with no way back. Keep a copy outside Control Plane.
  • The superuser password is re-applied from the secret on every start. Changing it in the dashboard is reverted at the next restart — change it in the secret instead, and note that doing so changes your login.
  • Rotating the secret needs a forced redeployment. The workload does not pick up a changed secret on its own: eleven minutes after a rotation the old password still worked and the workload reported ready: true throughout. See Rotating Credentials.
  • Realtime subscriptions are cut at ten minutes. timeoutSeconds is already at the platform maximum of 600, so this is a hard ceiling. No events are lost and the cut is a clean end-of-stream; the official SDKs reconnect automatically, but a hand-rolled client must handle it. See Realtime Subscriptions.
  • Single instance, no HA — and that is upstream’s design. PocketBase does not cluster, so there is no replicas knob and every restart is an outage of about 85 seconds. Data is not at risk; the same volume reattaches. See Restarts and Availability.
  • Set the Application URL at first login. Verification and password-reset emails build their links from Settings → Application → Application URL, which is a database setting the chart cannot write. Until you set it, those links point at the wrong host.
  • Uploads and local backup archives share the volume with the database. A file-heavy app needs more than the 10 GiB default; raise volumeset.capacity at install time, since capacity is fixed when the volume set is created.
  • Backups are platform volume snapshots, not off-site copies. They live in the platform storage layer next to the volume. For an off-platform copy, configure PocketBase’s own S3 backups in the dashboard.
  • Access-knob changes take time to propagate. Flipping publicAccess.enabled took 126 seconds to be enforced. Re-poll rather than trusting the first response.
  • This is not an official image. No official PocketBase image exists, so the template pins a community build and overrides its entrypoint. The PB_ADMIN_EMAIL and PB_ADMIN_PASSWORD variables from that image’s documentation do nothing here — PocketBase core reads no environment variables. An image override must run as root with pocketbase on PATH.
  • Data survives restarts and upgrades; uninstalling deletes it. The volume set goes with the release, though a final snapshot is taken on uninstall. Your credentials secret is yours and survives an uninstall.

External References

PocketBase Documentation

Official documentation for collections, rules, and the API

REST API Reference

Reading and writing records over the auto-generated API

Realtime API

Server-Sent Events subscriptions and the reconnect model

Client SDKs

Official JavaScript and Dart SDKs, which reconnect automatically

Going to Production

Upstream guidance on running PocketBase for real

PocketBase Template

View the source files, default values, and chart definition