Skip to content

Instantly share code, notes, and snippets.

@CloCkWeRX
Created August 7, 2026 05:37
Show Gist options
  • Select an option

  • Save CloCkWeRX/c208954ec49a34c45e7a0aa1c5989742 to your computer and use it in GitHub Desktop.

Select an option

Save CloCkWeRX/c208954ec49a34c45e7a0aa1c5989742 to your computer and use it in GitHub Desktop.
Prune old containers conservatively
#!/usr/bin/env bash
#
# docker-container-prune.sh
# Weekly low-priority cleanup of stopped containers older than 30 days.
# Runs pre-flight risk checks (system load, free disk space) and skips
# the run if either looks risky, instead of just logging that they're fine.
#
set -euo pipefail
### ------------------------- CONFIG ------------------------- ###
PRUNE_FILTER="until=30d" # prune containers stopped >30 days
LOCK_FILE="/var/run/docker-container-prune.lock"
DOCKER_ROOT="/var/lib/docker" # where docker stores images/containers
MAX_LOAD_MULTIPLIER=1.5 # skip if 1-min load avg > cores * this
MIN_FREE_DISK_PERCENT=10 # skip if free space on DOCKER_ROOT below this
### ------------------------------------------------------------ ###
# Ensure only one instance runs at a time (silent skip, nothing to explain).
exec 9>"$LOCK_FILE"
flock -n 9 || exit 0
# Docker must be present and responsive.
if ! command -v docker >/dev/null 2>&1; then
echo "docker CLI not found in PATH." >&2
exit 1
fi
if ! docker info >/dev/null 2>&1; then
echo "docker daemon not responding." >&2
exit 1
fi
# --- Risk check: system load ---
cores=$(nproc)
load_1min=$(awk '{print $1}' /proc/loadavg)
max_load=$(awk -v c="$cores" -v m="$MAX_LOAD_MULTIPLIER" 'BEGIN{printf "%.2f", c*m}')
over_load=$(awk -v l="$load_1min" -v m="$max_load" 'BEGIN{print (l>m)?1:0}')
if [[ "$over_load" -eq 1 ]]; then
echo "Skipping: load average ($load_1min) exceeds threshold ($max_load for $cores cores)." >&2
exit 0
fi
# --- Risk check: free disk space ---
# Low free space makes docker metadata ops unreliable, so we defer rather
# than risk a partial/failed cleanup.
used_percent=$(df --output=pcent "$DOCKER_ROOT" | tail -1 | tr -dc '0-9')
free_percent=$((100 - used_percent))
if (( free_percent < MIN_FREE_DISK_PERCENT )); then
echo "Skipping: only ${free_percent}% free on $DOCKER_ROOT (threshold ${MIN_FREE_DISK_PERCENT}%)." >&2
exit 0
fi
# --- Run the prune at low CPU/IO priority ---
# -f (force) is required since cron has no TTY to confirm the prompt.
if ! nice -n 19 ionice -c3 docker container prune -f --filter "$PRUNE_FILTER"; then
echo "docker container prune failed." >&2
exit 1
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment