Files
homelab-optimized/ansible/automation/playbooks/prune_containers.yml
Gitea Mirror Bot 72afe8052c
Some checks failed
Documentation / Build Docusaurus (push) Failing after 5m0s
Documentation / Deploy to GitHub Pages (push) Has been skipped
Sanitized mirror from private repository - 2026-04-20 00:58:22 UTC
2026-04-20 00:58:22 +00:00

421 lines
14 KiB
YAML
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
# Docker Cleanup and Pruning Playbook
# Clean up unused containers, images, volumes, and networks
# Usage: ansible-playbook playbooks/prune_containers.yml
# Usage: ansible-playbook playbooks/prune_containers.yml -e "aggressive_cleanup=true"
# Usage: ansible-playbook playbooks/prune_containers.yml -e "dry_run=true"
- name: Docker System Cleanup and Pruning
hosts: "{{ host_target | default('all') }}"
gather_facts: yes
vars:
dry_run: "{{ dry_run | default(false) }}"
aggressive_cleanup: "{{ aggressive_cleanup | default(false) }}"
keep_images_days: "{{ keep_images_days | default(7) }}"
keep_volumes: "{{ keep_volumes | default(true) }}"
backup_before_cleanup: "{{ backup_before_cleanup | default(true) }}"
cleanup_logs: "{{ cleanup_logs | default(true) }}"
max_log_size: "{{ max_log_size | default('100m') }}"
tasks:
- name: Check if Docker is running
systemd:
name: docker
register: docker_status
failed_when: docker_status.status.ActiveState != "active"
- name: Create cleanup report directory
file:
path: "/tmp/docker_cleanup/{{ ansible_date_time.date }}"
state: directory
mode: '0755'
- name: Get pre-cleanup Docker system info
shell: |
echo "=== PRE-CLEANUP DOCKER SYSTEM INFO ==="
echo "Date: {{ ansible_date_time.iso8601 }}"
echo "Host: {{ inventory_hostname }}"
echo ""
echo "System Usage:"
docker system df
echo ""
echo "Container Count:"
echo "Running: $(docker ps -q | wc -l)"
echo "Stopped: $(docker ps -aq --filter status=exited | wc -l)"
echo "Total: $(docker ps -aq | wc -l)"
echo ""
echo "Image Count:"
echo "Total: $(docker images -q | wc -l)"
echo "Dangling: $(docker images -f dangling=true -q | wc -l)"
echo ""
echo "Volume Count:"
echo "Total: $(docker volume ls -q | wc -l)"
echo "Dangling: $(docker volume ls -f dangling=true -q | wc -l)"
echo ""
echo "Network Count:"
echo "Total: $(docker network ls -q | wc -l)"
echo "Custom: $(docker network ls --filter type=custom -q | wc -l)"
register: pre_cleanup_info
changed_when: false
- name: Display cleanup plan
debug:
msg: |
🧹 DOCKER CLEANUP PLAN
======================
🖥️ Host: {{ inventory_hostname }}
📅 Date: {{ ansible_date_time.date }}
🔍 Dry Run: {{ dry_run }}
💪 Aggressive: {{ aggressive_cleanup }}
📦 Keep Images: {{ keep_images_days }} days
💾 Keep Volumes: {{ keep_volumes }}
📝 Cleanup Logs: {{ cleanup_logs }}
{{ pre_cleanup_info.stdout }}
- name: Backup container list before cleanup
shell: |
backup_file="/tmp/docker_cleanup/{{ ansible_date_time.date }}/{{ inventory_hostname }}_containers_backup.txt"
echo "=== CONTAINER BACKUP - {{ ansible_date_time.iso8601 }} ===" > "$backup_file"
echo "Host: {{ inventory_hostname }}" >> "$backup_file"
echo "" >> "$backup_file"
echo "=== RUNNING CONTAINERS ===" >> "$backup_file"
docker ps --format "table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}" >> "$backup_file"
echo "" >> "$backup_file"
echo "=== ALL CONTAINERS ===" >> "$backup_file"
docker ps -a --format "table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.CreatedAt}}" >> "$backup_file"
echo "" >> "$backup_file"
echo "=== IMAGES ===" >> "$backup_file"
docker images --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}\t{{.CreatedAt}}" >> "$backup_file"
echo "" >> "$backup_file"
echo "=== VOLUMES ===" >> "$backup_file"
docker volume ls >> "$backup_file"
echo "" >> "$backup_file"
echo "=== NETWORKS ===" >> "$backup_file"
docker network ls >> "$backup_file"
when: backup_before_cleanup | bool
- name: Remove stopped containers
shell: |
{% if dry_run %}
echo "DRY RUN: Would remove stopped containers:"
docker ps -aq --filter status=exited
{% else %}
echo "Removing stopped containers..."
stopped_containers=$(docker ps -aq --filter status=exited)
if [ -n "$stopped_containers" ]; then
docker rm $stopped_containers
echo "✅ Removed stopped containers"
else
echo " No stopped containers to remove"
fi
{% endif %}
register: remove_stopped_containers
- name: Remove dangling images
shell: |
{% if dry_run %}
echo "DRY RUN: Would remove dangling images:"
docker images -f dangling=true -q
{% else %}
echo "Removing dangling images..."
dangling_images=$(docker images -f dangling=true -q)
if [ -n "$dangling_images" ]; then
docker rmi $dangling_images
echo "✅ Removed dangling images"
else
echo " No dangling images to remove"
fi
{% endif %}
register: remove_dangling_images
- name: Remove unused images (aggressive cleanup)
shell: |
{% if dry_run %}
echo "DRY RUN: Would remove unused images older than {{ keep_images_days }} days:"
docker images --filter "until={{ keep_images_days * 24 }}h" -q
{% else %}
echo "Removing unused images older than {{ keep_images_days }} days..."
old_images=$(docker images --filter "until={{ keep_images_days * 24 }}h" -q)
if [ -n "$old_images" ]; then
# Check if images are not used by any container
for image in $old_images; do
if ! docker ps -a --format "{{.Image}}" | grep -q "$image"; then
docker rmi "$image" 2>/dev/null && echo "Removed image: $image" || echo "Failed to remove image: $image"
else
echo "Skipping image in use: $image"
fi
done
echo "✅ Removed old unused images"
else
echo " No old images to remove"
fi
{% endif %}
register: remove_old_images
when: aggressive_cleanup | bool
- name: Remove dangling volumes
shell: |
{% if dry_run %}
echo "DRY RUN: Would remove dangling volumes:"
docker volume ls -f dangling=true -q
{% else %}
{% if not keep_volumes %}
echo "Removing dangling volumes..."
dangling_volumes=$(docker volume ls -f dangling=true -q)
if [ -n "$dangling_volumes" ]; then
docker volume rm $dangling_volumes
echo "✅ Removed dangling volumes"
else
echo " No dangling volumes to remove"
fi
{% else %}
echo " Volume cleanup skipped (keep_volumes=true)"
{% endif %}
{% endif %}
register: remove_dangling_volumes
- name: Remove unused networks
shell: |
{% if dry_run %}
echo "DRY RUN: Would remove unused networks:"
docker network ls --filter type=custom -q
{% else %}
echo "Removing unused networks..."
docker network prune -f
echo "✅ Removed unused networks"
{% endif %}
register: remove_unused_networks
- name: Clean up container logs
shell: |
{% if dry_run %}
echo "DRY RUN: Would clean up container logs larger than {{ max_log_size }}"
find /var/lib/docker/containers -name "*-json.log" -size +{{ max_log_size }} 2>/dev/null | wc -l
{% else %}
{% if cleanup_logs %}
echo "Cleaning up large container logs (>{{ max_log_size }})..."
log_count=0
total_size_before=0
total_size_after=0
for log_file in $(find /var/lib/docker/containers -name "*-json.log" -size +{{ max_log_size }} 2>/dev/null); do
if [ -f "$log_file" ]; then
size_before=$(stat -f%z "$log_file" 2>/dev/null || stat -c%s "$log_file" 2>/dev/null || echo 0)
total_size_before=$((total_size_before + size_before))
# Truncate log file to last 1000 lines
tail -1000 "$log_file" > "${log_file}.tmp" && mv "${log_file}.tmp" "$log_file"
size_after=$(stat -f%z "$log_file" 2>/dev/null || stat -c%s "$log_file" 2>/dev/null || echo 0)
total_size_after=$((total_size_after + size_after))
log_count=$((log_count + 1))
fi
done
if [ $log_count -gt 0 ]; then
saved_bytes=$((total_size_before - total_size_after))
echo "✅ Cleaned $log_count log files, saved $(echo $saved_bytes | numfmt --to=iec) bytes"
else
echo " No large log files to clean"
fi
{% else %}
echo " Log cleanup skipped (cleanup_logs=false)"
{% endif %}
{% endif %}
register: cleanup_logs_result
when: cleanup_logs | bool
- name: Run Docker system prune
shell: |
{% if dry_run %}
echo "DRY RUN: Would run docker system prune"
docker system df
{% else %}
echo "Running Docker system prune..."
{% if aggressive_cleanup %}
docker system prune -af --volumes
{% else %}
docker system prune -f
{% endif %}
echo "✅ Docker system prune complete"
{% endif %}
register: system_prune_result
- name: Get post-cleanup Docker system info
shell: |
echo "=== POST-CLEANUP DOCKER SYSTEM INFO ==="
echo "Date: {{ ansible_date_time.iso8601 }}"
echo "Host: {{ inventory_hostname }}"
echo ""
echo "System Usage:"
docker system df
echo ""
echo "Container Count:"
echo "Running: $(docker ps -q | wc -l)"
echo "Stopped: $(docker ps -aq --filter status=exited | wc -l)"
echo "Total: $(docker ps -aq | wc -l)"
echo ""
echo "Image Count:"
echo "Total: $(docker images -q | wc -l)"
echo "Dangling: $(docker images -f dangling=true -q | wc -l)"
echo ""
echo "Volume Count:"
echo "Total: $(docker volume ls -q | wc -l)"
echo "Dangling: $(docker volume ls -f dangling=true -q | wc -l)"
echo ""
echo "Network Count:"
echo "Total: $(docker network ls -q | wc -l)"
echo "Custom: $(docker network ls --filter type=custom -q | wc -l)"
register: post_cleanup_info
changed_when: false
- name: Generate cleanup report
copy:
content: |
🧹 DOCKER CLEANUP REPORT - {{ inventory_hostname }}
===============================================
📅 Cleanup Date: {{ ansible_date_time.iso8601 }}
🖥️ Host: {{ inventory_hostname }}
🔍 Dry Run: {{ dry_run }}
💪 Aggressive Mode: {{ aggressive_cleanup }}
📦 Image Retention: {{ keep_images_days }} days
💾 Keep Volumes: {{ keep_volumes }}
📝 Log Cleanup: {{ cleanup_logs }}
📊 BEFORE CLEANUP:
{{ pre_cleanup_info.stdout }}
🔧 CLEANUP ACTIONS:
🗑️ Stopped Containers:
{{ remove_stopped_containers.stdout }}
🖼️ Dangling Images:
{{ remove_dangling_images.stdout }}
{% if aggressive_cleanup %}
📦 Old Images:
{{ remove_old_images.stdout }}
{% endif %}
💾 Dangling Volumes:
{{ remove_dangling_volumes.stdout }}
🌐 Unused Networks:
{{ remove_unused_networks.stdout }}
{% if cleanup_logs %}
📝 Container Logs:
{{ cleanup_logs_result.stdout }}
{% endif %}
🧹 System Prune:
{{ system_prune_result.stdout }}
📊 AFTER CLEANUP:
{{ post_cleanup_info.stdout }}
💡 RECOMMENDATIONS:
- Schedule regular cleanup: cron job for this playbook
- Monitor disk usage: ansible-playbook playbooks/disk_usage_report.yml
- Consider log rotation: ansible-playbook playbooks/log_rotation.yml
{% if not aggressive_cleanup %}
- For more space: run with -e "aggressive_cleanup=true"
{% endif %}
✅ CLEANUP COMPLETE
dest: "/tmp/docker_cleanup/{{ ansible_date_time.date }}/{{ inventory_hostname }}_cleanup_report.txt"
- name: Display cleanup summary
debug:
msg: |
✅ DOCKER CLEANUP COMPLETE - {{ inventory_hostname }}
=============================================
🔍 Mode: {{ 'DRY RUN' if dry_run else 'LIVE CLEANUP' }}
💪 Aggressive: {{ aggressive_cleanup }}
📊 SUMMARY:
{{ post_cleanup_info.stdout }}
📄 Full report: /tmp/docker_cleanup/{{ ansible_date_time.date }}/{{ inventory_hostname }}_cleanup_report.txt
🔍 Next Steps:
{% if dry_run %}
- Run without dry_run to perform actual cleanup
{% endif %}
- Monitor: ansible-playbook playbooks/disk_usage_report.yml
- Schedule regular cleanup via cron
=============================================
- name: Restart Docker daemon if needed
systemd:
name: docker
state: restarted
when:
- restart_docker | default(false) | bool
- not dry_run | bool
register: docker_restart
- name: Verify services after cleanup
ansible.builtin.command: "docker ps --filter name={{ item }} --format '{{ '{{' }}.Names{{ '}}' }}'"
loop:
- plex
- immich-server
- vaultwarden
- grafana
- prometheus
register: service_checks
changed_when: false
failed_when: false
when:
- not dry_run | bool
- name: Display service verification
debug:
msg: "{{ service_verification.stdout }}"
when: service_verification is defined