TL;DR Talos en NixOS zijn allebei declaratief en allebei immutable te maken, en daar houdt de gelijkenis wel zo’n beetje op. Talos draait Kubernetes en verder niks, en er is geen shell om in te driften. NixOS is een volledige Linux waarbij de hele machine in één Nix-config staat. Ik draai Talos op m’n clusternodes en NixOS op de machines eromheen, en deze post is hoe ik bij die verdeling uitkwam.
Ik heb over Talos Linux geschreven als het immutable Kubernetes-OS, en ik heb Arch en NixOS voor werkstations naast elkaar gelegd. Na allebei bleef dezelfde vraag in m’n inbox vallen. Waarom dan geen NixOS op de Kubernetes-nodes zelf?
Terechte vraag. Op papier lijken de twee familie. NixOS en Talos zijn allebei declaratief. Allebei immutable te maken. Allebei zetten ze de configuratie van een machine onder versiebeheer. Dus waarom zou je voor een cluster de één boven de ander kiezen?
Eerst m’n bias. Ik draai een homelab waar ik elke laag wil snappen en in eigen hand wil hebben, en drift verdraag ik slecht. Beide heb ik in productie gedraaid, en Nix vind ik als taal gewoon leuk, wat niet iedereen zal beamen. Ik begin hier dus met een voorkeur die al vaststaat. Ik heb toch geprobeerd ze allebei een eerlijke kans te geven.
Ik vergelijk ze op wat m’n eigen keuze bepaalde: hoe je ze configureert, wat een aanvaller op de machine aantreft, wat de node verder nog kan draaien, hoe upgrades en rollbacks gaan, hoe Kubernetes op de node komt, en hoe debuggen eruitziet als er iets stuk is. De featurelijstjes staan al op de homepages van beide projecten.
Waar de twee expres van elkaar verschillen
Talos Linux draait Kubernetes en verder niks. Elke ontwerpkeuze erin buigt naar die ene taak.
NixOS is een general-purpose Linux die toevallig goed is in Kubernetes. Het kan alles wat een traditionele distributie kan, alleen staat de hele machine beschreven in één declaratieve config.
Elke trade-off hieronder komt uit dat ene verschil in bedoeling.
Configuratie
Talos: YAML
Talos-configuratie is platte YAML:
machine:
type: controlplane
network:
hostname: node-01
interfaces:
- interface: eth0
dhcp: true
install:
disk: /dev/sda
cluster:
clusterName: production
controlPlane:
endpoint: https://k8s.example.com:6443
Je past het toe met talosctl apply-config en de node convergeert naar die staat. Meer valt er niet te leren. Het schema is gedocumenteerd en de velden doen wat erop staat.
NixOS: de taal Nix
NixOS-configuratie schrijf je in Nix, een functionele taal met eigen ideeën:
{ config, pkgs, ... }:
{
networking.hostName = "node-01";
services.k3s = {
enable = true;
role = "server";
extraFlags = toString [
"--cluster-init"
"--disable traefik"
];
};
# Je kunt hier alles doen
services.prometheus.exporters.node.enable = true;
virtualisation.docker.enable = true;
system.stateVersion = "24.05";
}
Je past het toe met nixos-rebuild switch en het systeem convergeert naar wat je declareerde. Zelfde idee, ander vocabulaire.
Talos z’n YAML is beperkt en je leert het in een middag. Nix is een stuk krachtiger, en het kostte mij weken voordat het ophield vreemd te voelen. Dat zeg ik als iemand die het uiteindelijk leuk is gaan vinden.
De vraag is dus scope. Heeft de node alleen Kubernetes nodig, dan is de YAML genoeg en koopt de Nix-leercurve je niks. Heeft de node Kubernetes plus een stapel andere dingen nodig, dan is diezelfde leercurve wat die stapel betaalt.
Security
Talos: niks te vinden
Talos haalt z’n security uit weglaten:
- Geen SSH-daemon
- Geen shell
- Geen package manager
- Read-only root filesystem
- Minimale userspace (alleen containerd , kubelet en het hoognodige)
Een aanvaller die een container compromitteert en naar de host ontsnapt, vindt daar vrijwel niks. Geen tools om mee te pivoten, geen voor de hand liggende manier om te blijven zitten, geen package manager om malware mee binnen te halen. Dat is blast radius verkleinen als OS-ontwerp, en dat vind ik belangrijker dan de meeste features.
NixOS: gehardend als jij het zegt
NixOS kun je een heel eind hardenen, maar elke stap is opt-in:
{
# Disable SSH password auth
services.openssh = {
enable = true;
settings.PasswordAuthentication = false;
};
# Firewall
networking.firewall = {
enable = true;
allowedTCPPorts = [ 6443 ];
};
# Read-only store (altijd waar)
# Maar /etc, /var, /home zijn nog steeds mutable
}
De /nix/store is altijd read-only, en dat is mooi. /etc, /var en /home blijven mutable tenzij je er iets aan doet. Dat kan, en het is iets wat je moet onthouden.
Talos is dus gehardend bij aflevering en NixOS is gehardend nadat jij het werk hebt gedaan. Voor een node die alleen Kubernetes draait wil ik de paranoïde defaults, en liever heb ik niet eens de optie om ze over te slaan.
Wat de node verder kan draaien
Hier lopen de twee het hardst uit elkaar.
NixOS: alles
Een NixOS-node draait Kubernetes en daarnaast alles wat je verder op een Linux-machine zou zetten. De Prometheus node exporter, een log shipper of monitoring agents als systemd-service in plaats van als pod. Een bare-metal database of een legacy app die niks in een container te zoeken heeft. Elk van de 80.000+ packages in nixpkgs. De debugtools die je op de node zelf wilt, één configregel verderop.
Hier een PostgreSQL naast Kubernetes, om te testen:
{
services.k3s.enable = true;
services.postgresql = {
enable = true;
package = pkgs.postgresql_15;
};
}
Talos: verder niks, met opzet
Talos z’n beperkingen zíjn het product. Een ongedocumenteerde wijziging kun je niet maken, want er is geen shell om hem in te maken. Elke wijziging gaat door de config, dan de API, dan apply. De node is z’n config en er is geen tweede plek om te kijken. En wat niet op de machine staat kan niet misbruikt worden.
Draaien je nodes alleen maar Kubernetes-workloads, dan houden die muren je van fouten af. Moeten je nodes meer doen, dan zijn diezelfde muren een kooi, en is NixOS de uitweg.
Upgrades en rollback
Talos: twee partities
talosctl upgrade --nodes 192.168.1.10 \
--image ghcr.io/siderolabs/installer:v1.7.0
Talos schrijft het nieuwe OS naar een tweede partitie en reboot erin. Boot de nieuwe image niet, dan valt hij uit zichzelf terug op de vorige. De upgrade landt helemaal of hij landt niet, en jij zit in geen van beide gevallen naar een console te staren.
NixOS: generations
nixos-rebuild switch --flake .#node-01
Elke configuratiewijziging levert een nieuwe generation op, en elke generation is bootbaar vanuit GRUB. Teruggaan is een eerdere kiezen:
nixos-rebuild switch --rollback
# Of selecteer vanuit boot menu
NixOS bewaart standaard meerdere generations, dus je kunt terug naar de configuratie van weken geleden.
Beide rollbackverhalen zijn goed, en dat is zeldzamer dan het klinkt. Talos doet het voor je als een boot mislukt. NixOS bewaart zoveel generations als je wilt en laat jou kiezen.
Hoe Kubernetes op de node komt
Talos: het zit er al op
Talos komt met alles wat een cluster nodig heeft. De Kubernetes-componenten zitten ingebouwd, de CNI -config staat in de machine config, etcd wordt voor je beheerd, certificaten roteren vanzelf, en een upgrade draint de node, upgradet hem en geeft hem weer vrij. Je installeert Kubernetes niet op Talos. Het komt met het OS mee.
NixOS: kies er één
NixOS geeft je opties:
# Optie 1: K3s (lichtgewicht)
services.k3s = {
enable = true;
role = "server";
};
# Optie 2: Volledige Kubernetes via kubeadm
services.kubernetes = {
roles = ["master" "node"];
masterAddress = "k8s.example.com";
};
# Optie 3: Alleen de kubelet
services.kubernetes.kubelet.enable = true;
Jij bepaalt hoe Kubernetes op de machine komt. Meer opties, en elke optie is een beslissing die nu van jou is.
Talos betekent minder beslissingen en defaults die specifiek voor Kubernetes gekozen zijn. NixOS laat je het bedraden zoals jou uitkomt, en dan ben jij degene die achter die bedrading moet staan.
Debuggen
Talos: via de API
# Logs bekijken
talosctl logs kubelet --nodes 192.168.1.10
# Systeem info ophalen
talosctl get members
# Bestanden lezen
talosctl read /etc/hosts
# Dashboard (read-only TUI)
talosctl dashboard
Alles gaat via de API. Geen SSH, geen shell. Er is een debugcontainer voor noodgevallen, en die is met opzet onhandig zodat je er niet uit gewoonte naar grijpt.
NixOS: SSH erin
# SSH erin
ssh root@192.168.1.10
# Gebruik elke tool
htop
journalctl -u k3s
kubectl get nodes
vim /etc/nixos/configuration.nix
Het is een Linux-machine. Elke tool die je kent werkt al. Comfortabel, en het is ook precies hoe drift begint, want elke SSH-sessie is een kans om iets te fixen dat nooit meer terug in Git komt. Ik heb het zelf gedaan.
Talos laat je debuggen via de API, de logs en de metrics. NixOS laat je debuggen hoe je wilt, inclusief de manieren waarop de node anders achterblijft dan z’n config. Of die vrijheid helpt of pijn doet, hangt af van hoe gedisciplineerd je bent op een slechte dag.
Wat ik zou kiezen
Na beide gedraaid te hebben, beslis ik zo.
Kies Talos als:
- Kubernetes de enige workload op deze nodes is
- je het kleinste attack surface wilt dat er te krijgen is
- drift voorkomen je meer waard is dan flexibiliteit
- je prima machines via een API beheert
- het team al declaratief werkt
Kies NixOS als:
- dezelfde nodes ook non-Kubernetes-workloads moeten draaien
- je een volledige Linux wilt, met de declaratieve voordelen erbij
- je packages nodig hebt die niet als Talos-extensie bestaan
- het team al in Nix zit
- je verder wilt aanpassen dan Talos toestaat
Mijn setup
Ik draai beide, in verschillende rollen. De Kubernetes-nodes in m’n homelab zijn Talos. Die draaien alleen Kubernetes, en ik wil de security en de eenvoud van een cluster dat zich nergens kan verstoppen. De servers eromheen zijn NixOS. Die draaien Postgres, backupdiensten en de monitoring die ik bewust buiten Kubernetes houd.
Dat geeft me clusternodes die niet kunnen driften, en NixOS-machines voor alles wat niet in een pod thuishoort. Elk staat waar z’n grenzen van pas komen.
De hybride aanpak
Sommige teams draaien NixOS op de controlplane-nodes, waar ze debugtoegang willen, en Talos op de workers, waar ze de lockdown willen. Het werkt. Het betekent ook twee soorten node onderhouden, zolang het cluster bestaat.
Er is nog een pad dat ik heb zien werken. Begin op NixOS omdat het vertrouwd is, en stap over naar Talos zodra het team gewend is aan declaratief werken. NixOS leert je het denkmodel rustig aan. Talos dwingt het af.
De KubeVirt-optie
Er is een derde aanpak die op tafel hoort. KubeVirt, met NixOS als VMs binnen Kubernetes. Talos, of een ander minimaal OS, gaat op de bare metal, en de NixOS-workloads worden VMs die Kubernetes beheert.
De stack ziet er zo uit:
Waarom dit werkt
Je VM-definities worden Kubernetes-manifests:
apiVersion: kubevirt.io/v1
kind: VirtualMachine
metadata:
name: nixos-workload-01
spec:
running: true
template:
spec:
domain:
resources:
requests:
memory: 4Gi
cpu: 2
devices:
disks:
- name: nixos-disk
disk:
bus: virtio
volumes:
- name: nixos-disk
persistentVolumeClaim:
claimName: nixos-workload-01-pvc
Met ArgoCD aan het stuur staat deze VM-definitie in Git. De NixOS-config binnen de VM staat ook in Git. Van boven tot onder is het geheel declaratief en heeft het een geschiedenis.
De volledig declaratieve stack
Combineer dat met:
- Ansible voor de bare-metal-provisioning, Talos of K3s op de fysieke nodes zetten
- ArgoCD voor alles in Kubernetes, KubeVirt-VMs inbegrepen
- NixOS-flakes voor de VM-configuraties, uit Git gehaald als de VM boot
Dan staat je hele infrastructuur, van bare metal tot VMs tot Kubernetes-workloads, in Git. Wijzig de NixOS-config van een VM, commit, ArgoCD synct, de VM herbouwt. Volledige audit trail, volledige reproduceerbaarheid, en precies het soevereiniteitsverhaal waar ik steeds weer op uitkom.
Wanneer dit zinvol is
Dit is z’n complexiteit waard als:
- je gemengde workloads hebt, deels in containers en deels in een volledige VM
- je één control plane wilt, Kubernetes, voor alles
- je workloads hebt die niet in een container passen maar die je toch onder GitOps wilt
- je KubeVirt toch al draait om andere redenen
Het kost wat. Je draait nu een virtualisatielaag binnen Kubernetes, met alles wat daarbij hoort. Maar heb je toch VMs nodig, dan is ze beheren via dezelfde GitOps-pipeline als de rest van je omgeving een sterke positie.
Voorbeeld: een database op een NixOS-VM
Stel, je wilt een PostgreSQL-server met kerneltuning die niet in een container past. In plaats van met de hand een aparte NixOS-server te onderhouden:
- Definieer de VM in een KubeVirt-manifest, in Git
- De VM boot NixOS en haalt z’n config uit een flake, in Git
- ArgoCD beheert de levenscyclus van de VM
- Backups, monitoring en netwerk lopen allemaal via Kubernetes
De database krijgt de flexibiliteit van NixOS, jij krijgt de scheduling van Kubernetes, en alles staat in Git.
Welke beperkingen wil je
NixOS en Talos wijzen naar dezelfde soort infrastructuur: declaratief, reproduceerbaar, onder versiebeheer. Wat verschilt is welke beperkingen ze jou opleggen.
Talos zet je vast op alleen Kubernetes, alleen de API en zo min mogelijk oppervlak. Dat wil je als je liever hebt dat het systeem de discipline afdwingt dan dat je op de jouwe leunt.
NixOS houdt je aan een declaratieve configuratie en laat je verder de hele Linux-machine. Dat wil je als je die ruimte nodig hebt en je het team vertrouwt om er niet mee te gaan driften.
Voor pure Kubernetes-nodes draai ik Talos. Voor alles eromheen draai ik NixOS. Beide projecten releasen regelmatig en beide communities zijn actief, dus kies je vandaag, kies dan op wat de node verder nog moet draaien. Dat was de vraag die het voor mij besliste.
