Compare
A side-by-side of k3sm against Kubernetes in a Linux VM on a Mac, macOS VMs orchestrated across Macs, and a Linux Kubernetes cluster.
k3sm is Kubernetes whose nodes are Macs and whose Pods are Darwin processes, so it answers a different question than the VM-based tools. This page compares them point by point; the narrative version is why k3sm.
Four things are being compared:
- Kubernetes in a Linux VM on your Mac: desktop container tools, local-cluster tools, and Linux VM managers that boot Linux Kubernetes in one or more guests on a single Mac, with ports and directories forwarded back to the host.
- A Linux Kubernetes cluster: anything from a managed cloud offering to k3s on hardware.
- macOS VMs orchestrated across Macs: tools that boot every job as a macOS guest under Virtualization.framework and place guests on hosts from a controller, so a build farm boots a fresh macOS guest for every job.
- k3sm: Kubernetes whose nodes are Macs and whose Pods are Darwin processes.
Side by Side#
Verdicts only; every no and partial in the k3sm column is expanded in the two sections below.
| Linux VM on a Mac | a Linux Kubernetes cluster | macOS guests across Macs | k3sm | |
|---|---|---|---|---|
| runs your existing Linux images | yes, unmodified | yes, unmodified | no, VM images rather than container images | partial, linux/arm64 unmodified via vm; amd64-only images do not run |
CNCF [Conformance] certified | yes, typically | yes, typically | the unit of work is a VM, not a Pod | no, cannot be |
| runs a macOS-only workload | no | no | yes, in a macOS guest | yes |
| GPU and unified memory | no, behind the hypervisor | yes, Linux GPUs | partial, the guest’s paravirtualized GPU | yes, direct |
| filesystem performance for host files | partial, bind mounts | yes, native | partial, directories shared into the guest | yes, native |
| schedules a fleet of Macs | no, single laptop | no, different hardware | yes, a controller places guests on hosts | yes, wireguard mesh on two Macs today; experimental until v0.3 |
| per-tenant isolation on a shared node | yes, namespaces and cgroups | yes | yes, one VM per job | partial, shared trust domain |
| enforced CPU limits | yes, CFS millicores | yes, CFS millicores | yes, vCPUs per guest | no, accounting only |
| enforced memory limits | yes, cgroup guarantee | yes, cgroup guarantee | yes, guest memory size | partial, sampled then SIGKILL |
| NetworkPolicy as a security boundary | yes, with a CNI | yes | each guest has its own network stack | no, a hint on the server node; workers enforce none |
| eviction under node pressure | yes | yes | guests per host is the placement limit | partial, memory only, one Pod at a time |
Helm charts as HelmChart objects | yes, when the guest runs k3s | yes, on k3s | — | yes, k3s’s fields in helm.k3sm.io/v1; no private-repo credentials (Helm) |
| auto-applied manifest directory | yes, when the guest runs k3s | yes, on k3s | — | partial, apply-only; RBAC, admission, Secret and ServiceAccount objects refused |
| Secrets encrypted at rest | yes, an API server option | yes, an API server option | — | partial, opt-in on a new cluster; no key rotation |
| the API surface | upstream | upstream | the tool’s own API | upstream |
| release status | stable lines | stable lines | released tools | pre-release builds (v0.1.6 is current), ad-hoc signed |
| install | one download | — | an agent per Mac | install script; Homebrew tap at first stable release |
| resource cost on your Mac | an always-on VM | — | a VM per job | processes |
Where k3sm Loses#
- An
amd64-only Linux image does not run at all. It is refused at pull on both paths. The default path wantsdarwin/arm64, thevmpath wantslinux/arm64. An unmodifiedlinux/arm64image does run: addingruntimeClassName: vmboots it in a per-Pod micro-VM on a single node. On the default path a Pod is a Mac binary, so a workload that has to run there is rebuilt, not relabelled, though registry, tags, digests and pull secrets all behave as expected (what runs). A Linux VM tool runs any Linux image, amd64 or arm64, on a mature path today. - It is not CNCF-conformant, and there is no path to becoming so. The suite requires Linux containers, cgroups, CNI, and network namespaces; k3sm has none of them by construction and cannot carry a Certified Kubernetes badge.
- The release line is younger. Today the install is the script and builds are pre-releases; the Homebrew tap, the notarized package, and a supported-versions policy come with the first stable release planned. There is no support contract on offer.
- The isolation model is weaker. Same-node native Pods share a uid and a port space. Seatbelt
(macOS’s process sandbox) bounds what each Pod can reach, but it is neither a namespace nor a tenancy boundary. A Pod can name the
vmRuntimeClass for a per-Pod micro-VM boundary instead (linux/arm64only). - The resource model is weaker. CPU limits are accounting and QoS, never a millicore ceiling.
Memory limits are sampled
proc_pid_rusagefollowed by SIGKILL, which is best-effort and not a cgroup guarantee. Node-pressure eviction covers memory only, one Pod at a time. - NetworkPolicy is not a security boundary. It applies to Service-VIP ingress only, with no
egress and no
ipBlock, and a joined worker enforces none. - No head-to-head numbers. k3sm publishes no performance comparison against the tools in
this table; the figures it does publish, the
vmpath’s restart latency and idle cost, are on the limitations page. - Developer ID signing and notarization do not run inside a native Pod. A Pod reaches the
Apple frameworks, not the login session, so a keychain read comes back empty with a zero exit
status and a workload that tests for a certificate takes the wrong branch without an error.
Signing with a Developer ID identity and notarizing both rest on that keychain. SwiftPM does not
finish either:
swift buildre-signs its product by absolute path as its last step, and a Pod may name a file for signing only by a relative path. Sign and notarize on the host. AvmPod runs Linux and cannot stand in for it (limitations).
Each of these is a place to contribute; see community.
And Where It Wins#
- Your workload can be a Mac. Metal, CoreML inference on the Neural Engine, and full-speed
local disk at host paths are reached directly, because there is no guest between the Pod and
the OS. MLX model serving runs on the Apple GPU. The Seatbelt profile a Pod
runs under bounds this. What it reaches is the frameworks, not the login session. A Pod also
builds.
clangandswiftccompile C, Objective-C and Swift against an installed SDK and ad-hoc sign the result, with no guest to cross to do it. The limitations page lists the Apple services it holds a Pod away from. - A room full of Macs becomes a cluster. A Mac joins with one token, the upstream scheduler places work on it, a joined worker serves NodePorts across nodes and resolves cluster DNS in-Pod, and Pod traffic between nodes crosses a wireguard mesh. A worker runs as its own LaunchDaemon, reuses its stored node credential after a restart, and deregisters from the cluster on uninstall. Today that runs on a two-Mac setup; multi-node ships as experimental until the v0.3 release.
- The API is the upstream one. Your manifests, RBAC, CRDs, and controllers are the same
ones you use everywhere, because the control-plane binaries are the same ones.
Helm charts install through k3s’s
HelmChartandHelmChartConfigkinds, with the same fields under thehelm.k3sm.io/v1group. - No always-on guest. Native Pods are processes. Only a
vmPod adds a micro-VM, one per Pod, created when the Pod starts. - The unit of work is a process, not a macOS guest. A macOS guest is a whole operating system with its own memory and boot, so a host runs only a few at once. A node runs as many native Pods as its CPU and memory carry, and there is no hypervisor between the Pod and Metal or unified memory.
How to Choose#
- For a full Linux development environment on a Mac, or for amd64: a Linux VM tool. For
running a single arm64 Linux image inside a k3sm Pod, the
vmRuntimeClass does that directly. - For a production Kubernetes cluster: Linux Kubernetes, the platform the ecosystem builds for, certifies, and supports.
- For a fleet of Macs where every job must be a fresh macOS VM: a macOS VM orchestrator.
- For orchestrating work that must run on macOS (builds, Apple-platform tests, on-device inference) through the Kubernetes API, as processes on shared nodes rather than as guests: k3sm.
To try it: install.
Full detail: what runs for the workloads the default path takes, limitations for what does not, and the conformance profile for the formal self-assessment.