Skip to content

fix(upgrade): stop swapoff OOM from aborting run_upgrade - #3172

Merged
vpetersson merged 1 commit into
masterfrom
fix/swapoff-oom-upgrade-abort
Jul 9, 2026
Merged

fix(upgrade): stop swapoff OOM from aborting run_upgrade#3172
vpetersson merged 1 commit into
masterfrom
fix/swapoff-oom-upgrade-abort

Conversation

@vpetersson

Copy link
Copy Markdown
Contributor

Fixes #3165

Problem

run_upgrade.sh aborts at the Ansible task system : Disable swap
(ansible/roles/system/tasks/misc.yml) with:

fatal: [localhost]: FAILED! => {"changed": true, "cmd": ["/sbin/swapoff", "--all"],
  "rc": -9, "stderr": "", "stdout": ""}

rc: -9 is a SIGKILL from the OOM-killer, not an error return.
swapoff --all faults every used swap page back into RAM at once. It
runs at the peak-memory moment of the whole upgrade: right after the apt
dist-upgrade and while the previous-version Docker stack is still
resident (the stack isn't torn down/rebuilt until upgrade_docker_containers,
which runs after the entire Ansible play). On a memory-tight board
(here a 1 GB Pi 4 on Trixie, whose dynamically-sized swap is larger than
the old fixed 100 MB) there isn't enough headroom, so the kernel kills
swapoff and the upgrade aborts. The task itself is long-standing; the
Trixie upgrade path is what now reliably tips these boards over.

Fix

bin/install.sh — new stop_docker_stack(), called before
run_ansible_playbook, brings the running stack down first so swapoff
(and the dist-upgrade) have the RAM headroom they need. No-op on a fresh
install; best-effort so a stale compose file can't abort the upgrade.
upgrade_docker_containers brings everything back up afterwards, and the
install ends in a reboot regardless.

ansible/roles/system/tasks/misc.yml — hardened Disable swap:

  • memory guard: only run swapoff when SwapUsed < MemAvailable - 64 MiB
  • retries: 3 + until rc == 0 + failed_when: false: transient
    pressure retries; a persistent OOM leaves swap on rather than aborting
  • swapfile removal gated on rc == 0 — never unlink an active swapfile

Disabling swap is best-effort host hygiene, so skipping/leaving it on
under memory pressure is safe; it must never gate the whole upgrade.

Validation (real 1 GB Pi 4, arm64, zram swap)

Check Result
Reported condition (stack up, ~340 MB free, 342 MB swap used) guard skips swapoff → play green (old code would OOM-abort)
Exact rc -9 SIGKILL reproduced retried, tolerated, removal gated off, ansible exits 0
docker compose stop reclaims ~140 MB RAM, drops zram usage 342 M → 65 M
Run branch (headroom present) swapoff runs and succeeds (rc 0), removal gated on success

shellcheck, ansible-lint (production profile) and
ansible-playbook --syntax-check all pass.

🤖 Generated with Claude Code

`swapoff --all` faults all used swap back into RAM at once. On a
memory-tight board it runs at peak memory — right after the apt
dist-upgrade and with the previous Docker stack still resident — and the
OOM-killer reaps it (rc -9), aborting the whole upgrade (issue 3165).

- install.sh: stop the container stack before the Ansible play so
  swapoff has the headroom to fault swap back in; no-op on fresh install
- misc.yml: only attempt swapoff when RAM can absorb the used swap;
  retry and never hard-fail; remove the swapfile only once swap is off

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
@vpetersson
vpetersson requested a review from a team as a code owner July 9, 2026 05:54
@vpetersson
vpetersson requested a review from Copilot July 9, 2026 05:54
@sonarqubecloud

sonarqubecloud Bot commented Jul 9, 2026

Copy link
Copy Markdown

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR prevents Anthias upgrades from aborting when swapoff --all gets SIGKILL’d by the OOM killer on low-memory devices (Issue #3165), by freeing memory earlier and making swap disabling best-effort under memory pressure.

Changes:

  • Add a pre-Ansible stop_docker_stack() step in bin/install.sh to bring down the existing Docker Compose stack before the dist-upgrade/swapoff phase.
  • Harden the Ansible “Disable swap” step with a MemAvailable/SwapUsed guard, retries, and non-fatal behavior; gate swapfile removal on successful swapoff.

Reviewed changes

Copilot reviewed 1 out of 2 changed files in this pull request and generated no comments.

File Description
bin/install.sh Stops the running Docker Compose stack before the Ansible play to reclaim RAM and reduce OOM risk during swapoff/dist-upgrade.
ansible/roles/system/tasks/misc.yml Adds a memory-based guard and retry/tolerance logic around swapoff, and prevents removing /var/swap unless swapoff actually succeeded.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@vpetersson
vpetersson merged commit 675ef82 into master Jul 9, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] Upgrade to 20260708 or latest version fails with error on Disable SWAP with non-zero return code

2 participants