# Helm charts Two example charts for deploying [zddc-server](../zddc/) on Kubernetes. Both compile zddc-server from source via an init container — no container image needs to be pulled from a registry, and no binary needs to be built ahead of time. The init container clones the repo at a configured git ref and runs `go build`; the main container is plain alpine + the freshly built static binary. ## Charts | Chart | When to use | |---|---| | **`zddc-server-prod/`** | Production. Pin `zddc.gitRef` to a stable tag (`zddc-server-vX.Y.Z`). Slower probe cadence; image-pull policy `IfNotPresent`. Mounts the data PVC directly RW at `ZDDC_ROOT`. | | **`zddc-server-dev/`** | Development / soak. Tracks `main` by default; `helm upgrade` triggers a pod recreate so each rollout pulls the latest commit. Faster probes; debug-level logging (request headers logged — sensitive). Wraps the data PVC in **OverlayFS** (lower = PVC mounted RO, upper = ephemeral `emptyDir`) so dev-side writes never mutate the underlying store. Use this shape when the dev replica points at the same data as prod. | The chart values are nearly identical between the two; the differences are encoded as defaults in each chart's `values.yaml.example`. The dev chart's overlay-isolation layer is a structural difference, not a values-level toggle — see `zddc-server-dev/templates/deployment.yaml` for the privileged init container and the `data-readonly` / `overlay-scratch` / `data` volume sandwich. ## Quick start ```sh # Pre-requisite: a PersistentVolumeClaim for ZDDC_ROOT data kubectl apply -f - <<'EOF' apiVersion: v1 kind: PersistentVolumeClaim metadata: name: zddc-root spec: accessModes: [ReadWriteMany] # or RWO if single replica is fine resources: { requests: { storage: 100Gi } } storageClassName: your-shared-fs # NFS, CephFS, SMB, etc. EOF # Production install cp helm/zddc-server-prod/values.yaml.example my-prod-values.yaml $EDITOR my-prod-values.yaml # set zddc.gitRef, hostnames, etc. helm install zddc-server-prod helm/zddc-server-prod/ -f my-prod-values.yaml # Dev install (tracks main HEAD) cp helm/zddc-server-dev/values.yaml.example my-dev-values.yaml $EDITOR my-dev-values.yaml helm install zddc-server-dev helm/zddc-server-dev/ -f my-dev-values.yaml # Trigger a rebuild from latest main HEAD (dev chart) helm upgrade zddc-server-dev helm/zddc-server-dev/ -f my-dev-values.yaml ``` ## What the chart does and doesn't do **Does:** - Clones the configured `zddc.gitRepo` at `zddc.gitRef` in an init container, builds the Go binary, copies it to a shared `emptyDir`, and starts the main container against that binary. - Wires the `ZDDC_*` environment-variable contract (root path, addr, email header, CORS allowlist, log level, index path). - Mounts a caller-supplied PersistentVolumeClaim at `ZDDC_ROOT` (prod chart) or as the OverlayFS lowerdir behind a merged `ZDDC_ROOT` (dev chart). - Optionally creates an Ingress (`ingress.enabled: true`). **Does not:** - Create the PVC. Operators provision storage themselves; the chart only references it by name. - Manage TLS for the pod. zddc-server runs in plain HTTP mode behind whatever ingress / authenticating reverse proxy the cluster already has. `ZDDC_TLS_CERT=none` and `ZDDC_INSECURE_DIRECT=1` are hardcoded in the templates because the chart is opinionated about the TLS-terminated-upstream deployment shape. - Authenticate users. zddc-server reads the user's email from a header set by the upstream proxy (`X-Auth-Request-Email` by default). The chart does not deploy oauth2-proxy / nginx-auth-request / Pomerium / etc. — bring your own. - Manage secrets. `values.yaml.example` contains no secrets and never should. ACL email lists belong in `.zddc` files inside the data volume; image-pull credentials and TLS certs (if you enable ingress TLS) reference Kubernetes secrets you've created separately. ## Why build from source instead of using a registry image Three reasons: 1. **Reproducibility.** The init container's logs show exactly which git ref was built. There's no opaque "what did I deploy" question that a registry tag can introduce. 2. **One distribution channel.** Codeberg release-asset binaries already exist for direct downloads; the chart compiles its own binary from the same source git ref so there's nothing extra to maintain (no separate image registry, no image-promotion pipeline). 3. **Smaller blast radius.** A compromised build image affects only pods that pull during the compromise window. A compromised registry image stays compromised across rollbacks until the digest is rotated. The cost: every pod start takes 30-60s to clone + `go build` instead of pulling a pre-baked image. Acceptable for both chart audiences (production rollouts are infrequent; dev rollouts trade build time for tracking-main convenience). ## Linting ```sh helm lint helm/zddc-server-prod/ helm lint helm/zddc-server-dev/ # Render to inspect (uses default values from values.yaml.example): helm template test-prod helm/zddc-server-prod/ \ --values helm/zddc-server-prod/values.yaml.example ```