Gerrit Tamboer

Zijn we klaar voor embedded Kubernetes? Ronde 2

6 november 2026

In de eerste ronde van dit onderzoek vergeleek ik KubeSolo, K3s en Talos op identieke virtuele hardware, met de beschikbare hoeveelheid geheugen stapsgewijs verlaagd totdat elke installatie uiteindelijk onbruikbaar werd. KubeSolo bleek het lichtst (382 MB), Talos het zwaarst maar het meest voorspelbaar (726 MB), en K3s nam met 591 MB een tussenpositie in. In deze tweede ronde breid ik die vergelijking uit met drie kandidaten die in de eerste ronde nog alleen genoemd werden: k0s, MicroK8s en Kairos (met K3s).

Vorige aflevering

In de vorige aflevering onderzocht ik de eerste drie kandidaten: Talos, K3s en KubeSolo onder dezelfde geheugendruk. Benieuwd wat daaruit kwam? Lees ronde 1 hier.

Deze drie bleken in de praktijk minder vergelijkbaar dan verwacht, en één liet een faalpatroon zien dat ernstiger was dan alle drie de kandidaten uit ronde 1 samen. Toch blijken die afzonderlijke incidenten meer met elkaar gemeen te hebben dan je op het eerste gezicht zou denken: onder de motorkap komt bij vier van de zes geteste configuraties dezelfde architecturale zwakte terug, verdeeld over drie verschillende Kubernetes-implementaties.

Drie nieuwe architecturen

k0s volgt dezelfde filosofie als K3s uit ronde 1: een compacte, losse Kubernetes-distributie bovenop een bestaand besturingssysteem. Het scheidt kubelet wel als apart proces van de rest van de control-plane, een tussenpositie tussen K3s' volledige bundeling en Talos' volledige scheiding. MicroK8s kiest voor dezelfde aanpak, maar is via Canonical's snap-pakketsysteem onlosmakelijk verbonden aan systemd, en dus aan een Ubuntu-achtige OS-basis. Kairos ten slotte is geen runtime-bovenop-een-OS zoals de andere twee, maar een eigen framework voor immutable besturingssystemen, conceptueel dichter bij Talos uit ronde 1. Later in dit artikel blijkt dat die vergelijking maar beperkt opgaat.

Architectuur naast elkaar: gestandaardiseerde 2GB-meting

Alpine + k0s

OS: Alpine Linux

k0s-controller (1 proces) apiserver · scheduler · controller-manager · kine (SQLite)

kubelet (apart proces)

containerd

corednskube-router
Totaal, 2GB-test635 MB
Ubuntu + MicroK8s

OS: Ubuntu 24.04 minimal

kubelite (1 proces) apiserver · scheduler · controller-manager · kubelet · kube-proxy

k8s-dqlite (Raft, géén kine)

containerd

corednscalico-node ×5cluster-agent
Totaal, 2GB-test828 MB
Hadron + Kairos + K3s

OS: Hadron Linux (musl + systemd)

k3s-server (1 proces) apiserver · scheduler · controller · kubelet · kube-proxy · kine

containerd

kairos-agent: eenmalig bij boot, geen achtergronddienst

coredns
Totaal, 2GB-test772 MB

k0s plaatst kubelet apart, net als Talos; MicroK8s en Kairos+K3s bundelen net als KubeSolo/K3s uit ronde 1.

Hoeveel kost het kale besturingssysteem eigenlijk?

De opzet van deze ronde wijkt op een paar punten af van ronde 1. MicroK8s is bewust getest op Ubuntu Server Minimal in plaats van Alpine. MicroK8s wordt namelijk via snap gedistribueerd, en snapd vereist systemd, dat op Alpine ontbreekt. Ook bij Kairos moest ik de oorspronkelijke opzet aanpassen: de officiële Alpine-variant bleek tijdens dit onderzoek niet langer actief gebouwd te worden. De huidige releases leveren alleen nog Hadron, Kairos' eigen Linux-distributie.

Omdat de OS-basis tussen de kandidaten verschilt, vergelijkt deze ronde in feite complete combinaties van besturingssysteem en Kubernetes-runtime, niet kale runtimes op zich. Daarom heb ik voor het eerst ook de kale besturingssystemen zelf gemeten, zonder Kubernetes-componenten en drie minuten na een schone reboot. Die baseline ontbrak in ronde 1, maar blijkt belangrijk om de verschillen tussen de kandidaten goed te kunnen interpreteren. De metingen van Alpine, Ubuntu en Hadron hieronder horen daarmee bij de kern van deze ronde, niet bij een bijzaak.

Kaal besturingssysteem, geen Kubernetes (t+3min na schone reboot)

96 MB
Alpine
123 MB
Hadron+Kairos
266 MB
Ubuntu minimal

Hadron draait, net als Ubuntu, een volledige systemd-stack, maar blijft opvallend dicht bij Alpine's OpenRC-cijfer.

Twee dingen vallen op. Ten eerste: Ubuntu's "minimal" cloud-image is met 266 MB bijna drie keer zo zwaar als kaal Alpine, nog vóórdat MicroK8s ook maar gestart is. Dat is relevant voor een project dat zichzelf "micro" noemt, maar verplicht op deze OS-basis draait. Ten tweede: Hadron, Kairos' eigen, vanaf de basis opgebouwde distributie, komt met 123 MB opvallend dicht bij Alpine uit, ondanks dat beide Hadron én Ubuntu een volledige systemd-stack draaien. Dat systemd zelf niet de verklaring is voor Ubuntu's gewicht, komt verderop nog terug.

Geheugengebruik bij 2GB: de volledige oplossing

Met die OS-baselines in het achterhoofd, hier het totale geheugengebruik van elke kandidaat: besturingssysteem, Kubernetes-runtime en standaard add-ons samen, exact dezelfde meetmethode als ronde 1 (drie minuten na Ready, 2 GB toegewezen RAM).

Geheugengebruik bij 2GB toegewezen RAM

635 MB
k0s
828 MB
MicroK8s
772 MB
Kairos+K3s

RAM gebruikt ("used", free-equivalent) in MB, drie minuten na Ready. Ter referentie uit ronde 1: KubeSolo 382MB, K3s 591MB, Talos 726MB.

k0s is met 635 MB het lichtst van de drie, iets zwaarder dan K3s uit ronde 1 (591 MB) en logisch gezien kubelet hier als apart proces draait. MicroK8s is met 828 MB het zwaarst, grotendeels verklaarbaar: 266 MB daarvan is al de Ubuntu-baseline, en nog eens 338 MB gaat naar Calico, de volwaardige BGP-gebaseerde CNI die MicroK8s standaard meeneemt in plaats van het lichtere kube-router of flannel van de andere kandidaten. Kairos+K3s op Hadron landt met 772 MB tussen de twee in, zwaarder dan diezelfde K3s-versie op Alpine zou zijn (zie verderop).

Wat de documentatie belooft en waar maintainers op moeten letten

k0s geeft officieel 1 GB RAM op als minimum voor een gecombineerde controller+worker-node, het laagste officiële cijfer van alle zes kandidaten tot nu toe. Dat cijfer verdient meer achtergrond dan een simpele vergelijking met mijn eigen metingen, want wat er precies achter zit, is genuanceerder dan alleen "een optimistische schatting".

De officiële requirements-tabel noemt die 1 GB zonder enig voorbehoud voor architectuur: hetzelfde cijfer voor x86_64, aarch64, armv7l en riscv64. Maar dezelfde pagina bevat een voetnoot die in de tabel zelf niet terugkomt: "The operating system and application requirements must be considered in addition to the k0s part." Die 1 GB is dus voor k0s alléén, niet voor OS en k0s samen. De enige concrete meting die het project zelf publiceert, is gedaan op een AWS t3.xlarge met 16 GB RAM en Ubuntu Server, waar ze er zelf bij vermelden dat de OS zo'n 180 MB extra gebruikte, op een machine waar geheugendruk nooit een rol speelde.

Ik testte op 1 GB totaal, exact het bedrag dat k0s' eigen documentatie aanmerkt als "voor k0s alleen". Zelfs met mijn eigen, veel lichtere Alpine-baseline (96 MB) opgeteld bij hun eigen 510 MB-cijfer kom je nog altijd ruim onder de 1 GB waar geheugendruk zou gaan meespelen. Dat is geen beschuldiging aan het adres van het k0s-project (de voetnoot staat er immers gewoon, netjes gedocumenteerd), maar het is wel een punt van aandacht: de hoofdtabel en die voetnoot spreken elkaar in de praktijk tegen, en het cijfer is nooit onder echte geheugendruk gevalideerd. Voor precies de doelgroep die op dit soort cijfers afgaat, embedded/edge-gebruikers met een vast, krap geheugenbudget, zou een duidelijker gecombineerd cijfer (OS + k0s, getest onder druk) veel waard zijn.

Vier manieren waarop Kubernetes onder geheugendruk faalt

En dat laatste punt is geen overdrijving. Op 1 GB liet k0s het meest ernstige faalpatroon zien van alle zes kandidaten in dit onderzoek tot nu toe, en MicroK8s voegde op hetzelfde niveau een nieuw, nog niet eerder waargenomen faalpatroon toe. Met Talos' kubelet-geheugendruk-bescherming uit ronde 1 erbij, heb ik nu vier wezenlijk verschillende manieren gezien waarop een Kubernetes-installatie onder geheugendruk kan bezwijken.

Vier faalpatronen, vier mechanismen

  • KubeSolo / K3s (ronde 1), stabiel plateau: trage SQLite-queries (7-33s) onder druk, maar het systeem stabiliseert op een hoge, constante load. Geen herstarts, geen spiraal.
  • Talos (ronde 1), preventieve weigering: een verdieping van het faalgedrag dat ronde 1 al documenteerde. Echte etcd, geen kine; kubelet's eigen geheugendruk-bescherming weigert pod-sandboxes toe te wijzen, een nette, voorspelbare stop.
  • k0s & Kairos+K3s, wegspiralerende cascade: load 9 → 21 binnen 20 minuten, bij beide kandidaten functioneel identiek. Zelfde Slow-SQL-patroon als KubeSolo/K3s, maar hier versterkt het zichzelf tot SSH onbereikbaar wordt, ongeacht OS of Kairos-laag.
  • MicroK8s, periodieke herstart: kubelite herstart elke 12-20 minuten netjes zichzelf na een gemiste leader-election-lease. Dqlite zelf crasht nooit.

Geheugendruk kan via verschillende architecturale mechanismen tot uiteenlopende faalpatronen leiden. De gevolgen variëren van een stabiel maar traag systeem tot periodieke herstarts of een zichzelf versterkende cascade.

Laagste geteste werkende RAM-configuratie, ronde 2

k0s
MicroK8s
Kairos
768MB1024MB2048MB
StabielOnbruikbaar

k0s en Kairos+K3s storten op 1GB functioneel identiek in. Geen van beide op een lager niveau dan 1GB getest, gezien de ernst van het falen.

Om te checken of de rekensom uit de vorige sectie ook in de praktijk standhoudt (k0s' eigen 510 MB plus mijn Alpine-baseline van 96 MB, ruim onder de 1 GB-grens), heb ik tussendoor nog een extra, niet-standaard niveau geprobeerd: 1,2 GB. Dat bleef twintig minuten lang volledig stabiel (load average tussen 0,00 en 0,24), tegenover een load average van bijna 19 binnen vier minuten op 1 GB. Geen gestandaardiseerde ladder-rung zoals de andere niveaus in dit onderzoek, maar wel een nuttige losse steekproef: ergens tussen 1 GB en 1,2 GB ligt een harde knik, geen geleidelijke overgang. De berekening op basis van de voetnoot blijkt dus ruwweg te kloppen.

Kine: de gedeelde zwakke plek onder drie Kubernetes-implementaties

Wat KubeSolo, K3s en k0s gemeen hebben, is kine: een laag die etcd's eigen gRPC-API nabootst, maar de data feitelijk in SQLite opslaat in plaats van in een echte Raft-gebaseerde distributed store. Dat zijn drie verschillende Kubernetes-implementaties. Omdat Kairos in deze ronde K3s als runtime gebruikt, komt de kine-cascade in totaal in vier geteste configuraties voor (KubeSolo, K3s op Alpine, k0s en Kairos+K3s), maar nog altijd bij drie onderliggende implementaties. Kine is daarmee een gemeenschappelijke factor in die drie implementaties. Het helpt verklaren waarom deze configuraties anders zijn opgebouwd dan Talos, maar blijkt onder zware geheugendruk ook hun gedeelde zwakke plek. Zodra de geheugendruk oploopt, lopen SQLite-queries die normaal milliseconden duren op tot 7 à 33 seconden. Dat veroorzaakt een kettingreactie van timeouts en retries binnen de control-plane.

Belangrijke nuance: kine kost zelf weinig geheugen. Het is niet de reden dat de footprint groot is, het is de reden dat de ondergrens instort zodra je hem opzoekt. De waarschijnlijke oorzaak is architecturaal, niet SQLite zelf: bij alle drie implementaties draait kine in hetzelfde proces als apiserver en scheduler, zonder eigen resource-isolatie. Wanneer dat proces onder geheugendruk de garbage collector harder moet laten werken, ondervindt kine daarvan een neveneffect. MicroK8s' k8s-dqlite (een écht gedistribueerde SQLite met Raft-consensus, geen simpele vertaal-laag) lijkt daardoor een andere, iets robuustere categorie fout te vertonen: dqlite zelf crasht nooit, alleen de bundeling van apiserver/scheduler eromheen (kubelite) doet dat.

Kairos+K3s op Hadron bevestigt dat dit mechanisme los staat van de OS-basis: op 1 GB vertoonde deze combinatie een functioneel identiek traject aan k0s' instorting: load oplopend van 11 naar 15 binnen acht minuten, uiteindelijk SSH zelf onbereikbaar, geheugen steeds binnen budget. Dezelfde kine-laag, dezelfde cascade, ongeacht of de onderliggende OS Alpine of Hadron heet en ongeacht de Kairos-laag ertussen.

Wat kost Kairos' eigen laag, los van Hadron en K3s?

Omdat Kairos' Alpine-flavor wegviel, kon ik Kairos niet meer isoleren van zijn OS-basis zoals gepland. Als alternatief heb ik de drie lagen apart gemeten: Hadron kaal, Hadron met de Kairos-agent erbij (zonder Kubernetes), en vervolgens met K3s erbovenop.

Kairos gedecomponeerd: drie lagen, drie metingen

Hadron + Kairos-agent
123 MB
+ K3s
772 MB

Balken geschaald t.o.v. het totaal (772MB). De Kairos-agent zelf is in deze decompositie niet zichtbaar, zie hieronder waarom.

Het antwoord bleek eenvoudiger dan verwacht: de Kairos-agent kost in doorlopend gebruik vrijwel niets. systemctl list-units toont kairos-agent.service als inactive/dead. De agent past bij het opstarten eenmalig de cloud-config toe en de A/B-partitieverificatie, en blijft daarna niet als achtergronddienst hangen. De volledige 123 MB van de kale "core"-image is dus vrijwel zuiver Hadron zelf. De immutable-laag die Kairos conceptueel onderscheidt van een gewone Linux-installatie, is daarmee in RAM-termen bijna gratis: de overhead zit in boot-tijd, niet in geheugengebruik.

K3s zelf kostte op Hadron wel merkbaar meer dan diezelfde versie op Alpine uit ronde 1: 649 MB extra bovenop de Hadron-baseline, tegenover 495 MB extra bovenop Alpine's baseline in ronde 1. Dat is een verschil van zo'n 150 MB dat niet uit de OS-baseline zelf te verklaren is. Mogelijke verklaringen zijn een andere containerd-configuratie of systemd's cgroup-driver in plaats van Alpine's cgroupfs; dit is niet verder uitgezocht, gezien de beperkte meerwaarde voor de hoofdvraag van dit onderzoek.

Kairos en Talos: concurrenten in naam, niet in architectuur

Kairos en Talos worden vaak in één adem genoemd: beide zijn immutable, beide richten zich op Kubernetes, beide vermijden traditioneel pakketbeheer. Die gelijkenis houdt bij nadere beschouwing niet lang stand. Talos is een single-purpose besturingssysteem, specifiek gebouwd rond Kubernetes zelf: geen generieke userland, geen shell, geen SSH, uitsluitend te beheren via een eigen API. Het heeft geen identiteit los van Kubernetes. Het is, oneerbiedig gezegd, Kubernetes dat toevallig een besturingssysteem nodig had.

Kairos is het omgekeerde: een generiek framework voor immutable besturingssystemen (A/B-upgrades, cloud-config-bootstrapping, atomic updates) dat zelf geen mening heeft over wat erbovenop draait. Het kan K3s of k0s draaien, of helemaal niets (de "core"-image uit dit artikel), op in principe meerdere onderliggende Linux-distributies. Dat maakt Kairos conceptueel dichter bij Fedora CoreOS of Flatcar Container Linux met een optionele Kubernetes-laag, dan bij Talos' single-purpose ontwerp. Beide kunnen in dezelfde use-case terechtkomen, namelijk een immutable node voor een Kubernetes-cluster, maar de weg ernaartoe en de mate van keuzevrijheid die overblijft verschillen wezenlijk.

Het eindoordeel volgt in de volgende aflevering

Deze ronde bevestigt iets dat in ronde 1 al zichtbaar werd, maar nu scherper: RAM-gebruik alleen vertelt niet het hele verhaal. Een lage footprint zegt niets over hoe een systeem faalt als je de geheugengrens opzoekt, en dat blijkt per architectuur sterk te verschillen, los van de absolute cijfers. k0s' laagste officiële minimumvereiste ging gepaard met het heftigste faalgedrag van alle zes kandidaten; MicroK8s' zwaarste footprint ging gepaard met het meest "beschaafde" faalpatroon, juist dankzij een architecturaal robuustere datastore-keuze. De belangrijkste resultaten op een rij:

k0sMicroK8sKairos + K3s
OS-basisAlpineUbuntu 24.04 minimalHadron (eigen)
RAM (2GB, t+3min)635 MB828 MB772 MB
Kale OS-baseline96 MB (Alpine)266 MB123 MB
Boot tot Ready41s~45sn.g.
Datastorekine (SQLite)k8s-dqlite (Raft)kine (SQLite)
Standaard CNIkube-routerCalicokube-router
Laagst stabiel getesttussen 1GB en 2GB2 GBtussen 1GB en 2GB
Faalpatroon op de grensWegspiralerendPeriodieke herstartWegspiralerend (identiek aan k0s)
Officiële minimum-claim1 GB (optimistisch, zie boven)n.v.t.n.v.t.

Dit was ook de laatste experimentele testronde van deze reeks. Met zes kandidaten op tafel, KubeSolo, K3s en Talos uit ronde 1, en k0s, MicroK8s en Kairos (met K3s) hier, is er genoeg materiaal om een afgewogen conclusie te trekken. Dat kine/SQLite voor minstens de helft van de kandidaten de praktische bodem bepaalt, is daarbij een van de belangrijkste bevindingen om mee te nemen.

In de volgende, laatste aflevering van deze reeks volgt mijn eindoordeel: concreet advies over welke combinatie wanneer de voorkeur verdient, gebaseerd op alles wat deze twee testrondes hebben opgeleverd.

Alle metingen zijn direct uitgevoerd op een Proxmox-homelabcluster, oktober 2026. k0s en MicroK8s zijn herhaald met een gefixte serviceCIDR en, voor MicroK8s, een gecorrigeerde schijfpartitie na een provisioning-fout. Methodologie en volledige ruwe resultaten zijn op aanvraag beschikbaar.

Terug naar blog