Claude Transcripts docs GitHub
Work in progressUnder active development — not tested as ready for use. Breaking changes land without notice, stored data may need to be discarded between revisions, and there is no auth or security model.

30. Kubernetes deploy, generated from the app model

Date: 2026-08-30

Status

Accepted.

Context

The bundled stack has one definition, deploy/docker-compose.yml, and it is not hand-written: it is a projection of the services topology in the app model (SERVICES → toComposeObject), regenerated by gen:all and diff-checked in CI. That is what stopped the compose file drifting from the code — a hand-edit that the generator would drop is caught the next time anyone runs it.

People run this on Kubernetes too. A single-node k3s cluster is a common homelab shape, and the app image already assumes nothing about where its backends are (ADR 0002, containers.md). Until now the only route was to hand-write manifests against the compose file, which is exactly the drift the generator exists to prevent — with a second copy of every port, image tag, env name and healthcheck to keep in step.

Two ways to add Kubernetes support:

  1. Hand-written manifests / a Helm chart under deploy/. Familiar, flexible, and a second source of truth for the same topology.
  2. A second projection of the model — toKubernetesObjects beside toComposeObject, emitting a kustomize base — generated, committed, diff-checked like the compose file.

Decision

Option 2. packages/shared/src/blueprint/k8s.ts projects the same SERVICES into plain manifests; scripts/gen-k8s.ts writes them to deploy/k8s/base/ as a kustomize base, one file per service, plus a kustomization.yaml and a .env.template. It runs as part of gen:all, so CI fails on a stale base the same way it does for compose.

The translation rules, so the two shapes stay recognisably the same stack:

Kustomize rather than Helm because the generated artifact should be plain manifests anyone can read and kubectl apply; kustomize is built into kubectl, and overlays are the right home for the per-cluster choices (exposure, storage, mirror) that a generator must not bake in.

Consequences