Skip to content

Instantly share code, notes, and snippets.

@akunzai
Created April 1, 2025 07:34
Show Gist options
  • Save akunzai/c330451f4d4cdcf5253bdfd50dd3ce50 to your computer and use it in GitHub Desktop.
Save akunzai/c330451f4d4cdcf5253bdfd50dd3ce50 to your computer and use it in GitHub Desktop.
Clean up old Podman images while keeping the newest version of each image
#!/bin/sh
# Show usage information
show_help() {
cat << EOF
Usage: $(basename "$0") [OPTIONS] [IMAGE_PATTERN]
Clean up old Podman images while keeping the newest version of each image.
Options:
--dryrun Show what would be removed without actually removing anything
--help Display this help message and exit
Arguments:
IMAGE_PATTERN Pattern to match image names (default: .*)
Examples:
$(basename "$0") # Clean all images from any registry
$(basename "$0") --dryrun # Show what would be removed (dry run)
$(basename "$0") asia-east1-docker.pkg.dev # Clean only asia-east1-docker.pkg.dev images
$(basename "$0") --dryrun asia-east1-docker.pkg.dev # Dry run for asia-east1-docker.pkg.dev images
EOF
exit 0
}
# Parse arguments
IMAGE_PATTERN=".*"
DRYRUN=false
for arg in "$@"; do
case "$arg" in
--help|-h)
show_help
;;
--dryrun)
DRYRUN=true
;;
-*)
# Skip other options
;;
*)
IMAGE_PATTERN="$arg"
;;
esac
done
# Extract version from tag
extract_version() {
echo "$1" | grep -o '[0-9]\+\(\.[0-9]\+\)*' 2>/dev/null || echo ""
}
# Get images
TMPFILE=$(mktemp)
RESULT_FILE=$(mktemp)
LATEST_FILE=$(mktemp)
CLEANUP_FILE=$(mktemp)
# Ensure cleanup on exit
cleanup() {
rm -f "$TMPFILE" "$RESULT_FILE" "$LATEST_FILE" "$CLEANUP_FILE"
}
trap cleanup EXIT INT TERM
# Get all images matching pattern
podman image ls | grep "$IMAGE_PATTERN" > "$TMPFILE" || true
if [ ! -s "$TMPFILE" ]; then
echo "No images found matching pattern: $IMAGE_PATTERN"
exit 0
fi
# Process each line into repo, tag, id
cat "$TMPFILE" | while read -r line; do
repo=$(echo "$line" | awk '{print $1}')
tag=$(echo "$line" | awk '{print $2}')
id=$(echo "$line" | awk '{print $3}')
# Skip if no repo or tag
[ -z "$repo" ] || [ -z "$tag" ] && continue
# Store repo, tag, id
echo "$repo|$tag|$id" >> "$RESULT_FILE"
done
# Find latest version for each repo and list images to remove
for repo in $(cat "$RESULT_FILE" | cut -d'|' -f1 | sort | uniq); do
# Get all tags and find latest version
latest_tag=""
latest_ver=""
# Get all tags for this repo
for entry in $(grep "^$repo|" "$RESULT_FILE"); do
tag=$(echo "$entry" | cut -d'|' -f2)
# Skip 'latest' tag for version comparison
if [ "$tag" != "latest" ]; then
version=$(extract_version "$tag")
if [ -n "$version" ]; then
# If first version or newer
if [ -z "$latest_ver" ] || [ "$(printf '%s\n%s' "$latest_ver" "$version" | sort -V | tail -n1)" = "$version" ]; then
latest_ver="$version"
latest_tag="$tag"
fi
fi
fi
done
# Only proceed if we found a version tag
if [ -n "$latest_tag" ]; then
# Find old versions to remove
remove_ids=""
remove_tags=""
for entry in $(grep "^$repo|" "$RESULT_FILE"); do
tag=$(echo "$entry" | cut -d'|' -f2)
id=$(echo "$entry" | cut -d'|' -f3)
if [ "$tag" != "$latest_tag" ] && [ "$tag" != "latest" ]; then
remove_ids="$remove_ids $id"
remove_tags="$remove_tags $tag"
fi
done
# Clean up the lists
remove_ids=$(echo "$remove_ids" | tr -s ' ' | sed 's/^ //')
remove_tags=$(echo "$remove_tags" | tr -s ' ' | sed 's/^ //')
# Only output if we found images to remove
if [ -n "$remove_ids" ]; then
echo "Repo: $repo"
echo " Latest: $latest_tag"
echo " Remove: $remove_tags"
echo " Command: podman image rm -f $remove_ids"
if [ "$DRYRUN" = false ]; then
# shellcheck disable=SC2086
podman image rm -f $remove_ids
fi
fi
fi
done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment