If you manage Docker servers, you've likely encountered the "disk full" scenario where /var/lib/docker/overlay2/ is consuming all available space. Navigating these hash-named directories to find the culprit can be a nightmare.
We've created a Bash script that takes an overlay layer ID (or path) and hunts down its owner—whether it's a writable container layer or a read-only image layer.
The Script
Save the following code as find-overlay-owner.sh and make it executable (chmod +x find-overlay-owner.sh).
find-overlay-owner.sh
#!/usr/bin/env bash
#
# Usage:
# ./find-overlay-owner.sh /var/lib/docker/overlay2/<layer-id>
# or
# ./find-overlay-owner.sh 4ff38a668e6944c3c8d7dec10d21a5ecebf4cf6d9d9db8fcb76c08b7ee7166e1
#
set -e
LAYER="$1"
if [[ -z "$LAYER" ]]; then
echo "Usage: $0 <overlay2 layer dir or id>"
exit 1
fi
# Extract just the id if full path provided
BASENAME=$(basename "$LAYER")
echo "Checking overlay2 layer: $BASENAME"
echo "---------------------------------------"
##############################################
# 1️⃣ Check if this is a container writable layer
##############################################
FOUND_CONTAINER=0
for c in $(docker ps -aq); do
UPPER=$(docker inspect -f '{{.GraphDriver.Data.UpperDir}}' $c 2>/dev/null || true)
MERGED=$(docker inspect -f '{{.GraphDriver.Data.MergedDir}}' $c 2>/dev/null || true)
if [[ "$UPPER" == *"$BASENAME"* ]] || [[ "$MERGED" == *"$BASENAME"* ]]; then
echo "Container layer found!"
echo "Container ID: $c"
docker inspect -f 'Name: {{.Name}}
Image: {{.Config.Image}}
UpperDir: {{.GraphDriver.Data.UpperDir}}
MergedDir: {{.GraphDriver.Data.MergedDir}}' $c
echo "---------------------------------------"
FOUND_CONTAINER=1
fi
done
if [[ $FOUND_CONTAINER -eq 0 ]]; then
echo "No matching container writable layer found."
fi
##############################################
# 2️⃣ Check Docker layer database (image layers)
##############################################
echo "Searching image layerdb..."
MATCHES=$(grep -rl "$BASENAME" /var/lib/docker/image/overlay2/layerdb/sha256/ || true)
if [[ -z "$MATCHES" ]]; then
echo "No image layer entry found."
exit 0
fi
echo "$MATCHES" | while read -r file; do
LAYERDIR=$(dirname "$file")
echo ""
echo "Image layer match:"
echo "LayerDB path: $LAYERDIR"
if [[ -f "$LAYERDIR/cache-id" ]]; then
echo "cache-id: $(cat "$LAYERDIR/cache-id")"
fi
if [[ -f "$LAYERDIR/diff" ]]; then
echo "diff-id: $(cat "$LAYERDIR/diff")"
fi
if [[ -f "$LAYERDIR/parent" ]]; then
echo "parent: $(cat "$LAYERDIR/parent")"
fi
# Find which images reference this diff
DIFFID=$(cat "$LAYERDIR/diff" 2>/dev/null || true)
if [[ -n "$DIFFID" ]]; then
echo ""
echo "Searching images referencing this diff-id..."
docker images --no-trunc --digests | grep "${DIFFID#sha256:}" || echo "No direct image reference found"
fi
doneHow It Works
The script performs two main checks:
- Container Inspection: It iterates through all containers (running and stopped) and checks their
UpperDirandMergedDir. If the layer ID matches, it identifies the container that is writing to that layer. - Image Layer Database: If no container is found, it searches the Docker image layer database (
/var/lib/docker/image/overlay2/layerdb). If a match is found, it extracts thediff-idand cross-references it withdocker imagesto tell you which image owns that layer.
Usage
You will typically need root privileges to access the Docker directories.
# Using the full path
sudo ./find-overlay-owner.sh /var/lib/docker/overlay2/4ff38a66...
# Using just the layer ID
sudo ./find-overlay-owner.sh 4ff38a66...