ADR-0003: Builds run as Kubernetes Jobs with rootless BuildKit
Status
Accepted
Context
The operator must never build images in-process: builds are heavy,
bursty, and run untrusted user code (npm ci executes arbitrary
scripts).
Decision
Each Release gets a Job. The build container is the runtime's toolchain
image (from platform config); the static pagifier-builder binary and
buildctl are injected via an init container; a rootless buildkitd
sidecar builds and pushes the image. No privileged pods, no Docker
socket, no kaniko fork risk.
Consequences
- Untrusted build code runs in the tenant namespace under restricted PSA, bounded by Job deadlines and resource limits.
- The toolchain image is platform-controlled configuration — supporting a new Node version is an S3 upload, not an operator release.
- Server config (nginx.conf) is deliberately NOT baked into images: it mounts from a ConfigMap, so declarative header/cache changes redeploy without rebuilding.
- Cost: one buildkitd per build (no shared cache in V1). A shared BuildKit pool with cache volumes is the planned optimization.