Full inspectable Bash script, list-only default, dry-run mode, manifest + log on every run, SHA-256 verified ZIP
Ever run a command on your Proxmox box and immediately think ββ¦I shouldβve snapshotted that firstβ? This is for exactly that moment β a small, inspectable toolkit that makes βundoβ possible before you make the change.
BACK UP FOR REAL (vzdump, separate storage)
β
SNAPSHOT CHECKPOINT (this tool)
β
MANIFEST + LOG SAVED
β
MAKE YOUR CHANGE
The trap this avoids: a snapshot lives on the SAME storage as your container. If that disk dies, your βbackupβ dies with it. Snapshots are a fast rollback, not a real backup β this toolkit forces you to do a proper vzdump backup separately, first.
Why this oneβs actually safe to run as root
Defaults to LIST-ONLY β it wonβt touch anything unless you explicitly pass --allor--vmid
No auto-delete, ever β old snapshots never get silently pruned. Thatβs a deliberate choice, not a missing feature.
Full source is right below β youβre not trusting a black-box zip with root access on your hypervisor, read exactly what it does first
Actually tested on a real 3-container Proxmox host β list, dry-run, real snapshot, manifest, verification all confirmed working
Every run logs a manifest β a paper trail of what got snapshotted and when, under /var/log/bcbc-lxc-snapshots/
Use it
sudo bcbc-lxc-snapshot list β see what you've got, nothing changes
sudo bcbc-lxc-snapshot --all --dry-run β preview what WOULD happen
sudo bcbc-lxc-snapshot --vmid 123 β checkpoint one container
sudo bcbc-lxc-snapshot --all β checkpoint everything listed
Install:
sudo install -m 0755 bcbc-lxc-snapshot /usr/local/sbin/bcbc-lxc-snapshot
Verify the download
SHA-256 of the release ZIP:
28cc3e74e8c3dc32ad37126ebb926eab478e5c442a1489073ad1ebf61011c43c
sha256sum BCBC-Proxmox-Backup-LXC-Snapshot-Safety-Toolkit-v1.0.0-2026-08-31.zip
A match confirms your download is byte-for-byte what got packaged and tested β it doesnβt prove the tool is safe on its own, so read the source below too.
π Full script source β bcbc-lxc-snapshot (read before you run it as root)
#!/usr/bin/env bash
set -Eeuo pipefail
PREFIX="bcbc"
MODE="list"
VMID=""
DRY_RUN=0
LOG_DIR="/var/log/bcbc-lxc-snapshots"
STAMP="$(date +%Y%m%d-%H%M%S)"
RUNLOG="${LOG_DIR}/run-${STAMP}.log"
MANIFEST="${LOG_DIR}/manifest-${STAMP}.tsv"
safe_name() {
local s="$1"
s="${s//[^A-Za-z0-9_-]/-}"
printf '%s' "${s:0:30}"
}
log() {
printf '%s %s\n' "$(date '+%F %T%z')" "$*" | tee -a "$RUNLOG"
}
need_root() {
[[ $EUID -eq 0 ]] || {
echo "ERROR: run as root on the Proxmox host." >&2
exit 1
}
}
need_cmds() {
command -v pct >/dev/null || {
echo "ERROR: pct not found. Run this on a Proxmox VE host." >&2
exit 1
}
}
list_one() {
local id="$1" name
name="$(pct config "$id" 2>/dev/null | awk -F': ' '$1=="hostname"{print $2;exit}')"
printf '\nCT %s %s\n' "$id" "${name:-unknown}"
pct listsnapshot "$id" 2>/dev/null || echo " (no snapshots or listing unavailable)"
}
list_all() {
pct list
while read -r id _; do
[[ "$id" =~ ^[0-9]+$ ]] || continue
list_one "$id"
done < <(pct list | tail -n +2)
}
snapshot_one() {
local id="$1" name snap desc
pct status "$id" >/dev/null 2>&1 || {
log "ERROR CT ${id}: not found"
return 1
}
name="$(pct config "$id" | awk -F': ' '$1=="hostname"{print $2;exit}')"
name="$(safe_name "${name:-ct$id}")"
snap="$(safe_name "${PREFIX}-${STAMP}")"
desc="BCBC checkpoint ${STAMP} host=${HOSTNAME} ct=${id} name=${name}"
log "CT ${id} (${name}): creating snapshot ${snap}"
if (( DRY_RUN )); then
log "DRY-RUN: pct snapshot ${id} ${snap} --description '${desc}'"
printf '%s\t%s\t%s\t%s\tDRY-RUN\n' \
"$STAMP" "$id" "$name" "$snap" >> "$MANIFEST"
return 0
fi
if pct snapshot "$id" "$snap" --description "$desc" >> "$RUNLOG" 2>&1; then
log "SUCCESS CT ${id}: ${snap}"
printf '%s\t%s\t%s\t%s\tSUCCESS\n' \
"$STAMP" "$id" "$name" "$snap" >> "$MANIFEST"
else
log "FAILED CT ${id}: ${snap}"
printf '%s\t%s\t%s\t%s\tFAILED\n' \
"$STAMP" "$id" "$name" "$snap" >> "$MANIFEST"
return 1
fi
}
while (($#)); do
case "$1" in
list)
MODE="list"
shift
;;
--all)
MODE="all"
shift
;;
--vmid)
MODE="one"
VMID="${2:-}"
shift 2
;;
--prefix)
PREFIX="$(safe_name "${2:-bcbc}")"
shift 2
;;
--dry-run)
DRY_RUN=1
shift
;;
-h|--help)
cat <<'EOF'
Usage:
bcbc-lxc-snapshot list
bcbc-lxc-snapshot --all [--dry-run] [--prefix NAME]
bcbc-lxc-snapshot --vmid ID [--dry-run] [--prefix NAME]
Safety:
Default action is LIST ONLY.
No snapshot is created unless --all or --vmid is explicitly supplied.
No pruning or deletion is implemented in this public starter.
EOF
exit 0
;;
*)
echo "Unknown option: $1" >&2
exit 2
;;
esac
done
need_root
need_cmds
mkdir -p "$LOG_DIR"
touch "$RUNLOG" "$MANIFEST"
chmod 750 "$LOG_DIR"
chmod 640 "$RUNLOG" "$MANIFEST"
if [[ "$MODE" == "list" ]]; then
list_all
exit 0
fi
fail=0
if [[ "$MODE" == "one" ]]; then
[[ "$VMID" =~ ^[0-9]+$ ]] || {
echo "ERROR: --vmid requires a numeric container ID." >&2
exit 2
}
snapshot_one "$VMID" || fail=1
else
mapfile -t ids < <(pct list | awk 'NR>1 && $1~/^[0-9]+$/{print $1}')
for id in "${ids[@]}"; do
snapshot_one "$id" || fail=1
done
fi
log "Manifest: ${MANIFEST}"
log "Run log: ${RUNLOG}"
exit "$fail"
Also in the ZIP: a single self-contained HTML guide, no framework or external JS needed, works offline.
Download: BCBC-Proxmox-Backup-LXC-Snapshot-Safety-Toolkit-v1.0.0-2026-08-31.zip (8.8 KB)
!