Tutorials
A ladder from your first native Pod to the things k3sm will not do, with any unfinished step marked in progress or planned.
Twelve short walk-throughs, in order. The first six cover the single-node basics: install a node, run a Pod, front it with a Service, give it storage, probe it, and give it stable identity. The next five cover paths that ship with known limits (a second Mac, Linux images in a VM, building and loading images, MLX serving), and each one says where it stops. The last one runs a Linux-only binary on the native path, which fails, and then explains the reason and the workaround.
Every command on these pages works unless it is marked otherwise. A capability that is not finished carries a status badge, in progress or planned, and is written in the future tense.
Before You Start#
- k3sm needs Apple Silicon and macOS 26 or newer. It runs Pods as native arm64 Darwin processes. There is no x86_64 Mac path and no VM in the default path.
- Releases are published, and the install script installs the newest one. They are pre-release builds, ad-hoc signed and not notarized. The first tutorial installs a node, and the rest of each tutorial assumes one.
- Read limitations early. It is the inventory of what diverges and why.
Ladder#
| tutorial | what you end up with | |
|---|---|---|
| 1 | first pod | a node, a Pod, and that Pod visible in ps as a real process |
| 2 | deployments and services | a Deployment behind a ClusterIP you can curl |
| 3 | nodeport | that Service reachable from the Mac itself |
| 4 | storage | a PVC bound to a local-path PV that survives the Pod |
| 5 | probes | liveness and readiness driving restarts and endpoints |
| 6 | statefulset | stable per-replica identity and a headless Service |
| 7 | two macs | a second Mac joined over the wireguard mesh, on an experimental path |
| 8 | linux workloads and the vm runtimeclass | a linux/arm64 image booted in its own micro-VM, and where the path still stops |
| 9 | building an image | an OCI image built from a COPY-only Dockerfile |
| 10 | loading an image | that image recorded in the node’s image store |
| 11 | serving a model with mlx | how MLX serving works, and what your own Mac needs to try it |
| 12 | what will not work | a Linux binary failing, explained, with the documented way out |
Manifests#
Each tutorial shows its manifest inline. The same manifests ship in the k3sm repository under
examples/, with longer commentary in the
comments:
hello-native.yaml, deployment-native.yaml, service-clusterip.yaml, service-nodeport.yaml,
probes.yaml, statefulset-local-path.yaml.
Two Fields Every Pod Needs#
They appear in every manifest on every page, and a Pod without them does not run:
nodeSelector: kubernetes.io/os: darwin. A validating admission policy rejects any Pod that does not declare it, sokubectl applyfails and nothing is created.- a toleration for the
k3sm.io/provider:NoScheduletaint. Every k3sm node carries it. Without a matching toleration the Pod is admitted and then sitsPendingforever. Only DaemonSet-owned Pods get it injected, so a Deployment, StatefulSet, or Job template has to carry it itself.
Keep the toleration keyed on k3sm.io/provider rather than a blanket operator: Exists. A blanket
toleration also tolerates node.kubernetes.io/not-ready, which keeps a Pod bound to a node that has
stopped working.
Your first native pod
Install k3sm, start a node, run a Pod, and find that Pod in ps as an ordinary macOS process.
Deployments and services
Run a native workload under a Deployment, front it with a ClusterIP Service, and reach it through the userspace proxy.
Reaching a service from the host
Publish a workload on a NodePort and reach it from the Mac itself, and understand which interfaces that port answers on.
Storage that survives the pod
Claim a local-path PersistentVolume, see the node affinity it carries, and watch the data outlive the Pod that wrote it.
Liveness and readiness
Attach probes to a native Pod and watch readiness gate Service endpoints while liveness restarts the container.
Statefulsets and stable identity
Give a workload a stable name, its own per-replica volume, and a headless Service, and see exactly which parts of that identity resolve today.
Joining a second Mac
Mint a join token, bring up a second Mac as an agent over the wireguard mesh, and see two nodes serving one cluster: cross-node NodePort, PVC persistence, and in-pod kubectl and …
The vm RuntimeClass
Opt a Pod into a Virtualization.framework isolation boundary, check the node capability labels behind it, and see exactly where the Linux-image path stops today.
Building an image
Package a native darwin/arm64 binary into an OCI image: recorded in the node’s image store, and exportable as a portable artifact for registries and any other OCI tool.
Loading an image into a node
Ingest a docker-save tarball or an OCI layout into a node’s image store, see it recorded, and run a Pod from it with no registry in the path.
Serving a model with MLX
The MLXModel resource and the mlx.k3sm.io/gpu extended resource are shipped. Here is what serving a model through Kubernetes looks like on k3sm today, and what still has to be true …
What will not work, and why
Watch a Linux binary fail to start as a k3sm Pod, see exactly why it happens, and take the documented way out.