docs(build): correct OOM-daemon guidance and add global cargo/systemd knobs

systemd-oomd is desktop-only on Ubuntu (reverse-deps are ubuntu-desktop*);
'systemctl is-enabled systemd-oomd' returns not-found on this server, so
the previous advice to enable it would simply have failed. Recommend
earlyoom instead, with --avoid/--prefer tuned to protect the shell and
target rustc.

Also document the machine-wide alternatives to per-project tuning:
- sudo systemctl set-property user-1000.slice MemoryHigh=/MemoryMax=
  (covers every shell and build; sshd lives in system.slice so it can
  never lock you out)
- global ~/.cargo/config.toml, which does support [profile.*]

Clarify that the MemoryHigh warning applies to short-lived build scopes,
not to slice-level limits where gradual throttling is appropriate.
This commit is contained in:
2026-08-07 00:28:03 +00:00
parent a728c758a7
commit e2eb50eb85
+40 -12
View File
@@ -110,16 +110,43 @@ The machine-level protections below need no per-project changes.
```bash ```bash
# Reserve memory for the SSH daemon so it can't be swapped out entirely. # Reserve memory for the SSH daemon so it can't be swapped out entirely.
sudo systemctl edit ssh # [Service] / MemoryMin=128M sudo systemctl edit ssh # [Service] / MemoryMin=128M
# Kill on memory *pressure* rather than waiting for true OOM. This reacts
# during thrashing — exactly the window where the box currently becomes
# unreachable. Ubuntu ships it but leaves it inactive.
sudo systemctl enable --now systemd-oomd
``` ```
`earlyoom` is the lighter-weight alternative (`sudo apt install earlyoom`); **Do not bother with `systemd-oomd` on this box** — it is a separate
it kills only the single highest-scoring process, which during a build is package whose only reverse-dependencies are `ubuntu-desktop*`, so Ubuntu
`rustc` itself. Server never installs it (`systemctl is-enabled systemd-oomd``not-found`
here). Use **earlyoom** instead, which is in `universe` and kills only the
single highest-scoring process rather than a whole cgroup:
```bash
sudo apt install earlyoom
# /etc/default/earlyoom
EARLYOOM_ARGS="-m 8 -s 5 -r 60 \
--avoid '(^|/)(systemd|sshd|mosh-server|tmux.*|bash|fish)$' \
--prefer '(^|/)(rustc|cargo|ld|lld|collect2)$'"
```
`--avoid` protects your shell; `--prefer` points it at the compiler. Fedora
enabled earlyoom by default for exactly this "system becomes completely
unresponsive, user has no choice but to force power off" scenario.
### Cap every build at once, forever (needs root once)
This is the global knob — no wrapper script, no per-project config:
```bash
sudo systemctl set-property user-1000.slice MemoryHigh=5G MemoryMax=6500M
```
Applies to every shell and every build you start, persists across reboots.
Because `sshd` itself lives in `system.slice`, capping the user slice can
never lock you out of a new login. (Ubuntu already ships `TasksMax=33%`
this way via `/usr/lib/systemd/system/user-.slice.d/`, so it's a
distro-blessed pattern.)
Note `MemoryHigh` at the *slice* level is reasonable — it throttles a
sprawling session gradually. Do not put it on a single short-lived build
scope, where it causes the reclaim-stall described below.
### Cap one build ad-hoc (no root) ### Cap one build ad-hoc (no root)
@@ -136,10 +163,11 @@ OOM — it was *thrashing*. With swap available the kernel pages `sshd` out
to feed the build and the box goes catatonic while `oom_kill` stays at 0. to feed the build and the box goes catatonic while `oom_kill` stays at 0.
Denying the build swap turns a slow total failure into a fast contained one. Denying the build swap turns a slow total failure into a fast contained one.
**Do not add `MemoryHigh`.** It sounds safer but traps the process in **Do not add `MemoryHigh` to a single build scope.** It sounds safer but
continuous reclaim so it grinds forever instead of dying. Measured against traps the process in continuous reclaim so it grinds forever instead of
an identical 256 MB ceiling: `MemoryMax` alone → clean kill in seconds; dying. Measured against an identical 256 MB ceiling: `MemoryMax` alone →
`MemoryMax` + `MemoryHigh` → still spinning after 45 seconds. clean kill in seconds; `MemoryMax` + `MemoryHigh` → still spinning after 45
seconds.
### Diagnosing which crate is expensive ### Diagnosing which crate is expensive