Skip to content

Instantly share code, notes, and snippets.

@agners
Last active November 30, 2023 21:18
Show Gist options
  • Save agners/c5ae781c828dbf76c4cc1e4588c034a5 to your computer and use it in GitHub Desktop.
Save agners/c5ae781c828dbf76c4cc1e4588c034a5 to your computer and use it in GitHub Desktop.
Prune corrupted Docker layers in overlayfs2 storage
#!/bin/sh
# (c) 2023 Stefan Agner
# This scripts attempts to clear corrupted Docker overlay2 storage metadata
# which can be left over after a power failure.
# See also: https://github.com/moby/moby/issues/42964
#
# Typically the error message when attempting to pull the image for which
# corrupted layers are in the storage looks like:
# failed to register layer: error creating overlay mount to /mnt/data/docker/overlay2/6ee02298ee75a4f96e77f90551673cb700f29867f9ad1c4e20b8c816bfcf0735/merged: too many levels of symbolic links
# Docker storage directory, intended for Home Assistant OS
docker_storage_dir="/mnt/data/docker"
# Warning message
echo "WARNING: This script attempts to clear corrupted Docker overlay2 storage metadata."
echo "Use with caution, as it may result in data loss. Ensure you have backups before running this script."
# Initial sanity check for the overlay2 directory
overlay2_dir="$docker_storage_dir/overlay2"
layerdb_dir="$docker_storage_dir/image/overlay2/layerdb/sha256"
if [ ! -d "$overlay2_dir" ]; then
echo "Error: Directory $overlay2_dir does not exist."
exit 1
fi
# Find unique cache IDs
cache_ids=$(find "$overlay2_dir" -type f -size 0 \( -name lower -o -name link \) -exec sh -c 'basename $(dirname {})' \; | sort -u)
# Check if cache IDs are found
if [ -z "$cache_ids" ]; then
echo "No cache IDs with size 0 found. Exiting."
exit 0
fi
# Count the number of cache IDs found
num_cache_ids=$(echo "$cache_ids" | wc -w)
# Prompt the user for confirmation
echo "Found $num_cache_ids cache IDs with size 0. Do you want to continue and delete them? (yes/no)"
read user_confirmation
# Check user's response
if [ "$user_confirmation" != "yes" ]; then
echo "Operation canceled. No directories were deleted."
exit 0
fi
# Initialize counters for summary
deleted_cache_dirs=0
deleted_layerdb_dirs=0
# Iterate through cache IDs
for cache_id in $cache_ids; do
# Find corresponding directory in layerdb
layerdb_dir_path=$(dirname $(grep -r -e "$cache_id" "$layerdb_dir"/*/cache-id))
cache_dir="$overlay2_dir/$cache_id"
# Remove cache directory
if [ -d "$cache_dir" ]; then
rm -rf "$cache_dir"
deleted_cache_dirs=$((deleted_cache_dirs + 1))
fi
# Remove LayerDB directory
if [ -d "$layerdb_dir_path" ]; then
rm -rf "$layerdb_dir_path"
deleted_layerdb_dirs=$((deleted_layerdb_dirs + 1))
fi
done
# Print summary
echo "Summary: Deleted $deleted_cache_dirs cache directories and $deleted_layerdb_dirs LayerDB directories."
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment