· · 11 min leestijd Advanced

NixOS vs Talos voor Kubernetes nodes: twee smaken immutable infrastructuur

Vergelijking van NixOS en Talos Linux als besturingssysteem voor Kubernetes nodes. Beide zijn declaratief en immutable, maar met heel verschillende trade-offs.

Een kristallen kubus en een gladde zwarte plaat met één poort, een goblin zoekt op de plaat naar een toetsenbordaansluiting

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:

KubeVirt Stack

ArgoCD (GitOps)

KubeVirt VMs (NixOS, andere OSes)

Kubernetes (K3s/Talos)

Bare Metal (Ansible provisioned)

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:

  1. Definieer de VM in een KubeVirt-manifest, in Git
  2. De VM boot NixOS en haalt z’n config uit een flake, in Git
  3. ArgoCD beheert de levenscyclus van de VM
  4. 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.