{"id":51380837,"url":"https://github.com/saiyam1814/kiac","last_synced_at":"2026-07-03T16:04:55.519Z","repository":{"id":364108171,"uuid":"1265379854","full_name":"saiyam1814/kiac","owner":"saiyam1814","description":"Local Kubernetes on Apple's container framework - every node is its own lightweight VM. Metrics, storage, and LoadBalancer included.","archived":false,"fork":false,"pushed_at":"2026-06-11T16:18:32.000Z","size":313,"stargazers_count":13,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-06-11T18:11:22.861Z","etag":null,"topics":["apple-silicon","containers","kubernetes","macos"],"latest_commit_sha":null,"homepage":"https://saiyam1814.github.io/kiac/","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/saiyam1814.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-10T18:10:36.000Z","updated_at":"2026-06-11T17:30:19.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/saiyam1814/kiac","commit_stats":null,"previous_names":["saiyam1814/kiac"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/saiyam1814/kiac","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saiyam1814%2Fkiac","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saiyam1814%2Fkiac/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saiyam1814%2Fkiac/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saiyam1814%2Fkiac/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/saiyam1814","download_url":"https://codeload.github.com/saiyam1814/kiac/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saiyam1814%2Fkiac/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35092229,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-03T02:00:05.635Z","response_time":110,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["apple-silicon","containers","kubernetes","macos"],"created_at":"2026-07-03T16:04:54.948Z","updated_at":"2026-07-03T16:04:55.508Z","avatar_url":"https://github.com/saiyam1814.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"assets/banner.png\" alt=\"kiac - Kubernetes in Apple Containers\" width=\"100%\"\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cb\u003eLocal Kubernetes clusters where every node is its own lightweight VM.\u003c/b\u003e\u003cbr\u003e\n  Native on Apple silicon, powered by \u003ca href=\"https://github.com/apple/container\"\u003eapple/container\u003c/a\u003e. No Docker Desktop. No Lima. No QEMU.\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/saiyam1814/kiac/releases\"\u003e\u003cimg src=\"https://img.shields.io/github/v/release/saiyam1814/kiac?color=326CE5\u0026label=release\" alt=\"release\"\u003e\u003c/a\u003e\n  \u003ca href=\"LICENSE\"\u003e\u003cimg src=\"https://img.shields.io/badge/license-MIT-326CE5\" alt=\"MIT\"\u003e\u003c/a\u003e\n  \u003cimg src=\"https://img.shields.io/badge/platform-Apple%20silicon-555\" alt=\"Apple silicon\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/Kubernetes-1.32–1.36-326CE5\" alt=\"Kubernetes 1.32-1.36\"\u003e\n  \u003ca href=\"https://saiyam1814.github.io/kiac/\"\u003e\u003cimg src=\"https://img.shields.io/badge/website-kiac-326CE5\" alt=\"website\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"assets/kiac-demo.gif\" alt=\"kiac creating a 3-node cluster\" width=\"80%\"\u003e\n\u003c/p\u003e\n\n```bash\nbrew install saiyam1814/tap/kiac\nkiac create cluster --workers 2\n```\n\n---\n\n## Why this matters\n\nRunning a local Kubernetes cluster on a Mac has always meant a quiet compromise. Your \"nodes\" were containers sharing one kernel inside one hidden Linux VM, all pretending to be separate machines. It worked until you tried to test a node failure, or `kubectl top`, or a `type: LoadBalancer` service, and the illusion cracked.\n\nA Kubernetes node wants to *be* a machine: its own kernel, its own kubelet, its own cgroups, its own IP that can come and go on its own. **kiac gives every node exactly that** by booting each one as its own lightweight virtual machine on Apple's native runtime. The result is a local cluster that behaves like a real one, created with a single command in a couple of minutes.\n\n## Why Apple containers\n\nWhen Apple shipped `container` 1.0, most people read it as \"Docker, but from Apple.\" It is something more interesting underneath: **every container is its own lightweight virtual machine.**\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"assets/apple-container-anatomy.png\" alt=\"How one Apple container works\" width=\"92%\"\u003e\n\u003c/p\u003e\n\nThe [Containerization](https://github.com/apple/containerization) framework boots a separate, minimal Linux VM for each container on Apple's `Virtualization.framework`:\n\n- **The image becomes a disk.** The OCI image is turned into an EXT4 filesystem and handed to the VM as its root block device. No overlay mount layered on a shared host kernel.\n- **A dedicated kernel boots.** Each container gets its own minimal, optimized Linux kernel. It is not shared with the host or any other container.\n- **`vminitd` is PID 1.** A tiny Swift init system comes up first, then launches and supervises your process. The host drives it through a gRPC API over `vsock`.\n- **virtio devices, direct networking.** No BIOS, no legacy device emulation, so the VM boots in about a second and gets its own IP you can reach from your Mac.\n\nYou get the developer experience of containers with the isolation boundary of a virtual machine. That combination is exactly what a Kubernetes node wants.\n\n## Why Kubernetes on Apple containers: real isolation\n\nWhen local Kubernetes tools run \"nodes\" as Docker containers, those nodes are processes sharing one Linux kernel, separated only by namespaces. Namespaces are a software boundary *inside* a single shared kernel. With kiac, the boundary between nodes is the **hypervisor** itself.\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"assets/isolation.png\" alt=\"Where the isolation boundary sits\" width=\"100%\"\u003e\n\u003c/p\u003e\n\nThat difference is not academic. It changes what the cluster can actually do:\n\n- **Blast radius.** A container escape that reaches the shared kernel reaches every node on it. With a VM per node, an escape is contained to one VM.\n- **Failure domains.** A shared kernel is a shared fate: one panic or runaway sysctl takes everything down together. With kiac, a kernel problem stays inside the VM that caused it.\n- **Real node failure.** Stop one node VM and it behaves like an actual node going offline: NotReady detection, eviction, rescheduling. You cannot meaningfully test that when \"stopping a node\" means killing one of several processes that share a kernel.\n- **Per-node kernel reality.** Each node has its own `/proc`, `/sys`, modules, and sysctls. Node-level behavior is real, not simulated.\n\nContainers are great for packaging software, and kiac depends on them. The point is narrower: when the workload you are isolating is itself a machine, a machine-grade boundary is the right tool.\n\n## Features\n\n- 🔒 **Hardware-grade isolation** — each node is one lightweight VM with its own kernel and cgroups, not namespaces sharing a daemon.\n- 📊 **Metrics out of the box** — `kubectl top nodes` works the moment the cluster is up. metrics-server ships preconfigured.\n- 💾 **PVCs that just bind** — a default StorageClass (local-path-provisioner) is installed on create, so StatefulSets and `volumeClaimTemplates` work immediately.\n- ⚖️ **`type: LoadBalancer` works** — MetalLB ships by default, pooled on node IPs, so Services get a real EXTERNAL-IP you can curl from your Mac. No `\u003cpending\u003e`, no tunnels.\n- 🌐 **Direct networking** — every node gets a routable IP on macOS 26+. Hit NodePorts directly, no port-mapping flags.\n- 🧱 **Multi-node, day one** — `--workers N` gives a real topology: scheduling, cross-node pod networking, node failures you can practice on.\n- 🖥️ **A console when you want one** — `kiac ui` opens a local web console to create, watch, and delete clusters, same engine as the CLI.\n- 🍎 **Native stack** — one Swift runtime from Apple, one Go binary from us. Coexists with Docker Desktop, kind, and k3d; never touches the Docker socket.\n\n## Quickstart\n\n### Requirements\n\n- An Apple silicon Mac\n- macOS 26+ for multi-node clusters (single-node works on macOS 15, with limitations)\n- [apple/container](https://github.com/apple/container/releases) 1.0.0+\n- `kubectl`\n\n### Install\n\n```bash\nbrew install saiyam1814/tap/kiac\n```\n\n\u003cdetails\u003e\n\u003csummary\u003eOther install methods\u003c/summary\u003e\n\n```bash\n# With Go\ngo install github.com/saiyam1814/kiac@latest\n\n# From source\ngit clone https://github.com/saiyam1814/kiac \u0026\u0026 cd kiac \u0026\u0026 make build\n```\n\u003c/details\u003e\n\n### Create your first cluster\n\n```bash\nkiac doctor                                  # check your setup\nkiac create cluster --name dev --workers 2   # 1 control plane + 2 workers\n```\n\n```text\n⬢ kiac v0.1.0 · Kubernetes in Apple Containers\n ✓ Preflight checks (0.3s)\n ✓ Pulling node image kindest/node:v1.36.1 (8.4s)\n ✓ Booting 3 node VM(s) (9.8s)\n ✓ Initializing Kubernetes control plane (49.6s)\n ✓ Joining 2 worker(s) (13.5s)\n ✓ Installing CNI (kindnet) (0.4s)\n ✓ Installing storage (local-path-provisioner) (0.5s)\n ✓ Installing metrics-server (0.4s)\n ✓ Installing LoadBalancer (MetalLB) (0.6s)\n ✓ Waiting for nodes to be Ready (10.7s)\n ✓ Configuring LoadBalancer IP pool (3.2s)\n ✓ Writing kubeconfig (0.2s)\n\nCluster \"dev\" is ready in 2m26s. Every node is its own lightweight VM.\n```\n\nThe kubeconfig is merged into `~/.kube/config` as context `kiac-dev` (your existing config is backed up to `~/.kube/config.kiac.bak` the first time).\n\n### See the isolation pay off\n\n```bash\n$ kubectl get nodes -o wide\nNAME                     STATUS   ROLES           VERSION   INTERNAL-IP    KERNEL-VERSION    CONTAINER-RUNTIME\nkiac-dev-control-plane   Ready    control-plane   v1.36.1   192.168.64.2   6.12.28 (arm64)   containerd://2.3.1\nkiac-dev-worker-1        Ready    \u003cnone\u003e          v1.36.1   192.168.64.3   6.12.28 (arm64)   containerd://2.3.1\nkiac-dev-worker-2        Ready    \u003cnone\u003e          v1.36.1   192.168.64.4   6.12.28 (arm64)   containerd://2.3.1\n\n$ kubectl top nodes\nNAME                     CPU(cores)   CPU(%)   MEMORY(bytes)   MEMORY(%)\nkiac-dev-control-plane   269m         5%       828Mi           20%\nkiac-dev-worker-1        35m          0%       288Mi           7%\nkiac-dev-worker-2        52m          1%       359Mi           9%\n\n$ kubectl expose deploy web --port=80 --type=LoadBalancer\n$ kubectl get svc web\nNAME   TYPE           EXTERNAL-IP    PORT(S)        AGE\nweb    LoadBalancer   192.168.64.3   80:30495/TCP   15s\n$ curl http://192.168.64.3       # HTTP 200, straight from your Mac\n```\n\n## Usage\n\n```bash\nkiac doctor                                  # check your setup\nkiac create cluster                          # single node, everything included\nkiac create cluster --name dev --workers 2   # 1 control plane + 2 workers\nkiac create cluster --k8s-version 1.34       # pick your Kubernetes (1.32-1.36 pinned)\nkiac ui                                      # local web console to create/manage clusters\nkiac get clusters\nkiac get nodes --name dev\ncontainer build -t myapp:dev .               # build with apple/container\nkiac load image myapp:dev --name dev         # push it into every node\nkiac delete cluster --name dev\n```\n\n### Flags for `create cluster`\n\n| Flag | Default | Description |\n|---|---|---|\n| `--name` | `dev` | cluster name |\n| `--workers` | `0` | worker count; control plane is untainted when 0 |\n| `--k8s-version` | `1.36` | Kubernetes minor, pinned digests for 1.32-1.36 |\n| `--image` | resolved from `--k8s-version` | explicit node image override |\n| `--cni` | `kindnet` | pod network: `kindnet` or `none` (Flannel/Calico/Cilium need kernel features missing from Apple's stock node kernel; custom kernels are on the roadmap) |\n| `--cpus` | `4` | vCPUs per node VM |\n| `--memory` | `4G` | memory per node VM |\n| `--no-metrics` | `false` | skip metrics-server |\n| `--no-storage` | `false` | skip the local-path default StorageClass |\n| `--no-lb` | `false` | skip MetalLB (`type: LoadBalancer` support) |\n| `--wait` | `5m` | node readiness timeout |\n\n## How it works\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"assets/architecture.png\" alt=\"How kiac builds a cluster\" width=\"100%\"\u003e\n\u003c/p\u003e\n\nkiac drives the `apple/container` CLI to boot one lightweight VM per node from the standard `kindest/node` image (systemd, containerd, kubeadm preinstalled), initializes the control plane with `kubeadm`, joins the workers over the `vmnet` network, applies the kindnet CNI, and installs metrics-server, local-path storage, and MetalLB by default. It talks only to the `apple/container` runtime and never touches the Docker socket, so it coexists with Docker Desktop, Rancher Desktop, kind, and k3d.\n\n## Roadmap\n\n- **Node chaos** — `kiac stop|start node` to test real NotReady detection, eviction, and rescheduling\n- **Custom node kernels** (`--kernel`) to unlock Flannel, Calico, Cilium, and eBPF\n- **Persistent clusters** backed by `container machine` (WWDC26 persistent Linux environments), so a cluster survives a reboot\n- **Built-in LoadBalancer controller** to replace MetalLB (node-IP allocation needs no ARP speaker)\n- **HA control planes** and an ingress helper\n\n## Contributing\n\nIssues and PRs are welcome. New here? Look for issues labeled [`good first issue`](https://github.com/saiyam1814/kiac/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) — they are scoped with context and acceptance criteria to be a clean first contribution.\n\n## Credits\n\nkiac stands on other people's work: the [`apple/container`](https://github.com/apple/container) and [Containerization](https://github.com/apple/containerization) teams at Apple built the runtime; Akihiro Suda's [`kina`](https://github.com/AkihiroSuda/kina) proved Kubernetes on `apple/container` was viable; and the node experience reuses the [`kindest/node`](https://github.com/kubernetes-sigs/kind) image from the kind project.\n\n## License\n\n[MIT](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsaiyam1814%2Fkiac","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsaiyam1814%2Fkiac","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsaiyam1814%2Fkiac/lists"}