Skip to content

KB-059: A hung par2/unrar/7z helper stalls the entire SABnzbd queue

Status: Resolved (#5466).

Symptom

SABnzbd's post-processing queue stops advancing. Downloads keep completing and queuing for post-processing, but nothing finishes it. On 2026-09-26 a par2 repair spun on one CPU core for about 30 hours without reading a byte, while roughly 3,800 downloaded jobs waited behind it.

Cause

SABnzbd post-processes one job at a time and puts no timeout on the external helpers (par2, unrar, 7z) it shells out to. A single helper that hangs, rather than crashing or exiting, stalls every job queued behind it indefinitely.

SABnzbd's own mode=cancel_pp API cannot be used to recover from this: PostProcessor.cancel_pp (checked against 5.1.3 and develop) returns after checking only the first job in its list, so it misses the active job whenever another job is ahead of it in the queue.

No existing tool in this repository covered the gap. decluttarr (already deployed) deliberately skips completed downloads. Cleanuparr is torrent-only.

Fix

resources/pp-watchdog.sh, run in the background inside the SABnzbd app container (catatonit -- sh -c "/pp-watchdog/pp-watchdog.sh & exec /entrypoint.sh"), watches rchar + wchar from /proc/<pid>/io for every par2/unrar/7z process once a minute. A helper whose counters have advanced less than 16 MiB in 30 minutes gets SIGTERM; one that survives 5 more minutes gets SIGKILL. The floor matters: a hung par2 seen on 2026-09-28 sat at 97% CPU for 2 hours while still reading about 0.5 MB every few minutes, so a "no I/O at all" rule never fired. A healthy repair reads hundreds of MB a minute. SABnzbd then fails that one job, the arr blocklists the release and searches again, and post-processing moves on to the next job.

Every SIGTERM/SIGKILL is also appended to /config/pp-watchdog-kills.log on the SABnzbd config volume, trimmed to its newest 500 lines once it passes 1,000. Under a busy queue the container log rotates within about an hour, so kubectl logs alone loses a kill long before anyone looks for it. Read it with kubectl -n downloads exec deploy/sabnzbd -c app -- tail /config/pp-watchdog-kills.log.

A helper blocked reading a pipe is left alone: that's Direct Unpack's unrar waiting for SABnzbd to hand it the next volume, not a hang. unrar blocked writing a pipe is the sabnzbd/sabnzbd#3638 hang, and is killed.

Why a background process, not a sidecar

A sidecar container would need shareProcessNamespace to see the helper processes at all. That setting also lets every other container in the pod see this one's environment, including gluetun-webui, which runs as the same uid. gluetun-webui would then be able to read SABnzbd's API key and the gluetun sidecar's WireGuard key. Running the watchdog as a background process inside the SABnzbd container itself avoids sharing the pod's process namespace at all.

References

  • Fix: #5466.
  • sabnzbd/sabnzbd#3638: the pipe-write hang the watchdog's read/write pipe distinction exists for.