LEARN MORE
Memory and sleep
Kiri’s Linux VM takes memory as your containers need it and hands the rest back to macOS while it runs. When nothing has run for a minute, it powers off and holds no memory at all. The next request that needs it boots it again in about 0.15 s, so you rarely notice.
The VM’s limits
The VM boots with a memory ceiling and a number of CPUs. These are the most it may use, not what it takes.
| Default | Change it | |
|---|---|---|
| Memory ceiling | half the Mac’s RAM, at least 2 GB and at most 8 GB | Settings → Resources, from 1 GB up to the Mac’s RAM |
| CPUs | 2 | Settings → Resources, from 1 up to the Mac’s CPUs |
Settings saves these to ~/.kiri/vm.json and they apply from the VM’s next start. If the VM is running, Settings offers Restart VM to apply, which stops the running containers. The environment variables KIRI_MEMORY_MIB and KIRI_CPUS, set for the daemon, take precedence over Settings.
The CPUs are shared with macOS. The VM only uses them while containers are busy; the daemon itself uses no CPU when idle.
The balloon gives memory back
Inside the ceiling, a memory balloon keeps the VM close to what it actually uses. Every second the daemon asks the agent how much memory the guest holds, counting anonymous, shared and kernel memory but not the page cache, which Linux can drop when asked. It then sets the balloon’s target to that plus 256 MB of headroom, never below 256 MB, and macOS gets the rest.
- Shrinking is gradual: at most 256 MB per second, so the guest’s caches drain slowly instead of all at once.
- Growing is immediate. When the guest is short of memory (the kernel reports memory stalls through a PSI trigger), the agent tells the daemon, which grows the target by 1 GB at once, up to the ceiling.
- Changes under 64 MB are skipped, so the target doesn’t jitter.
The guest kernel also compresses idle pages with zswap, and there is no swap for containers: a --memory limit is a hard limit.
The window’s status bar shows the VM’s memory: what’s in use, and how much the balloon lets it take right now.
Sleeping when idle
The balloon only goes so far: powering the VM off is the one thing that gives all of its memory back to macOS, and a VM that boots in about a tenth of a second can afford to. So the daemon checks every 5 seconds, and puts the VM to sleep once all of these are true for 60 seconds:
- no container is running,
- no
execis running, - no API request has come in.
The window’s own status checks don’t count as requests, so leaving the window open doesn’t keep the VM awake. A running container does: a docker run -d nginx keeps the VM up until you stop it.
The daemon keeps the container records on the Mac, so docker ps -a still lists everything while the VM is asleep. The menu bar and the window show the VM as Asleep. Stop in the window stops every container and puts the VM to sleep at once.
Builds
BuildKit, which runs in a Kiri container for docker build, would otherwise keep the VM awake for good. When BuildKit containers are the only thing running and no build has run for 5 minutes, the daemon stops them; the next build starts them again.
Changing the timing
| Variable | Default | What it does |
|---|---|---|
KIRI_IDLE_SECS |
60 |
Seconds idle before the VM sleeps. 0 never sleeps. |
KIRI_BUILDER_IDLE_SECS |
300 |
Seconds without a build before idle BuildKit containers stop. |
These are read by the daemon when it starts, so set them in the daemon’s environment.
What Kiri itself costs
The daemon on the Mac stays at about 15 MB and measured 0.0 % CPU when idle. Measured on one Mac, the daemon’s footprint was 9.0 MB and the agent inside the VM held 0.69 MB. There is no process per container, on the Mac or in the VM. While the VM is awake, Apple’s Virtualization helper process adds about 69 MB on top of what the guest uses. See Benchmarks for every figure.
Limits for one container
Docker’s limit flags set the container’s cgroup in the VM, and docker update changes them while it runs.
| Flag | What it sets |
|---|---|
--memory 512m |
memory.max, with swap off: a hard limit |
--cpus 1.5 |
cpu.max: 150 ms of CPU time every 100 ms |
--cpu-shares 512 |
cpu.weight, converted from shares as runc does |
--pids-limit 100 |
pids.max |
A container can’t use more than the VM has, so a --memory above the ceiling has no extra effect.
© 2026 Kiri · runkiri.com