docker-py's containers.run(remove=True) only removes a container after it
ran. A helper whose start failed - an NFS destination refusing the mount -
stayed behind in "Created", holding the destination's volume. Every Test
click and scheduled backup added one (a user found 18), the volume could
not be deleted even with everything stopped, and changed mount options
never took effect because the volume could not be recreated with them.
- docker_client.run_container: create, start, wait, collect output and
always remove, also when the start fails; pulls a missing image. Used
for NFS destination I/O, bind-path inspection, the volume wipe before a
restore and the Trivy scanner.
- Every helper container is labelled stackpilot.helper; on startup the
stopped ones are removed, together with anything left on an NFS
destination volume by older versions.
- When an NFS destination's options change, stopped containers holding
its volume are removed before it is recreated; a running one is now a
clear error instead of silently keeping the old options.
- A refused mount explains itself: NFS enabled and reachable from the
Docker host, NFSv3 needing rpcbind and mountd, and nfsvers=4 needing
only port 2049.
- The scanner waits for Trivy's own 10-minute timeout instead of giving
up after the default client's 60 seconds.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>