Created
August 7, 2026 05:37
-
-
Save CloCkWeRX/c208954ec49a34c45e7a0aa1c5989742 to your computer and use it in GitHub Desktop.
Prune old containers conservatively
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/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