Skip to content

Make it durable

light is one app replica and a single-instance database. ha is two app replicas that join into one Erlang cluster — so a conversation streaming from either pod survives — over a Postgres the CNPG operator runs replicated at 2/2. This page stands that up on the same laptop cluster as Stand it up locally, so the first time you run the durable shape is not on a cluster that matters.

The replicated database is run by an operator, and operators are the one thing the build does not create:

Terminal window
just operators

That installs cert-manager, CNPG and the barman-cloud plugin into the cluster, cluster-wide. On k3d this is free to undo — just down deletes the whole cluster.

Terminal window
just params="--param tier=ha --param postgres=cnpg --param dataPlane=sprites --param storage=s3" up

One command, four parameters. tier=ha is the one you wanted; the other three exist because the laptop defaults are emulators, and ha refuses to stand on an emulator. Leave one off and the build stops and tells you, one at a time:

  • postgres=bundled is a single instance and cannot back an ha deployment → postgres=cnpg
  • dataPlane=spritzer is an in-memory emulator in a single pod → dataPlane=sprites
  • storage=floci is an emulator whose buckets do not survive a restart, and cannot hold the backups of an ha deployment → storage=s3

The errors are the documentation here: you can start from just params="--param tier=ha" up and let each refusal hand you the next parameter.

First boot takes a little longer than light: the app pods wait for CNPG to bring the primary up before starting. On a brand-new database you may still see RESTARTS 1 on one replica — both replicas race to create the migrations table, and the loser retries and wins. Once, on first boot, on an empty database; not a pattern to watch for after that.

Terminal window
kubectl get pods -n fountain
NAME READY STATUS RESTARTS AGE
fountain-6bb8986694-x4sm7 1/1 Running 1 97s
fountain-6bb8986694-z89wk 1/1 Running 0 97s
fountain-pg-1 1/1 Running 0 84s
fountain-pg-2 1/1 Running 0 64s

Two app replicas, and a two-instance Postgres named fountain-pg. The replicas find each other through a headless Service and form one Erlang cluster — the proof is in the logs:

Terminal window
kubectl logs deploy/fountain -n fountain | grep "connected to"
# [libcluster:fountain] connected to :"fountain_server@10.42.0.14"

Only the replica that initiated the connection logs it, so an empty grep on one pod means try the other, not that the cluster failed to form.

A PodDisruptionBudget (kubectl get pdb -n fountain) keeps at least one replica up through node drains. Reaching it, registering and the first admin are unchanged from the light walkthrough: just forward, then http://localhost:4000.

Two of the four parameters name things this laptop does not have, and the stand-up works anyway because the placeholders are only exercised later:

  • dataPlane=sprites points at the real Sprites API, but the token in the platform Secret is still local-dev-not-a-real-token. The app boots and serves; starting a conversation is what needs the real SPRITES_TOKEN.
  • storage=s3 means backups upload to a real bucket with the AWS credentials in the Secret — which are also placeholders, so the nightly backup job will dump and then fail to upload until you put real ones there. Backups and restore covers both halves.

That is the honest shape of ha on a laptop: the durability is real, the outside services are not yet. Putting real values in the Secret is the same step it would be on a real cluster — Secrets.

Terminal window
just down

Deletes the cluster, operators and all.