Tutorial 07 — Deploy with Helm
This tutorial is the substrate for every deployment-target tutorial that follows. Tutorials 08–13 (the cloud marketplaces) are thin packaging layers around the chart you install here, and 14 (on-prem) is this same chart with the air-gapped values file. If you only read one deployment tutorial, read this one — the rest are distribution mechanics on top of it.
The chart lives in the repo at deploy/helm/buttrbase-backend-rust/. It is real and committed; the commands below run against it as-is. For the why behind the topology modes, see docs/DEPLOYMENT.md and src/deployment.rs (the DeploymentMode enum is the source of truth).
> Honesty note on the tutorial series. Tutorials 01–06 document patterns that are in production today*. 07 and 14 are real, runnable deploy paths grounded in committed chart artifacts. 08–13 are packaging-and-listing guides for marketplaces ButtrBase is *targeting — the chart is real, the vendor-portal listings are forward-looking. 15 (federation) describes a design that is spec-stage. Each tutorial states its own status at the top.
1. What the chart deploys
One Deployment of the Rust backend, one Service, and one ConfigMap — that is the whole footprint. It deliberately does not package Postgres, Redis/Dragonfly, or object storage: those are dependencies you point at, not things the chart owns. This keeps a single chart honest across SaaS (managed Postgres, shared S3) and air-gapped on-prem (in-cluster Postgres, local-fs storage).
| Object | Template | Notes |
|--------|----------|-------|
| Deployment | templates/deployment.yaml | One container rust-backend, listens on 4300, /health readiness + liveness probes. |
| Service | templates/service.yaml | ClusterIP by default, port 4300. |
| ConfigMap | templates/configmap.yaml | Renders every BUTTRBASE_RUST_* env var from topology/providers/diagnostics/policy values. |
The container image defaults to ghcr.io/buttrbase/buttrbase-backend-rust:latest. Pin a real tag in production — latest is for the impatient.
2. Prerequisites
- A Kubernetes cluster (1.25+) and
kubectlpointed at it. - Helm 3.12+.
- A reachable Postgres database and its connection URL.
- Three secret values: the database URL, the app
secret-key, and the 32-byteencryption-key. The chart reads these from a Secret you create — it never templates secrets into the ConfigMap. Seedocs/secrets-architecture.mdanddocs/encryption-at-rest.mdfor how theencryption-keyis used.
Create the Secret first (the default names match values.yaml's env.*SecretRef):
kubectl create namespace buttrbasekubectl -n buttrbase create secret generic buttrbase-backend-rust \
--from-literal=database-url='postgres://buttrbase:…@db.internal:5432/buttrbase' \
--from-literal=secret-key="$(openssl rand -hex 32)" \
--from-literal=encryption-key="$(openssl rand -hex 32)"
> The encryption-key encrypts tenant secrets at rest. Losing it means losing access to every encrypted value — back it up in your secret manager before you install, not after.
3. Pick a topology
The chart ships four values files, one per DeploymentMode. Pick the one that matches what you are running; do not hand-edit values.yaml for a one-off.
| Values file | topology.mode | When |
|-------------|-----------------|------|
| values.yaml | saas | The shared multi-tenant control plane (ButtrBase's own hosted product). |
| values-single-tenant-managed.yaml | single-tenant-managed | One customer, one isolated install, you operate it. |
| values-single-tenant-byoc.yaml | single-tenant-byoc | One customer, their cloud account, you deploy into it. |
| values-on-prem.yaml | on-prem | Air-gapped / customer datacenter. See 14-self-host-on-prem.md. |
The differences are entirely in the values — storageBackend (s3 vs local-fs), networkMode (public/private/air-gapped), providers.ownership (buttrbase-managed/customer-managed/manual-invoice), and the IP allowlist. Each renders into a BUTTRBASE_RUST_* env var the backend reads at startup.
4. Install
Dry-run first — --dry-run renders the manifests without touching the cluster, so you can read exactly what the values produced:
helm template buttrbase ./deploy/helm/buttrbase-backend-rust \
-f ./deploy/helm/buttrbase-backend-rust/values-single-tenant-managed.yaml
Confirm the rendered ConfigMap carries the topology you expect (BUTTRBASE_RUST_DEPLOYMENT_MODE: "single-tenant-managed", the right BUTTRBASE_RUST_STORAGE_BACKEND, etc.). Then install:
helm upgrade --install buttrbase ./deploy/helm/buttrbase-backend-rust \
-n buttrbase \
-f ./deploy/helm/buttrbase-backend-rust/values-single-tenant-managed.yaml \
--set app.image.tag=0.1.0
upgrade --install is idempotent: the first run installs, every later run is a rolling upgrade. Override anything from the command line with --set (here, pinning the image tag away from latest).
5. Verify
The backend reports readiness on /health, which is exactly what the chart's probes hit. Confirm the pod went Ready, then probe through a port-forward:
kubectl -n buttrbase rollout status deploy/buttrbase --timeout=120s
kubectl -n buttrbase port-forward svc/buttrbase 4300:4300 &
curl -fsS http://localhost:4300/health && echo " ✓ healthy"
If rollout status hangs, the pod is almost always failing readiness because a secret value is wrong or the database is unreachable. Read the logs:
kubectl -n buttrbase logs deploy/buttrbase --tail=50
Two failures dominate first installs: a database-url the pod's network can't reach (probe never passes), and an encryption-key that isn't 32 bytes hex (the backend refuses to start rather than run with a weak key). Both are visible in the first ten log lines.
6. Confirm the deployment mode round-tripped
The whole point of the topology values is that the running backend knows which mode it is in — federation trust, storage routing, and provisioning all branch on it. Confirm the env reached the process:
kubectl -n buttrbase exec deploy/buttrbase -- printenv BUTTRBASE_RUST_DEPLOYMENT_MODE
→ single-tenant-managed
If this prints empty or saas when you installed a single-tenant values file, you installed the wrong -f. Re-run the helm upgrade --install with the correct file; Helm reconciles in place.
Done
You have a running ButtrBase backend on Kubernetes, installed from the committed chart, with its deployment mode verified end-to-end. From here:
08–13) all start from this chart. Begin with whichever cloud your customers buy through.14-self-host-on-prem.md swaps in values-on-prem.yaml and the licensing path.15-set-up-federation.md connects them.