Skip to content

Instantly share code, notes, and snippets.

@gioxx
Last active July 31, 2025 13:32
Show Gist options
  • Select an option

  • Save gioxx/4a0ccc7505294ebdd80602069f6ed4db to your computer and use it in GitHub Desktop.

Select an option

Save gioxx/4a0ccc7505294ebdd80602069f6ed4db to your computer and use it in GitHub Desktop.
How to properly shutdown virtual machines and LXCs in Proxmox using qm/pct commands (and track operations in a log file).
#!/bin/bash
# Proxmox: Shutdown VMs and LXCs (GSolone, 2025)
# This script stops all running VMs and LXC containers, excluding specified IDs, and logs the operation.
# In DEBUG mode, it only stops one VM and one LXC container for testing purposes, ignoring exclusions.
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
logfile="/var/log/proxmox_shutdown.log"
LOCKFILE="/tmp/proxmox_shutdown.lock"
# Timestamp helper
timestamp() {
date +"%Y-%m-%d %H:%M:%S"
}
# Check for existing lock
if [ -e "$LOCKFILE" ]; then
echo "[$(timestamp)] Lockfile exists, exiting ..." >> $logfile
exit 1
fi
touch "$LOCKFILE"
trap "rm -f $LOCKFILE" EXIT
# === Config ===
EXCLUDED_VMS=""
EXCLUDED_LXCS=""
DEBUG=false
DEBUG_VM_ID="" # Replace with a test VM ID
DEBUG_LXC_ID="" # Replace with a test LXC ID
if [[ "$1" == "--debug" ]]; then
DEBUG=true
fi
IFS=',' read -ra SKIP_VMS <<< "$EXCLUDED_VMS"
IFS=',' read -ra SKIP_LXCS <<< "$EXCLUDED_LXCS"
echo "[$(timestamp)] Shutdown script started (DEBUG=$DEBUG)" >> $logfile
# === Shutdown VMs ===
for vmid in $(qm list | awk 'NR>1 && $3 == "running" {print $1}'); do
if [[ "$DEBUG" == true ]]; then
if [[ "$vmid" == "$DEBUG_VM_ID" ]]; then
echo "[$(timestamp)] [DEBUG] Shutting down test VM ID $vmid" >> $logfile
qm stop $vmid
break
else
continue
fi
fi
if [[ " ${SKIP_VMS[@]} " =~ " $vmid " ]]; then
echo "[$(timestamp)] Skipping VM ID $vmid" >> $logfile
continue
fi
echo "[$(timestamp)] Shutting down VM ID $vmid" >> $logfile
qm stop $vmid
done
# === Shutdown LXC ===
for ct in $(pct list | awk 'NR>1 && $2 == "running" {print $1}'); do
if [[ "$DEBUG" == true ]]; then
if [[ "$ct" == "$DEBUG_LXC_ID" ]]; then
echo "[$(timestamp)] [DEBUG] Shutting down test LXC Container ID $ct" >> $logfile
pct stop $ct
break
else
continue
fi
fi
if [[ " ${SKIP_LXCS[@]} " =~ " $ct " ]]; then
echo "[$(timestamp)] Skipping LXC Container ID $ct" >> $logfile
continue
fi
echo "[$(timestamp)] Shutting down LXC Container ID $ct" >> $logfile
pct stop $ct
done
echo "[$(timestamp)] Shutdown script completed" >> $logfile
#!/bin/bash
# Proxmox: Start VMs and LXCs (GSolone, 2025)
# This script starts all stopped VMs and LXC containers, excluding specified IDs, and logs the operation.
# In DEBUG mode, it only starts one VM and one LXC container for testing purposes, ignoring exclusions.
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
logfile="/var/log/proxmox_startup.log"
LOCKFILE="/tmp/proxmox_startup.lock"
# Timestamp helper
timestamp() {
date +"%Y-%m-%d %H:%M:%S"
}
# Check for existing lock
if [ -e "$LOCKFILE" ]; then
echo "[$(timestamp)] Lockfile exists, exiting ..." >> $logfile
exit 1
fi
touch "$LOCKFILE"
trap "rm -f $LOCKFILE" EXIT
# === Config ===
EXCLUDED_VMS=""
EXCLUDED_LXCS=""
DEBUG=false
DEBUG_VM_ID="" # Replace with a test VM ID
DEBUG_LXC_ID="" # Replace with a test LXC ID
if [[ "$1" == "--debug" ]]; then
DEBUG=true
fi
IFS=',' read -ra SKIP_VMS <<< "$EXCLUDED_VMS"
IFS=',' read -ra SKIP_LXCS <<< "$EXCLUDED_LXCS"
echo "[$(timestamp)] Startup script started (DEBUG=$DEBUG)" >> $logfile
# === Start VMs ===
for vmid in $(qm list | awk 'NR>1 && $3 == "stopped" {print $1}'); do
if [[ "$DEBUG" == true ]]; then
if [[ "$vmid" == "$DEBUG_VM_ID" ]]; then
echo "[$(timestamp)] [DEBUG] Starting test VM ID $vmid" >> $logfile
qm start $vmid
break
else
continue
fi
fi
if [[ " ${SKIP_VMS[@]} " =~ " $vmid " ]]; then
echo "[$(timestamp)] Skipping VM ID $vmid" >> $logfile
continue
fi
echo "[$(timestamp)] Starting VM ID $vmid" >> $logfile
qm start $vmid
done
# === Start LXC ===
for ct in $(pct list | awk 'NR>1 && $2 == "stopped" {print $1}'); do
if [[ "$DEBUG" == true ]]; then
if [[ "$ct" == "$DEBUG_LXC_ID" ]]; then
echo "[$(timestamp)] [DEBUG] Starting test LXC Container ID $ct" >> $logfile
pct start $ct
break
else
continue
fi
fi
if [[ " ${SKIP_LXCS[@]} " =~ " $ct " ]]; then
echo "[$(timestamp)] Skipping LXC Container ID $ct" >> $logfile
continue
fi
echo "[$(timestamp)] Starting LXC Container ID $ct" >> $logfile
pct start $ct
done
echo "[$(timestamp)] Startup script completed" >> $logfile
@gioxx
Copy link
Author

gioxx commented Jul 31, 2025

Changelog 25-07-31:

  • PATH management (cron compatibility)
  • Lockfile to prevent concurrent execution
  • Dynamically updated timestamp for each log entry
  • DEBUG mode for safe testing
  • Custom VM/LXC exclusion list

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment