|
| 1 | +# Podman `libkrun`/`krunkit` Machine Provider (Opt-In) |
| 2 | + |
| 3 | +Tracks [Issue #409](https://github.com/aviadshiber/kapsis/issues/409). This document is the |
| 4 | +opt-in adoption guide referenced from `docs/STATUS-TRACKING.md`. |
| 5 | + |
| 6 | +## Background |
| 7 | + |
| 8 | +Kapsis's macOS mitigations for virtio-fs mount drops (sleep-prevention `caffeinate`, the |
| 9 | +vfkit watchdog, status-volume mirroring — see `CLAUDE.md`'s Mount Failure Detection section) |
| 10 | +exist because of a bug in Apple's Virtualization.framework (AVF) virtio-fs implementation, |
| 11 | +tracked upstream as Apple Feedback FB16008360. Podman's `libkrun` machine provider (hypervisor |
| 12 | +process `krunkit`) bypasses AVF entirely — it talks to `Hypervisor.framework` directly and |
| 13 | +implements its own virtio-fs server — so it structurally cannot hit that bug class. Podman |
| 14 | +made `libkrun` the **default** macOS provider in v6.0.0 (2026-06-24). |
| 15 | + |
| 16 | +**Kapsis does not default to `libkrun`.** As of this writing the switch is recent, there's an |
| 17 | +open filesystem-performance regression report against it |
| 18 | +([podman#29087](https://github.com/containers/podman/issues/29087)), and sleep/wake behavior |
| 19 | +under `libkrun` is publicly unvalidated. Kapsis's existing mitigation stack keeps working |
| 20 | +under either provider (see the table below) — nothing is removed for `libkrun` adopters. |
| 21 | + |
| 22 | +## Requirements |
| 23 | + |
| 24 | +- macOS 14+, **Apple Silicon only** (Intel Macs cannot use `libkrun`) |
| 25 | +- `krunkit` >= 1.3.1 (earlier versions had virtio-fs permission-semantics bugs, fixed through |
| 26 | + June 2026 in [libkrun#759](https://github.com/libkrun/libkrun/pull/759), |
| 27 | + [libkrun#734](https://github.com/libkrun/libkrun/pull/734)) |
| 28 | +- Podman >= 6.0.0 |
| 29 | + |
| 30 | +## Installing krunkit |
| 31 | + |
| 32 | +`brew install podman` does **not** bundle `krunkit`. Install it separately: |
| 33 | + |
| 34 | +```bash |
| 35 | +brew tap libkrun/krun |
| 36 | +brew install krunkit |
| 37 | +``` |
| 38 | + |
| 39 | +(The Podman GitHub `.pkg` installer bundles `krunkit`, so this step is only needed for |
| 40 | +Homebrew-installed Podman.) |
| 41 | + |
| 42 | +## Trying it |
| 43 | + |
| 44 | +There is no in-place conversion — a new machine is required, and Podman machines from |
| 45 | +different providers coexist (only one runs at a time): |
| 46 | + |
| 47 | +```bash |
| 48 | +podman machine stop # stop the current machine |
| 49 | +podman machine init --provider libkrun kapsis-libkrun |
| 50 | +podman machine start kapsis-libkrun |
| 51 | +``` |
| 52 | + |
| 53 | +**Cost of switching:** images and named volumes (including Kapsis's per-agent Maven/Gradle |
| 54 | +caches and `kapsis-*-status` volumes) live inside the machine's disk, not shared across |
| 55 | +machines. Expect to rebuild the Kapsis base image (`./scripts/build-image.sh`) and warm caches |
| 56 | +again on a new `libkrun` machine. |
| 57 | + |
| 58 | +**No Rosetta under `libkrun`** (Rosetta requires AVF). Kapsis builds arch-native arm64 images, |
| 59 | +so this has low impact — it would only matter if you deliberately run amd64-only images. |
| 60 | + |
| 61 | +## Verifying detection |
| 62 | + |
| 63 | +Kapsis detects the active provider at launch (macOS + Podman backend only) and records it in |
| 64 | +`status.json` as `machine_provider` (see `docs/STATUS-TRACKING.md`): |
| 65 | + |
| 66 | +```bash |
| 67 | +./scripts/launch-agent.sh ~/project --agent claude --task "..." & |
| 68 | +./scripts/kapsis-status.sh --json | grep machine_provider |
| 69 | +``` |
| 70 | + |
| 71 | +You can also query it directly: |
| 72 | + |
| 73 | +```bash |
| 74 | +podman machine inspect kapsis-libkrun --format '{{.VMType}}' |
| 75 | +``` |
| 76 | + |
| 77 | +## Mitigation status under `libkrun` |
| 78 | + |
| 79 | +| Mitigation | Under `libkrun` | Status | |
| 80 | +|---|---|---| |
| 81 | +| vfkit/krunkit watchdog (Issue #303) | Provider-agnostic — matches both `vfkit` and `krunkit` process names | Active | |
| 82 | +| Sleep prevention (`caffeinate`, Issue #276) | Root-cause trigger (AVF) doesn't apply, but unvalidated across sleep/wake under `libkrun` | Active (not yet a retirement candidate) | |
| 83 | +| Status volume mirroring (Issue #276) | Exists solely for AVF bind-mount drops | Active (not yet a retirement candidate) | |
| 84 | +| Pre-launch/entrypoint/liveness mount probes | Provider-agnostic | Active | |
| 85 | + |
| 86 | +None of these are gated on provider today — do not assume a `libkrun` host is exempt from a |
| 87 | +mount-failure exit code (4) until there is field evidence, not just theory, that it can't |
| 88 | +recur under `libkrun`. |
| 89 | + |
| 90 | +## Known open issues (tracked upstream, not by Kapsis) |
| 91 | + |
| 92 | +- FS-heavy workload performance regression after upgrading to Podman 6.0's `libkrun` default: |
| 93 | + [podman#29087](https://github.com/containers/podman/issues/29087) |
| 94 | +- Only one Podman machine runs at a time, complicating A/B testing: |
| 95 | + [podman#26281](https://github.com/containers/podman/issues/26281) |
| 96 | + |
| 97 | +## Recommendation |
| 98 | + |
| 99 | +Treat this as an **opt-in experiment**, not a default. If you try it, watch for the same |
| 100 | +symptoms the existing mitigations were built for (spurious `EACCES`/`ENOENT` under load, mount |
| 101 | +drops after sleep/wake) and report back on Issue #409 — a multi-week clean run is the bar for |
| 102 | +Kapsis to consider retiring any `applehv`-specific mitigation or recommending `libkrun` as the |
| 103 | +default on eligible hosts. |
0 commit comments