[Infra][SERVER] Root disk hit 99% from accumulated stale deploy images — pruned 222.7GB (now 49%); add scheduled prune on service nodes + investigate OOM kills
#757
Found during 2026-07-21 swarm recon; the CRITICAL disk alerts were never delivered because ops email is dead (#756).
What happened
SERVER / climbed 93% (07-20 14:21Z) → 99% (07-21 02:22Z), ~1%/hour, with 5.1G free of 436.5G at 02:5xZ.
docker system df on SERVER: 680 images totalling 276.5GB, only 91 in use — 216GB (78%) reclaimable. overlay2 was 262.3GB. Cause: every CI deploy pulls a new :latest digest and the superseded images accumulate as dangling; SERVER hosts the most services so it accumulates fastest.
Remediation done (2026-07-21 ~03:0xZ)
Ran the standard conservative cleanup (one-shot swarm job pinned to SERVER):
docker container prune -f → 116.3MB
docker image prune -f (dangling only — cannot touch in-use or tagged images) → 222.7GB reclaimed
After: / at 49% (214.6G free).
Follow-ups needed
Scheduled prune on service nodes (SERVER at minimum, ideally all nodes): a cron/swarm-cronjob running docker image prune -f (dangling) weekly, or docker image prune -af --filter until=168h for a deeper clean. Same class of problem as #514 (build-node GITEA-ACTION volumes) but on the service nodes' image cache — #514's fix does not cover this.
OOM kills on SERVER — HIGH: OOM kill on SERVER alerts (undelivered) at 20:32, 22:58, 00:27, 02:24Z on 07-20/21. Which process is being killed and whether it correlates with the fleet redeploy memory footprint needs a look (journalctl -k | grep -i oom on SERVER).
Kernel errors on dreamstream1 at 16:42, 20:50, 22:08, 01:31Z — also unseen due to #756; needs journalctl -k triage.
Found during 2026-07-21 swarm recon; the CRITICAL disk alerts were never delivered because ops email is dead (#756).
## What happened
- SERVER `/` climbed 93% (07-20 14:21Z) → 99% (07-21 02:22Z), ~1%/hour, with 5.1G free of 436.5G at 02:5xZ.
- `docker system df` on SERVER: **680 images totalling 276.5GB, only 91 in use — 216GB (78%) reclaimable**. overlay2 was 262.3GB. Cause: every CI deploy pulls a new `:latest` digest and the superseded images accumulate as dangling; SERVER hosts the most services so it accumulates fastest.
## Remediation done (2026-07-21 ~03:0xZ)
Ran the standard conservative cleanup (one-shot swarm job pinned to SERVER):
- `docker container prune -f` → 116.3MB
- `docker image prune -f` (dangling only — cannot touch in-use or tagged images) → **222.7GB reclaimed**
- After: `/` at **49%** (214.6G free).
## Follow-ups needed
1. **Scheduled prune on service nodes** (SERVER at minimum, ideally all nodes): a cron/swarm-cronjob running `docker image prune -f` (dangling) weekly, or `docker image prune -af --filter until=168h` for a deeper clean. Same class of problem as #514 (build-node GITEA-ACTION volumes) but on the service nodes' image cache — #514's fix does not cover this.
2. **OOM kills on SERVER** — `HIGH: OOM kill on SERVER` alerts (undelivered) at 20:32, 22:58, 00:27, 02:24Z on 07-20/21. Which process is being killed and whether it correlates with the fleet redeploy memory footprint needs a look (`journalctl -k | grep -i oom` on SERVER).
3. **Kernel errors on dreamstream1** at 16:42, 20:50, 22:08, 01:31Z — also unseen due to #756; needs `journalctl -k` triage.
Audited against origin/master — item 1 NOT DONE; items 2–3 unverifiable from git. Staying open.
First, confirming these are genuinely two different tickets, because the overlap with #514 is easy to assume away. The only prune script in the repo is gitea-act-runner/cleanup/gitea-actions-prune.sh (image prune at :74) — that lives under gitea-act-runner/ and is the build/runner-node cleanup #514 covers. This ticket is about service nodes, and git grep -rn "image prune\|system prune\|container prune" origin/master returns only those lines in that one runner script. No cron unit, no swarm-cronjob, no until=168h deep clean anywhere for service nodes.
So the 222.7 GB that was pruned by hand will accumulate again with nothing scheduled to stop it.
Items 2 and 3 — OOM kills on SERVER, kernel errors on dreamstream1 — are pure live-host diagnostics. Git can say nothing; they're settled by journalctl -k on those two nodes.
Worth connecting to what the other audits turned up, because item 2 may not need separate investigation:
#503 established confirmed bad RAM on SERVER, mitigated with a GRUB_BADRAM hole but the DIMM was never replaced and memtest was never run to map the full extent. OOM kills on a machine with known-bad memory pages are worth checking against that before treating them as a capacity problem.
#756 means nobody was ever paged for any of this. The disk alert that climbed 93% → 99% over twelve hours was found only by log sweep, and #756 is still open, so the same silence applies to the next occurrence.
Remaining: the scheduled service-node prune (item 1) in full; OOM and kernel-error triage, ideally checked against #503's bad-RAM finding first.
Audited against `origin/master` — **item 1 NOT DONE; items 2–3 unverifiable from git.** Staying open.
**First, confirming these are genuinely two different tickets**, because the overlap with #514 is easy to assume away. The only prune script in the repo is `gitea-act-runner/cleanup/gitea-actions-prune.sh` (`image prune` at `:74`) — that lives under `gitea-act-runner/` and is the **build/runner-node** cleanup #514 covers. This ticket is about **service nodes**, and `git grep -rn "image prune\|system prune\|container prune" origin/master` returns **only** those lines in that one runner script. No cron unit, no swarm-cronjob, no `until=168h` deep clean anywhere for service nodes.
So the 222.7 GB that was pruned by hand will accumulate again with nothing scheduled to stop it.
**Items 2 and 3** — OOM kills on SERVER, kernel errors on dreamstream1 — are pure live-host diagnostics. Git can say nothing; they're settled by `journalctl -k` on those two nodes.
**Worth connecting to what the other audits turned up**, because item 2 may not need separate investigation:
- **#503** established **confirmed bad RAM on SERVER**, mitigated with a `GRUB_BADRAM` hole but the DIMM was never replaced and memtest was never run to map the full extent. OOM kills on a machine with known-bad memory pages are worth checking against that before treating them as a capacity problem.
- **#756** means nobody was ever paged for any of this. The disk alert that climbed 93% → 99% over twelve hours was found only by log sweep, and #756 is still open, so the same silence applies to the next occurrence.
**Remaining:** the scheduled service-node prune (item 1) in full; OOM and kernel-error triage, ideally checked against #503's bad-RAM finding first.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Found during 2026-07-21 swarm recon; the CRITICAL disk alerts were never delivered because ops email is dead (#756).
What happened
/climbed 93% (07-20 14:21Z) → 99% (07-21 02:22Z), ~1%/hour, with 5.1G free of 436.5G at 02:5xZ.docker system dfon SERVER: 680 images totalling 276.5GB, only 91 in use — 216GB (78%) reclaimable. overlay2 was 262.3GB. Cause: every CI deploy pulls a new:latestdigest and the superseded images accumulate as dangling; SERVER hosts the most services so it accumulates fastest.Remediation done (2026-07-21 ~03:0xZ)
Ran the standard conservative cleanup (one-shot swarm job pinned to SERVER):
docker container prune -f→ 116.3MBdocker image prune -f(dangling only — cannot touch in-use or tagged images) → 222.7GB reclaimed/at 49% (214.6G free).Follow-ups needed
docker image prune -f(dangling) weekly, ordocker image prune -af --filter until=168hfor a deeper clean. Same class of problem as #514 (build-node GITEA-ACTION volumes) but on the service nodes' image cache — #514's fix does not cover this.HIGH: OOM kill on SERVERalerts (undelivered) at 20:32, 22:58, 00:27, 02:24Z on 07-20/21. Which process is being killed and whether it correlates with the fleet redeploy memory footprint needs a look (journalctl -k | grep -i oomon SERVER).journalctl -ktriage.Audited against
origin/master— item 1 NOT DONE; items 2–3 unverifiable from git. Staying open.First, confirming these are genuinely two different tickets, because the overlap with #514 is easy to assume away. The only prune script in the repo is
gitea-act-runner/cleanup/gitea-actions-prune.sh(image pruneat:74) — that lives undergitea-act-runner/and is the build/runner-node cleanup #514 covers. This ticket is about service nodes, andgit grep -rn "image prune\|system prune\|container prune" origin/masterreturns only those lines in that one runner script. No cron unit, no swarm-cronjob, nountil=168hdeep clean anywhere for service nodes.So the 222.7 GB that was pruned by hand will accumulate again with nothing scheduled to stop it.
Items 2 and 3 — OOM kills on SERVER, kernel errors on dreamstream1 — are pure live-host diagnostics. Git can say nothing; they're settled by
journalctl -kon those two nodes.Worth connecting to what the other audits turned up, because item 2 may not need separate investigation:
GRUB_BADRAMhole but the DIMM was never replaced and memtest was never run to map the full extent. OOM kills on a machine with known-bad memory pages are worth checking against that before treating them as a capacity problem.Remaining: the scheduled service-node prune (item 1) in full; OOM and kernel-error triage, ideally checked against #503's bad-RAM finding first.