containix
github · pkg.go.dev · v0.1.0
λ

Build. Push. Pull.
Reference.

— What it is

A containerd shim that runs Nix flakes as Kubernetes pods. Your flake reference is the artifact. The shim assembles the rootfs at startup.

— What it replaces

The Dockerfile. The image build. The registry push. The layer cache. The "works in CI, fails in prod" gap between them.

The pod spec

One annotation. That's the API.

Pods opt in with a RuntimeClass. The flake reference rides as an annotation. The image field still exists because the OCI spec demands one — a 49-byte empty image satisfies the contract.

Containix reads the annotation, resolves the flake against your binary cache, and bind-mounts the closure as the container's filesystem. runc sees a normal pod.

pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: my-app
  annotations:
    containix.dev/flake:      "github:org/app#default"
    containix.dev/entrypoint: "/bin/myapp"
spec:
  runtimeClassName: containix
  containers:
  - name: app
    image: "containix/empty:1.0.0"  # required, ignored
From annotation to running process

Four steps. No magic.

01 — Read

Annotation

The shim parses the flake reference and entrypoint from the pod's metadata.

02 — Resolve

Closure

nix build returns store paths. The closure is fetched from the binary cache.

03 — Assemble

Rootfs

Store paths are bind-mounted into a rootfs. An OCI spec is written alongside.

04 — Run

runc

The shim hands off. Standard container lifecycle from here. Nothing exotic.

What you don't have to do anymore

The pieces that go away.

— 01
Dockerfiles, gone.
Your flake describes the closure already. There's no second source of truth to maintain, no RUN apk add dance, no minus-one tag drift.
— 02
Registries, optional.
The closure comes from your binary cache. Cachix, Attic, S3, anything that speaks the Nix substituter protocol. Pods skip the registry-pull round trip.
— 03
Layer caches, obsolete.
Two pods on the same node sharing a library? They mount the same store path. One copy on disk. The kernel sorts the rest.
— 04
"Works in CI, fails in prod," banished.
Same flake lock at every stage. The closure you tested is the closure that runs. No build environment, no base image to drift.
— 05
Surprise binaries, eliminated.
The rootfs contains exactly what the closure declared. No shells, no package managers, no busybox you forgot was in there.
Where it stands

End-to-end. Early. Pin commits.

Containix is consumable today as packages.default or a NixOS module. Core packages sit at 91% test coverage. The shim has been driven on a NixOS VM and a local cluster.

The API will move before v1. If you depend on it, pin commits — not branches. File issues ungracefully; the project would rather hear about them.

Read the source. Run the shim.