nixk3s

The host is a flake, the cluster is git

Bare-metal k3s on NixOS with a fully declarative GitOps spine: nixidy renders typed Nix modules to a plain manifest tree, Argo CD reconciles it with prune and self-heal. No hand-applied YAML, no helm-on-the-CLI, no drift.

Status: pre-alpha, first modules landed

!

The spine is production-proven

This is not a design on paper. It runs a production single-node cluster today — 15+ Argo CD applications, GPU workloads, the whole works — and has survived node rebuilds and a bare-metal migration.

!

Modules landed

The generalized k3s-host, tenancy and apps modules, and the SPINE.md GitOps spine document, are extracted from that production system and published in this repo — and the first two are dogfooded: since 2026-07-22 the production cluster's k3s server config comes from k3s-host, and its entire Argo CD project layer is rendered by tenancy (zero-drift in-place adoption, semantically verified). apps is newer: the same cluster now declares its workloads through it, with a measured tail of whole objects still carried through the visible raw escape hatch.

!

No invented benchmarks or claims

Everything below describes the mechanism as it actually runs in production. Nothing is a projected number or a planned feature dressed up as shipped.

One coherent answer

The host is a flake

k3s server config, node labels, airgap images — all NixOS options, all reproducible. No manual host setup, no configuration drift between rebuilds.

Workloads are Nix too

nixidy renders typed Nix modules to a plain manifest tree you can read, diff, and audit. Argo CD (prune + selfHeal) reconciles the live cluster to exactly what git says it should be.

Airgapped images

Custom images are imported declaratively on the host via services.k3s.images — no registry required for cluster-internal pieces.

Tenancy by AppProject

A small, opinionated project model separating the things that manage the cluster from the things that run on it.

Modules

k3s-hostThe NixOS side: k3s server with sane bare-metal defaults, declarative node labels, airgap image import, disabled bundled components, extra server flags. Landed.
SPINE.mdThe nixidy environment pattern plus Argo CD bootstrap: render → commit → sync, with the rendered tree excluded from the render trigger so it never loops. Landed as a doc.
tenancyThe AppProject model and its allowed-destination conventions. Landed.
appsThe app grammar: an app declares what it NEEDS — image, ports, an exposure class, whether it scales to zero, which existing claims or node paths hold its state, which existing Secrets it consumes — and the module renders the Application, an optional Namespace, a Deployment and a Service. Options are parameters: it names objects and classes and takes every fleet fact from its consumer, so a declaration written against it is safe to publish. Landed.
addressingThe band model: a repository binds a band, its apps take slots inside it, and a slot outside it fails eval. Landed.
cockpitThe platform's own faces: a catalogue of the surfaces you open to see whether the platform is working, translated into the app grammar. Kept out of the default import on purpose. Landed.

Related projects

nixgpu

Priority-based single-GPU sharing, built on this spine. See nixgpu.corbet.ch.

nixapps

Curated nixidy app modules that deploy onto this spine. See nixapps.corbet.ch.

nixllm

The shared LLM serving lane (behaviors B4/B10/B14/B15 of the nixgpu contract), deployed onto this spine. See nixllm.corbet.ch.