Skip to content

Instantly share code, notes, and snippets.

@gquittet
Last active May 21, 2025 15:28
Show Gist options
  • Save gquittet/a13715e33b74fd85217b28f065c4dc06 to your computer and use it in GitHub Desktop.
Save gquittet/a13715e33b74fd85217b28f065c4dc06 to your computer and use it in GitHub Desktop.
Docker private registry cleaning script
#!/usr/bin/env sh
install_missing_dep() {
if ! command -v "$1" > /dev/null 2>&1; then
echo "Installing $1..."
apk add "$1"
fi
}
DEPS="curl jq"
echo "Checking dependencies..."
for DEP in $DEPS; do
install_missing_dep "$DEP"
done
# The goal of this script is to clean all images created by a CD
# Remove all cache commit images + old versions (one month old)
# Replace me
CLEAN_REGISTRY_AUTH_ENDPOINT=http://internal-docker-registry-auth:5001
REGISTRY_AUTH_TOKEN_SERVICE=Authentication
BLOB_LIMIT_DURATION=$((30*24*60*60)) # 30 days in seconds
if test -f .env; then
. .env
fi
REGISTRY_ENDPOINT="http://${REGISTRY_HTTP_ADDR-127.0.0.1:5000}/v2"
NOW=$(date +%s)
BLOB_LIMIT_DATE=$(date -d@"$((NOW-BLOB_LIMIT_DURATION))" +%s)
echo "Login to registry..."
# If the password is defined via env var -> don't ask and ask if it's not the case.
if [ -z "$CLEAN_REGISTRY_AUTH_PASSWORD" ]; then
echo "Enter the password for (admin):"
# shellcheck disable=SC3045
read -rs PASSWORD
else
PASSWORD=$CLEAN_REGISTRY_AUTH_PASSWORD
fi
delete_image() {
REPOSITORY=$1
TOKEN=$2
IMAGE=$3
echo "Cleaning $REPOSITORY:$IMAGE..."
SHA_SUM=$(curl --silent -v -H "Authorization: Bearer $TOKEN" -H "Accept: application/vnd.docker.distribution.manifest.v2+json" "$REGISTRY_ENDPOINT"/"$REPOSITORY"/manifests/"$IMAGE" 2>&1 | grep Docker-Content-Digest | awk '{print ($3)}')
curl --silent -X DELETE -H "Authorization: Bearer $TOKEN" "$REGISTRY_ENDPOINT"/"$REPOSITORY"/manifests/"$SHA_SUM"
}
get_image_date() {
REPOSITORY=$1
TOKEN=$2
IMAGE=$3
BLOB=$(curl --silent -H "Authorization: Bearer $TOKEN" -H "Accept: application/vnd.docker.distribution.manifest.v2+json" "$REGISTRY_ENDPOINT"/"$REPOSITORY"/manifests/"$IMAGE" | jq -r '.config.digest')
BLOB_DATE=$(curl --silent -H "Authorization: Bearer $TOKEN" -H "Accept: application/vnd.docker.distribution.manifest.v2+json" "$REGISTRY_ENDPOINT"/"$REPOSITORY"/blobs/"$BLOB" | jq -r '.created | sub("\\.[[:digit:]]+"; "") | fromdateiso8601')
echo "$BLOB_DATE"
}
list_repositories() {
TOKEN=$(curl --silent -u admin:"$PASSWORD" "$CLEAN_REGISTRY_AUTH_ENDPOINT/auth?service=$REGISTRY_AUTH_TOKEN_SERVICE&scope=registry:catalog:*" | jq '.token' | tr -d '"')
REPOSITORIES=$(curl --silent -H "Authorization: Bearer $TOKEN" "$REGISTRY_ENDPOINT"/_catalog | jq -r '.repositories | .[]')
echo "$REPOSITORIES"
}
auth_repository() {
REPOSITORY=$1
TOKEN=$(curl --silent -u admin:"$PASSWORD" "$CLEAN_REGISTRY_AUTH_ENDPOINT/auth?service=$REGISTRY_AUTH_TOKEN_SERVICE&scope=repository:$REPOSITORY:pull,delete" | jq '.token' | tr -d '"')
echo "$TOKEN"
}
# Used for debug purposes only
list_tags() {
REPOSITORIES=$(list_repositories)
for REPOSITORY in $REPOSITORIES; do
TOKEN=$(auth_repository "$REPOSITORY")
curl --silent -H "Authorization: Bearer $TOKEN" "$REGISTRY_ENDPOINT"/"$REPOSITORY"/tags/list
done
}
REPOSITORIES=$(list_repositories)
for REPOSITORY in $REPOSITORIES; do
echo "Cleaning $REPOSITORY repository..."
TOKEN=$(auth_repository "$REPOSITORY")
echo "Cleaning non-version (non vA.B.C/latest images)..."
COMMIT_IMAGES=$(curl --silent -H "Authorization: Bearer $TOKEN" "$REGISTRY_ENDPOINT"/"$REPOSITORY"/tags/list | jq -r '.tags | .[] | select(. | test("^v\\d+\\.\\d+\\.\\d+$") | not) | select(. != "latest")')
for IMAGE in $COMMIT_IMAGES; do
delete_image "$REPOSITORY" "$TOKEN" "$IMAGE"
done
echo "Cleaning old versions images..."
# Select all versions and sort by numbers to always keep the latest version even if the date is lower than the limit.
VERSION_IMAGES=$(curl --silent -H "Authorization: Bearer $TOKEN" "$REGISTRY_ENDPOINT"/"$REPOSITORY"/tags/list | jq -r '
.tags
| map(select(test("^v\\d+\\.\\d+\\.\\d+$")))
| map({
raw: .,
major: (capture("^v(?<major>\\d+)").major | tonumber),
minor: (capture("^v\\d+\\.(?<minor>\\d+)").minor | tonumber),
patch: (capture("^v\\d+\\.\\d+\\.(?<patch>\\d+)").patch | tonumber)
})
| sort_by(.major, .minor, .patch)
| map(.raw)[:-1][]
')
for IMAGE in $VERSION_IMAGES; do
BLOB_DATE=$(get_image_date "$REPOSITORY" "$TOKEN" "$IMAGE")
if [ "$BLOB_DATE" -lt "$BLOB_LIMIT_DATE" ]; then
delete_image "$REPOSITORY" "$TOKEN" "$IMAGE"
fi
done
echo ""
done
echo "Running garbage collection..."
registry garbage-collect --delete-untagged /etc/docker/registry/config.yml
echo ""
echo "Done!"
echo ""
echo "List of remaining tags:"
list_tags
echo ""
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment