On June 30, 2026, ngrok's Sam Rose shipped something absurd: a working Kubernetes cluster that runs entirely inside a browser tab. Pod lifecycles, the scheduler, Deployments and ReplicaSets, cluster DNS, service networking, garbage collection — all of it, in about 140KiB of gzipped JavaScript. The post hit 335 points and 103 comments on Hacker News, and the demo (three nodes, nine replicas, live request dots) runs without a single server behind it.
The stunt is fun. The inventory behind it is genuinely useful. To fit Kubernetes in a tab, Rose had to decide — component by component — what is load-bearing and what is ballast. That decision list is the closest thing our industry has to an experimental answer to a question every edge and single-node fleet operator asks: how little of Kubernetes do you actually need?
Here is the answer up front. This is the keep/drop table the port implies — labeled carefully as what the browser port skipped, not yet what your PaaS can skip. The adjudication comes later in this post.
| Component | Browser port | Verdict for a real single-node/edge fleet |
|---|---|---|
| Kubelet pod lifecycle + probes | Kept | Core — this is the irreducible unit |
| Scheduler | Kept | Core, even with one node |
| Deployment / ReplicaSet controllers | Kept | Core — desired-state reconciliation is the product |
| kube-proxy + CNI networking | Kept | Core — pods must find each other |
| Cluster DNS, IP allocation, container GC | Kept | Core — all three survived the cut |
| CRI container runtime | Kept (browser reimplementation) | Core — but yours already exists (containerd) |
| etcd | Dropped (in-memory state) | Replaceable, not removable — SQLite does the job |
| Cloud load balancers | Dropped (no cloud) | Genuinely droppable on a single box |
| systemd / OS process supervision | Dropped (the tab is the supervisor) | Collapsible to one supervisor process |
| Real image pulls | Dropped (TypeScript-defined images) | Must return — no real fleet skips the registry |
| ConfigMaps / Secrets | Dropped (not yet implemented) | Must return — every real app needs config |
| Resource requests / limits | Dropped (not yet implemented) | Must return — noisy neighbors are real on small boxes |
| RBAC / auth | Dropped | Returns at multi-tenant scale |
| Persistent volumes | Dropped | Returns the moment a tenant is stateful |
The rest of this post is the evidence for each row: what was actually built, why the most common description of it is wrong, and which drops survive contact with a real fleet.
It was not WASM at all
The most-shared description of this project is wrong, and the correction matters more than the stunt. Rose did not compile Kubernetes to WebAssembly. He tried — and hit compile errors, because Kubernetes calls system-level APIs that do not exist in a browser — then did something harder: a partial port of the Go codebase to TypeScript, line by line, over two months and 552 commits across 629 files.
The size comparison is the part worth memorizing. A bare Go "hello, world" compiled to WASM ships at roughly 540KiB gzipped. Webernetes — kubelet, scheduler, controllers, networking, runtime, the lot — ships at roughly 140KiB gzipped. The entire cluster is a quarter of the size of the runtime tax on an empty Go binary. That single ratio tells you how much of what we call "Kubernetes" is the Go runtime, the OS interface, and the everything-everywhere scaffolding — versus the actual orchestration logic, which turns out to be small enough to fit in a tab next to the page's fonts.
What exactly got ported? A partial kubelet (enough to run pods and probe them), the pod scheduler, the namespace controller, kube-proxy, the deployment controller and friends, a browser-based CNI so pods can talk over a simulated network, a browser-based container runtime behind the CRI, and an API for applying manifests and watching resources. What did not: ConfigMaps, Secrets, pod resources, persistent volumes, and real image pulls — images are TypeScript classes registered into a browser-side registry, not OCI blobs from Docker Hub.
Why the table is trustworthy
One compressed credibility note, because the whole post leans on this inventory. Nearly all of the roughly 100,000 lines were LLM-generated, and Rose's response to that was the correct one: he reviewed every line side by side with the Go original, and he built the test harness the project was missing. The same test suite runs dual-target — against a real k3s cluster and against webernetes — through a kubernetes.describe helper that injects either backend, using the official kubernetes-client/javascript API. At write-up time: 204 integration tests plus 1,855 unit tests, most of them direct ports of Go table tests. New bugs get a test that passes on k3s and fails on webernetes first, then a fix.
Total API-equivalent token spend was on the order of $4,300 across the two months, spiking hard in the final Deployment-support crunch — and in Rose's own accounting, his review time was still the most expensive line item.
That is why the keep/drop table deserves weight: the kept components are not reimplementations that look right, they are ports verified behavior-identical against k3s on hundreds of cases. When the table says the scheduler and kube-proxy survived, it means the logic survived contact with the real thing.
Adjudicating the drops: what your fleet can actually skip
Now the honest pass, row by row. A browser demo dropping something proves it is separable. It does not prove it is expendable. Three different fates await the dropped list.
True savings — drop these on a single node or edge box. Distributed state is the biggest one. Webernetes holds cluster state in memory; your fleet cannot do that and survive a reboot, but it does not need etcd either. k3s proved this years ago by shipping SQLite as the default datastore, with etcd, MySQL, and Postgres as options — one file instead of a quorum, on boxes as small as 512MB of RAM. Cloud load balancers are the second genuine drop: with one node there is nothing to balance across, and in-node networking plus a single ingress point covers it. Third is systemd-as-orchestrator-of-orchestrators: k3s collapses the control plane into a single binary and a single supervisor relationship, which is the production version of "the tab is the supervisor." These three — etcd-to-SQLite, no cloud LB, one process — are what take you from kubeadm's roughly 2GB-per-machine floor to k3s territory. They are proven, boring, and available today.
Must return — even on one node. Real image pulls come back first: a fleet that cannot pull OCI images is a demo, not a platform. ConfigMaps and Secrets come back with the first real tenant, because every application beyond "hello, world" needs config separated from code. Resource requests and limits come back fastest of all on small hardware — this is the finding the browser demo most understates. A bin-packed edge box running several tenants with no memory accounting is not a minimal fleet, it is an OOM-kill lottery. The kubelet features the port has not reached yet (pod resources especially) are precisely the ones a small box needs most, because contention arrives sooner when there is nowhere else to schedule.
Scale-gated — return when tenants multiply. RBAC and authentication are overkill for a single-operator edge box and non-negotiable the moment a second tenant gets an API key. Persistent volumes follow the same curve: stateless single-app boxes genuinely do not need them, and the first stateful tenant makes them load-bearing. The pattern is clean — the port's skip-list splits into "single-node simplifications" and "workload features it hadn't needed yet," and only the first group is real savings.
So the minimal viable control plane for a single-node or edge fleet, with evidence behind each line: kubelet plus CRI plus containerd, the scheduler, Deployment/ReplicaSet reconciliation, in-node networking with DNS and a service proxy, SQLite-grade durable state, a single supervisor process, image pulls, config primitives, and enforced resource accounting. Everything else on the CNCF landscape slide is either multi-node machinery or a workload feature waiting for its first tenant.
What the demo does not prove
Four honest limits, so nobody sizes a fleet off a tab. First, there is no real multi-node networking here — the "three nodes" are simulated, so nothing about CNI plugins, cross-node routing, or network policy at scale transfers. Second, nothing about etcd's failure modes transfers either; the port sidestepped distributed consensus entirely, which is exactly the part that pages you at 3am. Third, scheduling one pod onto a simulated node says nothing about scheduler throughput or bin-packing quality at hundreds of nodes. Fourth, there is no security boundary in a tab — no RBAC, no admission control, no isolation worth the name — so the demo cannot tell you what multi-tenancy costs. The keep-list is the irreducible core of orchestration logic. The bill for running it as a service — HA, security, storage, operability — is a separate invoice, and the browser never sees it.
The takeaway for platform builders
Strip away the novelty and webernetes is a working experiment in Kubernetes minimalism: the orchestration idea — watch desired state, reconcile actual state, give every pod an address and a name — fits in 140KiB. The gigabytes we associate with "running Kubernetes" are the price of distribution, durability, multi-tenancy, and the Go-and-OS scaffolding underneath, not the price of the idea itself. If you operate a single-node or edge fleet, that distinction is your budget: pay for SQLite instead of etcd, for one supervisor instead of a process tree, for no load balancer at all — and spend what you saved on the unglamorous rows, resource accounting and config primitives, that small boxes punish you for skipping.
Rose built the project to make interactive Kubernetes teaching content — the HN thread is full of educators mourning Katacoda's paywall and seeing a zero-cost, per-reader cluster in this — and as a teaching artifact it is already excellent. As an architecture argument it is better than it intended to be: a machine-checked list of what Kubernetes cannot shed, and permission, with receipts, to shed much of the rest.
Running single-node Kubernetes on machines you own and wondering where the floor is? Bex.co is the open-source, AI-native Render alternative — push a git repo, get a running HTTPS service on hardware you control, with the control plane kept as small as the workload allows. Star the repo on GitHub or deploy your first app today.



