You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
On runners where /mnt is not a separate block device (it's just a directory on the root filesystem), the default holster disk reclamation leaves almost no free space on the root disk. Any step that runs after this action and writes to the root disk can then fail with ENOSPC: no space left on device, even though the Nix store has plenty of room.
This surfaced in mdarocha/mdarocha.pl (the deploy workflow) and mdarocha/robo-intern-v2 when Dependabot bumped this action from v1.2.0 → v1.3.0 (v1.3.0 introduced the nothing-but-nix step).
Observed failure
In the mdarocha.pl deploy, devenv test passed (it lives in /nix), then pulumi/esc-action tried to install the Pulumi CLI (pulumi-v3.253.0-linux-x64.tar.gz, ~120 MB compressed) into ~/.pulumi/bin and died:
ENOSPC: no space left on device, copyfile 'pulumi-.../pulumi-language-nodejs' -> '/home/runner/.pulumi/bin/pulumi-language-nodejs'
There is no separate /mnt mount in df — /mnt is a directory on /dev/root, and the /nix image (/mnt/disk0.img) physically consumes root's free space.
Run history confirms it's tied to the version bump, not the runners or Nix: every run on main and other branches kept passing on v1.2.0 during the same window; only the v1.3.0 branch failed, and on every retry (run_attempt 2 and 3).
Root cause (in nothing-but-nix @ v10)
holster is level 0. At level 0 the only step that runs is "The Volume", which sizes the /nix image from /mnt:
So it reserves only mnt-safe-haven (default 1024 MB) on whatever device backs /mnt, and gives everything else to /nix.
The root-safe-haven reserve (default 2048 MB) that would protect / is only applied inside add_expansion_disk(), which runs at level ≥ 1 (carve) or after purge at level ≥ 2. In holster (level 0) it is never called.
Consequences on a runner where /mnt lives on /:
df --output=avail /mnt returns the root disk's free space.
"The Volume" carves the /nix image out of that shared pool, leaving only mnt-safe-haven (~1 GB) free on the real root disk.
root-safe-haven never applies in holster, so nothing else protects /.
The build's own writes to root (workspace, caches) plus the Pulumi install exhaust that ~1 GB → ENOSPC.
The underlying assumption in nothing-but-nix "The Volume" is that / and /mnt are independent devices with independent free space. When /mnt is a path on /, that assumption breaks and the reserves don't protect the real device.
Options
Fix upstream (root cause): get nothing-but-nix "The Volume" to detect when /mnt shares the root device (e.g. compare stat -f -c %i / vs /mnt, or findmnt) and reserve against the true backing device / account for a combined safe haven. This fixes it for everyone regardless of protocol.
Workaround in this action (holster path only): raise the reserve that actually governs holster — mnt-safe-haven — when calling nothing-but-nix, so "The Volume" leaves more real headroom on the shared disk. Note: bumping root-safe-haven does nothing in holster (only affects rampage). Downside: on a normal runner with a separate /mnt, a larger mnt-safe-haven just leaves that space unused on the temp disk and gives /nix correspondingly less — a cost for no benefit there.
Per-repo escape hatch (already used): setting free-up-all-storage: true (rampage) works today because rampage purges preinstalled toolchains and runs add_expansion_disk, freeing real root space — but it's a heavy hammer (deletes toolchains) for what is really "leave more room free."
Notes
Affected runners: whatever provisions these jobs has no separate /mnt disk (standard GitHub-hosted ubuntu-latest normally mounts a separate /dev/sdb1 at /mnt; these did not).
Summary
On runners where
/mntis not a separate block device (it's just a directory on the root filesystem), the defaultholsterdisk reclamation leaves almost no free space on the root disk. Any step that runs after this action and writes to the root disk can then fail withENOSPC: no space left on device, even though the Nix store has plenty of room.This surfaced in
mdarocha/mdarocha.pl(thedeployworkflow) andmdarocha/robo-intern-v2when Dependabot bumped this action from v1.2.0 → v1.3.0 (v1.3.0 introduced thenothing-but-nixstep).Observed failure
In the
mdarocha.pldeploy,devenv testpassed (it lives in/nix), thenpulumi/esc-actiontried to install the Pulumi CLI (pulumi-v3.253.0-linux-x64.tar.gz, ~120 MB compressed) into~/.pulumi/binand died:dfat that point:There is no separate
/mntmount indf—/mntis a directory on/dev/root, and the/niximage (/mnt/disk0.img) physically consumes root's free space.Run history confirms it's tied to the version bump, not the runners or Nix: every run on
mainand other branches kept passing on v1.2.0 during the same window; only the v1.3.0 branch failed, and on every retry (run_attempt2 and 3).Root cause (in nothing-but-nix @ v10)
holsteris level 0. At level 0 the only step that runs is "The Volume", which sizes the/niximage from/mnt:So it reserves only
mnt-safe-haven(default 1024 MB) on whatever device backs/mnt, and gives everything else to/nix.The
root-safe-havenreserve (default 2048 MB) that would protect/is only applied insideadd_expansion_disk(), which runs at level ≥ 1 (carve) or after purge at level ≥ 2. In holster (level 0) it is never called.Consequences on a runner where
/mntlives on/:df --output=avail /mntreturns the root disk's free space./niximage out of that shared pool, leaving onlymnt-safe-haven(~1 GB) free on the real root disk.root-safe-havennever applies in holster, so nothing else protects/.The underlying assumption in
nothing-but-nix"The Volume" is that/and/mntare independent devices with independent free space. When/mntis a path on/, that assumption breaks and the reserves don't protect the real device.Options
Fix upstream (root cause): get
nothing-but-nix"The Volume" to detect when/mntshares the root device (e.g. comparestat -f -c %i /vs/mnt, orfindmnt) and reserve against the true backing device / account for a combined safe haven. This fixes it for everyone regardless of protocol.Workaround in this action (holster path only): raise the reserve that actually governs holster —
mnt-safe-haven— when callingnothing-but-nix, so "The Volume" leaves more real headroom on the shared disk. Note: bumpingroot-safe-havendoes nothing in holster (only affectsrampage). Downside: on a normal runner with a separate/mnt, a largermnt-safe-havenjust leaves that space unused on the temp disk and gives/nixcorrespondingly less — a cost for no benefit there.Per-repo escape hatch (already used): setting
free-up-all-storage: true(rampage) works today because rampage purges preinstalled toolchains and runsadd_expansion_disk, freeing real root space — but it's a heavy hammer (deletes toolchains) for what is really "leave more room free."Notes
/mntdisk (standard GitHub-hostedubuntu-latestnormally mounts a separate/dev/sdb1at/mnt; these did not).nothing-but-nix@687c797a730352432950c707ab493fcc951818d7(v10), steps "The Volume" and "The Purge" /expand_nix_volume.sh.