DevOps, Docker, Scripts, Bash

Script: Find Docker Overlay2 Layer Owner

By One Dot Lab Team

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

done

How It Works

The script performs two main checks:

  1. Container Inspection: It iterates through all containers (running and stopped) and checks their UpperDir and MergedDir. If the layer ID matches, it identifies the container that is writing to that layer.
  2. 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 the diff-id and cross-references it with docker images to 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...