Essential Bash Scripts for Proxmox VE Administration
Ready-to-use bash scripts for Proxmox VE: bulk VM operations, backup cleanup, resource reporting, snapshot auditing, and cluster health checks.
Not every Proxmox automation task requires Terraform or Ansible. For quick, targeted operations — bulk starting VMs, cleaning old backups, or generating a resource report — a well-written bash script is often the most practical solution. This guide provides ready-to-use scripts that you can drop onto your Proxmox node and run immediately.
1. Bulk VM Operations
Start, stop, or snapshot multiple VMs at once, filtered by name pattern or VMID range:
#!/bin/bash
# bulk-vm-ops.sh — Perform actions on multiple VMs
# Usage: ./bulk-vm-ops.sh [start|stop|shutdown|snapshot] [pattern]
ACTION="${1:-status}"
PATTERN="${2:-.*}"
echo "=== Bulk VM Operation: $ACTION (filter: $PATTERN) ==="
echo "Timestamp: $(date '+%Y-%m-%d %H:%M:%S')"
echo ""
for VMID in $(qm list | awk 'NR>1 {print $1}'); do
VM_NAME=$(qm config "$VMID" | grep '^name:' | awk '{print $2}')
# Skip VMs that don't match the pattern
echo "$VM_NAME" | grep -qE "$PATTERN" || continue
case "$ACTION" in
start)
echo "Starting VM $VMID ($VM_NAME)..."
qm start "$VMID" 2>&1
;;
stop)
echo "Stopping VM $VMID ($VM_NAME)..."
qm stop "$VMID" 2>&1
;;
shutdown)
echo "Graceful shutdown VM $VMID ($VM_NAME)..."
qm shutdown "$VMID" --timeout 120 2>&1
;;
snapshot)
SNAP_NAME="bulk_$(date '+%Y%m%d_%H%M%S')"
echo "Snapshotting VM $VMID ($VM_NAME) as $SNAP_NAME..."
qm snapshot "$VMID" "$SNAP_NAME" --description "Bulk snapshot" 2>&1
;;
status)
STATUS=$(qm status "$VMID" | awk '{print $2}')
printf " VM %-6s %-25s %s\n" "$VMID" "$VM_NAME" "$STATUS"
;;
esac
done
echo ""
echo "Done."
2. Backup Cleanup Script
Automatically remove old backup files while keeping a configurable number of recent backups per VM:
#!/bin/bash
# backup-cleanup.sh — Remove old vzdump backups, keeping N most recent per VM
# Usage: ./backup-cleanup.sh [keep_count] [backup_dir]
KEEP=${1:-3}
BACKUP_DIR="${2:-/var/lib/vz/dump}"
DRY_RUN="${3:-false}"
echo "=== Backup Cleanup ==="
echo "Directory: $BACKUP_DIR"
echo "Keeping: $KEEP most recent per VM"
echo ""
TOTAL_FREED=0
# Find all unique VMIDs in backup filenames
for VMID in $(ls "$BACKUP_DIR"/vzdump-*-*.vma* 2>/dev/null | \
sed -E 's/.*vzdump-(qemu|lxc)-([0-9]+)-.*/\2/' | sort -u); do
# List backups for this VMID, newest first
BACKUPS=($(ls -t "$BACKUP_DIR"/vzdump-*-"${VMID}"-*.vma* 2>/dev/null))
TOTAL=${#BACKUPS[@]}
if [ "$TOTAL" -le "$KEEP" ]; then
echo "VM $VMID: $TOTAL backups (keeping all)"
continue
fi
DELETE_COUNT=$((TOTAL - KEEP))
echo "VM $VMID: $TOTAL backups, removing $DELETE_COUNT oldest"
# Remove the oldest backups (beyond the keep count)
for BACKUP_FILE in "${BACKUPS[@]:$KEEP}"; do
FILE_SIZE=$(stat -c%s "$BACKUP_FILE" 2>/dev/null || echo 0)
TOTAL_FREED=$((TOTAL_FREED + FILE_SIZE))
if [ "$DRY_RUN" = "true" ]; then
echo " [DRY RUN] Would remove: $(basename "$BACKUP_FILE")"
else
echo " Removing: $(basename "$BACKUP_FILE")"
rm -f "$BACKUP_FILE"
# Also remove associated .log and .notes files
rm -f "${BACKUP_FILE%.vma*}.log"
rm -f "${BACKUP_FILE%.vma*}.notes"
fi
done
done
echo ""
echo "Space freed: $((TOTAL_FREED / 1024 / 1024)) MB"
3. Resource Report Script
Generate a summary of CPU, memory, and disk usage across all VMs and containers:
#!/bin/bash
# resource-report.sh — Generate a resource utilization report
echo "=========================================="
echo " Proxmox Resource Report"
echo " Host: $(hostname)"
echo " Date: $(date '+%Y-%m-%d %H:%M:%S')"
echo "=========================================="
echo ""
# Host resources
echo "--- Host Resources ---"
echo "CPU Cores: $(nproc)"
echo "Load Average: $(uptime | awk -F'load average:' '{print $2}')"
free -h | awk '/^Mem:/ {printf "Memory: %s used / %s total (%s free)\n", $3, $2, $4}'
echo ""
# VM summary
echo "--- Virtual Machines ---"
printf "%-6s %-20s %-8s %-6s %-10s %-8s\n" "VMID" "NAME" "STATUS" "CORES" "MEMORY" "DISK"
echo "--------------------------------------------------------------"
for VMID in $(qm list | awk 'NR>1 {print $1}'); do
CONFIG=$(qm config "$VMID" 2>/dev/null)
NAME=$(echo "$CONFIG" | grep '^name:' | awk '{print $2}')
CORES=$(echo "$CONFIG" | grep '^cores:' | awk '{print $2}')
MEMORY=$(echo "$CONFIG" | grep '^memory:' | awk '{print $2}')
STATUS=$(qm status "$VMID" 2>/dev/null | awk '{print $2}')
# Get primary disk size
DISK=$(echo "$CONFIG" | grep -E '^(scsi|virtio|ide|sata)0:' | \
grep -oP 'size=\K[^,]+' | head -1)
printf "%-6s %-20s %-8s %-6s %-10s %-8s\n" \
"$VMID" "${NAME:--}" "$STATUS" "${CORES:-?}" "${MEMORY:-?}MB" "${DISK:--}"
done
echo ""
# Container summary
echo "--- LXC Containers ---"
printf "%-6s %-20s %-8s %-6s %-10s\n" "CTID" "NAME" "STATUS" "CORES" "MEMORY"
echo "----------------------------------------------"
for CTID in $(pct list | awk 'NR>1 {print $1}'); do
CONFIG=$(pct config "$CTID" 2>/dev/null)
NAME=$(echo "$CONFIG" | grep '^hostname:' | awk '{print $2}')
CORES=$(echo "$CONFIG" | grep '^cores:' | awk '{print $2}')
MEMORY=$(echo "$CONFIG" | grep '^memory:' | awk '{print $2}')
STATUS=$(pct status "$CTID" 2>/dev/null | awk '{print $2}')
printf "%-6s %-20s %-8s %-6s %-10s\n" \
"$CTID" "${NAME:--}" "$STATUS" "${CORES:-?}" "${MEMORY:-?}MB"
done
echo ""
# Storage summary
echo "--- Storage ---"
pvesm status 2>/dev/null | awk 'NR==1 || NR>1 {printf "%-15s %-10s %-10s %-10s %s\n", $1, $2, $3, $4, $5}'
4. Snapshot Audit Script
Find stale snapshots that are consuming disk space:
#!/bin/bash
# snapshot-audit.sh — Find old snapshots across all VMs and containers
MAX_AGE_DAYS=${1:-7}
echo "=== Snapshot Audit (older than $MAX_AGE_DAYS days) ==="
echo ""
FOUND=0
# Audit VM snapshots
for VMID in $(qm list | awk 'NR>1 {print $1}'); do
VM_NAME=$(qm config "$VMID" | grep '^name:' | awk '{print $2}')
SNAPSHOTS=$(qm listsnapshot "$VMID" 2>/dev/null | grep -v "current" | grep -v "^\`" | awk '{print $2}')
for SNAP in $SNAPSHOTS; do
[ -z "$SNAP" ] && continue
[ "$SNAP" = "current" ] && continue
FOUND=$((FOUND + 1))
echo " VM $VMID ($VM_NAME): snapshot '$SNAP'"
done
done
# Audit container snapshots
for CTID in $(pct list 2>/dev/null | awk 'NR>1 {print $1}'); do
CT_NAME=$(pct config "$CTID" | grep '^hostname:' | awk '{print $2}')
SNAPSHOTS=$(pct listsnapshot "$CTID" 2>/dev/null | grep -v "current" | awk '{print $2}')
for SNAP in $SNAPSHOTS; do
[ -z "$SNAP" ] && continue
[ "$SNAP" = "current" ] && continue
FOUND=$((FOUND + 1))
echo " CT $CTID ($CT_NAME): snapshot '$SNAP'"
done
done
echo ""
echo "Total snapshots found: $FOUND"
[ "$FOUND" -gt 0 ] && echo "Review and remove stale snapshots to reclaim disk space."
5. Cluster Health Check
A comprehensive health check script for Proxmox clusters:
#!/bin/bash
# cluster-health.sh — Quick cluster health overview
echo "=== Cluster Health Check ==="
echo "Date: $(date '+%Y-%m-%d %H:%M:%S')"
echo ""
# Cluster status
echo "--- Cluster Status ---"
pvecm status 2>/dev/null | grep -E "(Cluster|Quorum|Node|Expected|Total)"
echo ""
# Node status
echo "--- Node Status ---"
pvecm nodes 2>/dev/null
echo ""
# Check for failed services
echo "--- Service Health ---"
for SVC in pvedaemon pveproxy pvestatd corosync pve-cluster; do
STATUS=$(systemctl is-active "$SVC" 2>/dev/null)
printf " %-20s %s\n" "$SVC" "$STATUS"
done
echo ""
# HA status
echo "--- HA Status ---"
ha-manager status 2>/dev/null || echo " HA not configured"
echo ""
# Storage health
echo "--- Storage Health ---"
pvesm status 2>/dev/null | awk '{printf " %-15s %-10s %-8s\n", $1, $2, ($NF=="active" ? "OK" : $NF)}'
echo ""
# Ceph status (if applicable)
if command -v ceph &>/dev/null; then
echo "--- Ceph Status ---"
ceph health 2>/dev/null
echo ""
fi
echo "=== Check Complete ==="
Installation and Scheduling
# Copy scripts to the Proxmox node
scp *.sh root@192.168.1.100:/usr/local/bin/
# Make them executable
chmod +x /usr/local/bin/*.sh
# Schedule the backup cleanup weekly via cron
echo "0 3 * * 0 /usr/local/bin/backup-cleanup.sh 3 /var/lib/vz/dump >> /var/log/backup-cleanup.log 2>&1" | crontab -
# Run the health check on demand
/usr/local/bin/cluster-health.sh
These scripts give you direct, no-dependency automation for everyday Proxmox tasks. For a more visual overview of your cluster health and VM status on the go, ProxmoxR complements these command-line tools by putting key metrics on your phone. Together, scripts and mobile monitoring cover both scheduled automation and real-time visibility.
Take Proxmox management mobile
All the features discussed in this guide — accessible from your phone with ProxmoxR. Real-time monitoring, power control, firewall management, and more.