Linux containers that give memory back to your Mac.

A runtime plugin for Apple Container that boots Apple’s own Linux guest with libkrun instead of Virtualization.framework. When the guest frees memory, macOS can reclaim it, and the container keeps running.

Illustration: the guest allocates pages, then frees them. Free-page reporting hands those pages back to the host while the VM stays up.

How freed memory gets back to macOS

Apple’s stock Container kernel negotiates VIRTIO_BALLOON_F_REPORTING with libkrun. Nothing in apple/container or apple/containerization changes; the plugin uses Apple Container’s public runtime plugin contract.

  1. The guest frees pages

    A process inside the container exits or releases memory, and the Linux kernel returns those pages to its free lists.

  2. The kernel reports them

    The virtio-balloon driver batches free pages and reports them to the VMM, without any manual balloon inflation.

  3. libkrun releases the backing

    libkrun marks the host memory behind those pages with MADV_FREE.

  4. macOS reclaims it

    Under memory pressure, macOS takes the pages back for other apps while the container keeps running.

How it compares with Apple’s runtime

Network throughput matches or beats Apple’s runtime. File copies and volume writes are faster. Stopping a container takes about 23 ms longer.

Network throughput

krun throughput divided by Apple’s, so above 1.0× means krun is faster. Each run pairs the two runtimes on the same Mac with 8 parallel TCP streams for 20 seconds, repeated 3 times. BBR and Reno are the guest’s TCP congestion control.
Directionkrun ÷ AppleResult
Guest to host, BBR 11–17% faster1.11× median, earlier 1.17×
Guest to host, Reno Same speed1.01× median, earlier 0.97×
Host to guest Same speed1.00× median, earlier 0.98×

Container lifecycle and file I/O

Median of 5 paired samples in milliseconds, lower is better. Both runtimes ran on the same Mac with a warmed image and networking off, in alternating order so host drift affects both equally. Reproduce with scripts/benchmark_runtimes.sh.
OperationMedian timekrun vs Apple
Start a container
759 ms
739 ms
3% fasterwithin noise
Stop a container
86 ms
109 ms
27% slower+23 ms
Delete a container
115 ms
104 ms
10% faster
exec a command
74 ms
78 ms
5% slowerwithin noise
CPU workload
539 ms
529 ms
2% faster
copyIn
95 ms
81 ms
15% faster
copyOut
88 ms
75 ms
15% faster
Write to a volume
120 ms
107 ms
11% faster

Works with the container commands you already use

Select the runtime with --runtime container-krun-runtime. Apple Container still manages images, networks, and volumes.

Lifecycle

  • run, exec, stop, signals, waitInteractive stdio and terminal resize
  • --initSignal forwarding and zombie reaping
  • container logsInit output plus the VM boot log
  • Container statistics
  • Live rootfs export

Networking

  • Native vmnet on macOS 26Works with no setup through a managed krun network
  • Multiple networksExtra attachments become eth1, eth2, …
  • Published TCP and UDP portsThrough Apple’s SocketForwarder
  • Published Unix socketsWorks with --network none
  • SSH agent forwarding with --ssh

Storage

  • Named and anonymous volumesAttached as virtio-blk devices
  • Host directory mountsvirtio-fs, read-write or read-only
  • copyIn and copyOut
  • container cleanTrims the rootfs and block-backed mounts

Not supported

  • Rosetta. Use Apple’s default runtime for x86_64 images.
  • Nested virtualization.

Unsupported requests fail with a clear error instead of silently falling back.

Install

Install it next to Apple’s runtime and opt in per container. The project is experimental.

You need

  • An Apple Silicon Mac on macOS 26 or later
  • Apple Container 1.5.0 or later
  • Administrator approval, once, for the network helper

Networking needs a root-owned copy of the VM helper and a sudo rule that lets only you run it. container-krun-runtime-setup installs both and asks for your password for that step only. Pass --no-network to skip it; containers started with --network none still work.

  1. Install the package

    brew install RobertDeRose/tap/container-krun-runtime
  2. Register it with container

    container-krun-runtime-setup
    container system stop && container system start
  3. Run a container

    container run --rm \
      --runtime container-krun-runtime \
      alpine:3.20 echo hello-from-libkrun

Run container-krun-runtime-setup again after brew upgrade changes either package.