k3sm licensed apache-2.0 (DCO)

kubernetes for macOS, natively

k3sm is the macOS/Apple-Silicon analog of k3s, a single-binary Kubernetes distribution whose Pods run as native Darwin processes by default. The vm RuntimeClass runs linux/arm64 images in a per-pod micro-VM.

$ k3sm kubectl apply -f examples/hello-native.yaml
pod/hello-native created

$ k3sm kubectl get pods
NAME           READY   STATUS    RESTARTS   AGE
hello-native   1/1     Running   0          4s

$ pgrep -fl 'tail -f /dev/null'
48213 /usr/bin/tail -f /dev/null

k3sm is the macOS and Apple-Silicon analog of k3s. It runs the upstream Kubernetes control plane in one binary, and a Pod is a Mac binary running as a native Darwin process. Seatbelt, the macOS sandbox, confines each Pod. Pod IPs are lo0 loopback aliases, wireguard-go links Macs, launchd supervises the daemons, and APFS clones each Pod’s root. Linux images take the vm RuntimeClass, which boots linux/arm64 images in per-pod micro-VMs on a single node.

The current release is v0.1.6. The Homebrew tap and a notarized package land with the first stable release.

Get Started#

Apple Silicon, macOS 26 or newer:

curl -fsSL https://k3sm.io/install.sh | sh

The script resolves the newest release, verifies its checksums, and runs sudo k3sm install. It shows you exactly what it will do before asking for anything. install covers pinning a version and every other channel. To build from source instead:

for r in apis runtimed darwin-net k3sm; do git clone https://github.com/k3sm-io/$r; done
go work init ./apis ./runtimed ./darwin-net ./k3sm
cd k3sm && CGO_ENABLED=1 go build -o k3sm ./cmd/k3sm
sudo ./k3sm install

The installed launchd daemon starts the control plane and registers the Mac as a node. The quickstart has the first manifest, including the kubernetes.io/os: darwin selector every k3sm Pod needs.

A Pod is then a native binary your Mac sandboxed and is supervising:

$ k3sm kubectl apply -f examples/hello-native.yaml
pod/hello-native created

$ k3sm kubectl get pods
NAME           READY   STATUS    RESTARTS   AGE
hello-native   1/1     Running   0          4s

$ pgrep -fl 'tail -f /dev/null'
48213 /usr/bin/tail -f /dev/null

No container or VM sits between the Pod and the process table. kubectl scheduled it, Seatbelt confines it, launchd keeps its supervisor alive, and ps sees an ordinary macOS process.

What People Run on It#

CI for Apple platforms

Build agents and test runners are already native macOS binaries. Run them as Deployments, Jobs and CronJobs under the upstream controllers, on Macs you already have.

Why It Is Not Another Linux VM#

1signed binary

upstream control plane

One binary supervises kube-apiserver, scheduler and controller-manager, built from source for darwin/arm64, over kine → SQLite. RBAC, admission, CRDs and the scheduler are the same upstream components.

0VMs on the default path

posix_spawn under Seatbelt

Containers are posix_spawn’d at host paths under a generated default-deny Seatbelt profile with an APFS clonefile root, so your app reaches the Mac’s frameworks with nothing in between. Linux images opt in to vm.

26+macOS, on Apple silicon

full Pod lifecycle

Seatbelt-confined Pods run the full lifecycle: logs / exec / top, and they keep running when the node daemon restarts. what runs lists what the default path takes.

What Replaces Each k3s Component#

k3sm keeps the upstream control plane and rebuilds everything that assumed Linux on macOS. The walk-through from kubectl apply down to posix_spawn is on architecture.

k3s (Linux) k3sm (macOS-native)

apiserver / scheduler / controller-manager
the same components, built from source upstream
etcd
kine → SQLite upstream
CoreDNS
the same, plus a getaddrinfo shim in pods upstream + native
kubelet
Virtual Kubelet + a Darwin provider (logs, exec and top are provider-built) native
containerd + runc
k3sm-runtimed (Seatbelt at host paths, APFS, posix_spawn, userspace limits) native
Flannel
lo0-alias pod IPs and a wireguard-go mesh native
kube-proxy
a userspace Service proxy native
systemd / OpenRC
launchd, an app-bundle-wrapped daemon native
helm-controller, auto-deploying manifests
HelmChart and HelmChartConfig in helm.k3sm.io/v1, with the same fields as k3s, and manifests in a root-owned directory applied automatically native

What k3sm Does Not Do#

  • Does not run Linux images on the default path, where they are refused at pull. The vm RuntimeClass runs linux/arm64 images; linux/amd64 is a later release.
  • Does not pass CNCF [Conformance], which requires Linux containers, cgroups and CNI. The conformance profile states what is claimed.
  • Does not isolate tenants on one node, because every Pod runs as the same unprivileged _k3sm user under its own Seatbelt profile.
  • Does not enforce CPU limits. Memory is sampled and can drive an OOMKill or an eviction; CPU gets QoS and no ceiling.
  • Does not enforce NetworkPolicy as a security boundary. The server node applies it to Service-VIP ingress only, direct Pod-IP traffic bypasses it, and a joined worker enforces none.

limitations has the complete list, including volumes, DNS, restartPolicy, and which addresses Services answer on.