ADR-0815: Distroless Dockerfiles for vmafx-operator and vmafx-node¶
- Status: Accepted
- Date: 2026-05-29
- Deciders: lusoris
- Tags:
docker,ci,release,operator,node,k8s,phase4b,fork-local
Context¶
PR #152 added the vmafx-operator (cmd/vmafx-operator, ADR-0714) and the first published vmafx-node Dockerfile (docker/Dockerfile.node, ADR-0717). That PR did not include a dedicated docker/Dockerfile.operator — the operator binary was only built locally with go build. Without a release-wired image the operator cannot be deployed from a Helm chart or a kubectl apply manifest, and the node image (while the file exists) was not yet published by any CI workflow.
Two decisions are needed:
-
Operator image: a
docker/Dockerfile.operatorthat buildscmd/vmafx-operatoras a pure-Go, CGO_ENABLED=0 binary into agcr.io/distroless/static-debian12runtime. The operator never calls libvmaf directly (it drives Kubernetes CRD reconciliation); static-distroless is therefore the appropriate runtime — it is ~2 MB smaller thandistroless/cc-debian12and has no libc in the attack surface. -
CI release pipeline: a new workflow
docker-publish-operator-node.ymlthat fires onv*tag pushes andworkflow_dispatch, builds both images forlinux/amd64+linux/arm64(QEMU for the node's CGO stages; BuildKit cross-compilation for the operator's pure-Go binary), and applies the same cosign + syft SBOM pipeline asdocker-publish-production.yml(ADR-0698).
The node image (docker/Dockerfile.node) already existed and already used distroless/cc-debian12. This ADR covers wiring it to the release CI — no structural changes to the file itself.
Decision¶
We will add docker/Dockerfile.operator and the CI workflow .github/workflows/docker-publish-operator-node.yml.
Dockerfile.operator: two stages —golang:1.23-bookwormbuilder (CGO_ENABLED=0, TARGETOS/TARGETARCH build args for native cross-compilation) →distroless/static-debian12runtime running as uid 65532 (distrolessnonroot).Dockerfile.node(existing): no structural change; the workflow targets--target node-cpu(amd64 + arm64) for the released image; GPU variants remain manual or future-tracked.- CI workflow: three jobs (
build-operator,build-node,smoke-test) + a final aggregator gate. Operator job setsTARGETARCHbuild-arg per matrix platform; node job uses QEMU for arm64. Both jobs sign via cosign keyless OIDC and attest a CycloneDX SBOM.
Tag matrix for operator:
| Tag suffix | Platforms | Description |
|---|---|---|
| (none) | amd64, arm64 | vmafx-operator |
Tag matrix for node:
| Tag suffix | Platforms | Description |
|---|---|---|
| (none) | amd64, arm64 | vmafx-node (CPU) |
GPU node variants (-cuda12, -rocm6, -sycl) are out of scope for this ADR; they are amd64-only and will be wired in a follow-on.
Alternatives considered¶
| Option | Pros | Cons | Why not chosen |
|---|---|---|---|
distroless/cc-debian12 for operator | Same base as node | Includes glibc — unnecessary for a CGO_ENABLED=0 binary; ~2 MB larger; wider libc attack surface | distroless/static-debian12 is correct for static binaries |
| Inline operator build inside the node Dockerfile | Fewer Dockerfiles | Couples operator build time to the heavy ffmpeg/vmaf build stages; harder to cache | Separate file, faster CI, independent caching |
| Helm chart OCI artifact (no separate Dockerfile) | All-in-one | Helm OCI does not replace container images; the operator Deployment spec still needs an image reference | Must have a separate image |
Consequences¶
- Positive: operator and node are both released as signed, SBOM-attested, multi-arch OCI images on every
v*tag. Helm chart can referenceghcr.io/vmafx/vmafx-operatorandghcr.io/vmafx/vmafx-nodewith a real digest. - Negative: two additional CI jobs per release (~15 min each). The node job builds the full ffmpeg stack twice (amd64 native + arm64 via QEMU), which is slow (~30 min arm64).
- Neutral / follow-ups: GPU node variants should be added in a follow-on PR; the
docker-publish-production.ymlpattern can be copied exactly.
References¶
- ADR-0698: distroless base-image policy (production Dockerfile).
- ADR-0709: VMAFX Phase 4b distributed platform (parent scope).
- ADR-0713: vmafx-node Go worker binary.
- ADR-0714: vmafx-operator kubebuilder skeleton.
- ADR-0717: vmafx-node ffmpeg version policy + existing
docker/Dockerfile.node. - req: "add
Dockerfile.operatorandDockerfile.node(the operator builds the Go binarycmd/vmafx-operator, the node buildscmd/vmafx-node). Both distroless per ADR-0698. Multi-arch amd64+arm64. Wire to release-please + CI to build on tag."