Skip to content

Instantly share code, notes, and snippets.

@akirak
Created August 18, 2025 08:34
Show Gist options
  • Save akirak/19fe1ff2b20e5541b06c13d2e74a6359 to your computer and use it in GitHub Desktop.
Save akirak/19fe1ff2b20e5541b06c13d2e74a6359 to your computer and use it in GitHub Desktop.
A shell script to recursively remove podman containers that depend on an image. Useful for cleaning up images on NixOS
#!/usr/bin/env bash
# Script to recursively remove podman images and their dependent containers
# Usage: ./podman_recursive_rm.sh <image_id_or_name> [max_iterations]
set -e
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Function to print colored output
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
log_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; }
log_warning() { echo -e "${YELLOW}[WARNING]${NC} $1"; }
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
# Function to extract container ID from error message
extract_container_id() {
local error_msg="$1"
# Extract the container ID from the error message
# Pattern matches 64-char or 12-char hex strings (full or short container IDs)
echo "$error_msg" | grep -oE '[a-f0-9]{64}|[a-f0-9]{12}' | head -n1
}
# Main recursive removal function
recursive_remove() {
local image_id="$1"
local max_iterations="$2"
local iteration=1
log_info "Starting recursive removal of image: $image_id (max iterations: $max_iterations)"
while [[ $iteration -le $max_iterations ]]; do
log_info "=== Iteration $iteration/$max_iterations ==="
# Try to remove the image
log_info "Attempting to remove image: $image_id"
local output
local exit_code=0
output=$(podman image rm "$image_id" 2>&1) || exit_code=$?
if [[ $exit_code -eq 0 ]]; then
log_success "Successfully removed image: $image_id"
echo "$output"
return 0
fi
# Check if the error is about container dependencies
if echo "$output" | grep -q "image.*used by\|image is in use by a container"; then
log_warning "Image is in use by containers"
echo "$output"
# Extract container ID from error message
local container_id
container_id=$(extract_container_id "$output")
if [[ -n "$container_id" ]]; then
log_info "Found dependent container: $container_id"
log_info "Removing container: $container_id"
if podman container rm "$container_id"; then
log_success "Successfully removed container: $container_id"
else
log_error "Failed to remove container: $container_id"
return 1
fi
else
log_error "Could not extract container ID from error message"
return 1
fi
else
log_error "Image removal failed with error:"
echo "$output"
return $exit_code
fi
((iteration++))
done
log_error "Failed to remove image after $max_iterations iterations"
return 1
}
# Function to show usage
show_usage() {
echo "Usage: $0 <image_id_or_name> [max_iterations]"
echo ""
echo "Arguments:"
echo " image_id_or_name Image ID or name to remove"
echo " max_iterations Maximum iterations (default: 10)"
echo ""
echo "Examples:"
echo " $0 nginx:latest"
echo " $0 3c7c1348fe6e 5"
echo " $0 myimage:v1.0"
}
# Main script
main() {
local image_id="$1"
local max_iterations="${2:-10}"
# Validate arguments
if [[ -z "$image_id" ]]; then
log_error "Image ID or name is required"
show_usage
exit 1
fi
if [[ "$image_id" == "-h" || "$image_id" == "--help" ]]; then
show_usage
exit 0
fi
if ! [[ "$max_iterations" =~ ^[0-9]+$ ]] || [[ "$max_iterations" -lt 1 ]]; then
log_error "Invalid iteration count: $max_iterations"
exit 1
fi
# Check if podman is available
if ! command -v podman &> /dev/null; then
log_error "podman command not found"
exit 1
fi
# Check if image exists
if ! podman image exists "$image_id" 2>/dev/null; then
log_error "Image does not exist: $image_id"
exit 1
fi
log_info "Configuration: Image=$image_id, Max iterations=$max_iterations"
# Run the recursive removal
if recursive_remove "$image_id" "$max_iterations"; then
log_success "Image and dependent containers removed successfully!"
exit 0
else
log_error "Failed to remove image and/or containers"
exit 1
fi
}
# Run main function with all arguments
main "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment