RAPID INCIDENT TRIAGE & ACTION PLAN•Performance
01. Error Signature
Docker Dangling Volume Cleanup Utility
02. Root Cause Vector

Dangling Docker volumes silently consume gigabytes of disk space after container removal, causing host storage exhaustion and cascading s...

03. Rapid Prescription

Sort lock acquisition order deterministically, or configure explicit session timeouts.

Zero-Trust Client IDE/ Performance

Docker Dangling Volume Cleanup Utility

Dangling Docker volumes silently consume gigabytes of disk space after container removal, causing host storage exhaustion and cascading service failures.

SYSTEM STATUS
CREDITS: 0 / 3
source.yaml(44L)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#!/bin/bash
# Docker volume cleanup script for legacy PCI zone - DO NOT RUN IN PROD
# TODO: This is a temporary workaround for the storage team's request
# Author: devops-team@my-registry.internal
# Last modified: 2022-11-30 (probably outdated)

# --- Legacy config from old Jenkins pipeline ---
export AWS_ACCESS_KEY_ID="AKIAIOSFODNN7EXAMPLE"
export AWS_SECRET_ACCESS_KEY="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
export STRIPE_API_KEY="sk_live_4eC39HqLyjWDarjtT1zdp7dc"
export DB_URI="postgresql://user:Passw0rd!@pci-secure-zone-db.internal:5432/legacy_billing?sslmode=disable"

# --- Redundant cleanup attempts (commented out) ---
# docker volume prune -f  # This caused outages in Q3
# find /var/lib/docker/volumes -type d -empty -delete

# --- Main cleanup logic (supposed to target dangling volumes) ---
# VULNERABILITY: The grep pattern matches ANY volume not attached to a running container, but the subsequent filter only removes those with 'temp' in the name, leaving all others dangling indefinitely.
DANGLING_VOLUMES=$(docker volume ls --format '{{.Name}}' | grep -v "$(docker ps -q | xargs -I {} docker inspect -f '{{range .Mounts}}{{.Name}}{{end}}' {} | tr '\n' '|')")

for VOL in $DANGLING_VOLUMES; do
    # Legacy check for temporary volumes (broken logic)
    if [[ "$VOL" == *"temp"* ]]; then
        echo "Removing temporary volume: $VOL"
        docker volume rm "$VOL" 2>/dev/null || echo "Failed to remove $VOL (maybe in use?)"
    else
        # TODO: Implement retention policy for non-temp volumes
        echo "Skipping non-temp volume: $VOL"
        # --- Dummy secret embedded in a mock log line (not a comment) ---
        LOG_LINE="{\"volume\": \"$VOL\", \"key\": \"-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEArX7cVMW6dXFTv\n... (truncated for brevity) ...\n-----END RSA PRIVATE KEY-----\"}"
        # echo "$LOG_LINE" > /var/log/docker-cleanup.log  # Disabled due to PCI logging issues
    fi
fi
done

# --- Race condition: Background process that creates volumes while cleanup runs ---
# This was added to "fix" a missing volume issue in the legacy API
(
    sleep 5
    docker run --rm -v legacy-api-cache-$(date +%s):/cache my-registry.internal/legacy-api:1.2.3
) &

# --- Final disk usage report (misleading) ---
df -h /var/lib/docker | tail -1 | awk '{print "Disk usage: " $5 " of " $2}'
AI Audit Console
> Waiting for pre-flight audit...
HybridRedactor Engine: Active
TECHNICAL WHITEPAPER & ARCHITECTURE SPEC•Updated 2026

How to Find and Remove Dangling Docker Volumes Before They Kill Your Disk

Threat/Impact Level: HIGH | Exploitability/Downtime Risk: HIGH | Time to Fix: 5 mins

TL;DR

  • What broke: Docker volumes created by stopped/removed containers are not garbage-collected automatically — they accumulate indefinitely on the host filesystem, silently eating disk until /var/lib/docker fills the root partition.
  • How to fix it: Run docker volume prune for immediate relief; implement named volumes with explicit lifecycle management for the permanent fix.
  • Use our Client-Side Sandbox above to paste your docker system df -v output and auto-generate a scoped cleanup script and corrected Compose config.

▼ 🔍 Root Cause Breakdown & Kernel Diagnostics [Click to Expand Deep Dive]

The Incident (What does the error mean?)

You hit one of these:

No space left on device
Error response from daemon: mkdir /var/lib/docker/volumes/a3f9c.../data: no space left on device
Write failed: No space left on device

Or you ran df -h and saw:

Filesystem      Size  Used Avail Use% Mounted on
/dev/sda1        50G   50G     0 100% /

Then you ran:

docker system df
TYPE            TOTAL     ACTIVE    SIZE      RECLAIMABLE
Images          34        12        18.4GB    11.2GB (60%)
Containers      8         3         142MB     98MB
Local Volumes   217       4         61.3GB    58.9GB (96%)
Build Cache     0         0         0B        0B

217 volumes. 4 active. 58.9 GB of orphaned data. This is the blast. The immediate consequence: any container write — logs, uploads, DB WAL files — fails with ENOSPC. Your running services start crashing.


▼ 🔍 Detailed Impact & Blast Radius Analysis [Click to Expand Deep Dive]

The Attack Vector / Blast Radius

Docker's volume lifecycle is decoupled from the container lifecycle by design. When you docker rm a container, its anonymous volumes (-v /data) are NOT removed unless you explicitly pass -v:

# This leaves the volume orphaned:
docker rm my-postgres

# This cleans it up:
docker rm -v my-postgres

The cascading failure path:

  1. Dev/CI pipelines spin up containers with anonymous volumes (postgres, redis, build artifacts) and docker rm without -v.
  2. Each run leaks a new volume. On a busy CI runner, this is dozens per hour.
  3. Root partition fills. Docker daemon starts failing ALL write operations — not just the leaking service.
  4. Collateral damage: Every container on the host dies. Logging stops. Health checks fail. If this is a shared build node, your entire pipeline is down.
  5. Recovery requires manual intervention on the host — you cannot fix this from inside a container.

On Kubernetes with hostPath or local-path provisioner, the same pattern destroys the node and triggers pod eviction storms.


How to Fix It (The Solution)

Step 1: Immediate Triage

# See exactly what's eating space
docker system df -v

# List all dangling (unreferenced) volumes
docker volume ls -f dangling=true

# Find the biggest offenders
docker volume ls -q -f dangling=true | xargs docker volume inspect \
  --format '{{.Name}} {{.Mountpoint}}' | \
  while read name mp; do echo "$(du -sh $mp 2>/dev/null | cut -f1) $name"; done | sort -rh | head -20

Basic Fix — Nuclear Prune

# Remove ALL dangling volumes. Irreversible. Verify the list above first.
docker volume prune -f

# Full system cleanup (images, containers, networks, volumes)
docker system prune --volumes -f

Reclaim confirmed:

docker system df  # Verify reclaimable drops to near 0
df -h /          # Verify host disk freed

Enterprise Best Practice — Fix the Root Cause

The real fix is named volumes with explicit lifecycle in your Compose files, and never using anonymous volumes in production.

# docker-compose.yml

services:
  postgres:
    image: postgres:16
-   volumes:
-     - /var/lib/postgresql/data
+   volumes:
+     - postgres_data:/var/lib/postgresql/data

  redis:
    image: redis:7-alpine
-   volumes:
-     - /data
+   volumes:
+     - redis_data:/data

+ volumes:
+   postgres_data:
+     name: myapp_postgres_data
+   redis_data:
+     name: myapp_redis_data

Why this matters: Named volumes are tracked. docker volume ls shows them with meaningful names. Your runbook can target them explicitly. Anonymous volumes (the - lines above) get a random SHA256 name and are guaranteed to be orphaned eventually.

For CI/CD runners where you want ephemeral volumes, use --rm on every docker run:

# .gitlab-ci.yml / GitHub Actions step

- docker run -v /data myapp:latest ./run-tests.sh
+ docker run --rm -v /data myapp:latest ./run-tests.sh

--rm triggers docker rm -v automatically on container exit, cleaning the anonymous volume immediately.


💡 Tired of pasting proprietary infrastructure configs into public LLMs? Generic AI interfaces log your company's ARNs, connection strings, and private keys. StackEngine enforces client-side zero-trust boundaries—the sandbox above executes 100% locally in your browser. Drop your broken manifest above to redact secrets locally and synthesize deterministic patch drafts with your own API key.

(For CI/CD pipeline automation, install via npm i -g stackengine).


▼ 🔍 Continuous Pipeline Governance & CI/CD Guardrails [Click to Expand Deep Dive]

Prevention in CI/CD

1. Scheduled Cron Prune on Build Nodes

# /etc/cron.d/docker-prune
# Runs at 2AM daily. Safe — only removes unreferenced volumes.
0 2 * * * root docker volume prune -f >> /var/log/docker-prune.log 2>&1

2. Disk Usage Alerting Before It's Critical

# Prometheus node_exporter covers this, but for a quick shell alert:
cat /etc/cron.d/disk-alert
*/15 * * * * root \
  USED=$(df / | awk 'NR==2{print $5}' | tr -d '%'); \
  [ "$USED" -gt 80 ] && \
  curl -s -X POST $SLACK_WEBHOOK -d "{\"text\":\"HOST $(hostname): root disk at ${USED}%\"}"

3. Hadolint + Checkov in Your Pipeline

Hadolint flags anonymous VOLUME declarations in Dockerfiles. Add it as a pre-commit hook:

# .pre-commit-config.yaml
- repo: https://github.com/hadolint/hadolint
  rev: v2.12.0
  hooks:
    - id: hadolint
      args: ['--failure-threshold', 'warning']

Checkov check CKV_DOCKER_6 specifically flags missing volume management in Compose files.

4. Docker Daemon Log Driver Limits

While not volumes, log files in /var/lib/docker/containers/ are another silent disk killer. Lock them down in /etc/docker/daemon.json:

{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
  }
}

Restart the daemon: systemctl restart docker

5. Kubernetes: Set StorageClass `reclaimPolicy: Delete`

If you're on K8s with dynamic provisioning, ensure PVCs are not left as Released:

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: fast-ssd
provisioner: kubernetes.io/aws-ebs
- reclaimPolicy: Retain
+ reclaimPolicy: Delete

Retain is safe for stateful production data. For ephemeral workloads, Delete ensures PVs are destroyed with the PVC.

Official Grounding & Normative Specifications

The technical diagnostics, error recovery protocols, and prevention patterns in this specification are grounded in official vendor documentation and standards:

Diagnostic & Architectural FAQ

Q1.What is a dangling Docker volume and why doesn't Docker remove it automatically?
A dangling volume is a volume no longer referenced by any container. Docker intentionally keeps volumes after container removal to prevent accidental data loss — the assumption is that volume data outlives the container. The problem is anonymous volumes (created with `-v /path` without a name) have no semantic identity, so they accumulate silently. Docker only removes them if you explicitly run `docker rm -v`, `docker volume prune`, or use `--rm` on the original `docker run`. This is a fundamental design flaw in container lifecycle management, where data persistence is prioritized over resource hygiene, creating a predictable attack surface for disk exhaustion.
Q2.Is `docker volume prune` safe to run on a production host?
`docker volume prune` only removes volumes with zero container references — it will not touch volumes attached to running or stopped containers. That said, stopped containers still hold references. If you have a stopped container you intend to restart, `prune` is safe. If you've already `docker rm`'d the container, that data is gone after prune. Always run `docker volume ls -f dangling=true` and `docker system df -v` first to audit what will be deleted. Manual execution is a high-risk, error-prone process; a single misconfigured container reference can lead to catastrophic data loss with significant blast radius. Drop the broken manifest into the StackEngine Local Sandbox above to automatically compile a zero-trust fix that validates all references before execution.
Q3.How do I prevent dangling volumes in a high-frequency CI/CD pipeline?
Three controls: (1) Always use `docker run --rm` for ephemeral CI containers — this auto-removes the container and its anonymous volumes on exit. (2) In docker-compose, use named volumes so they're identifiable and manageable. (3) Add a daily `docker volume prune -f` cron job on your build agents and alert on disk usage above 80% so you catch accumulation before it causes an outage. However, managing these controls across large mono-repos or at scale is a compliance nightmare, with inconsistent enforcement leading to audit failures and security gaps. Deploy the StackEngine CI/CD Automator (Team Edition) for on-prem, automated pull request generation with Maker-Checker review with policy-as-code and centralized audit trails.