πŸ›Ÿ An Undo Button for Your Proxmox Box

:page_facing_up: 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

:warning: 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.

:locked: Why this one’s actually safe to run as root

  • :clipboard: Defaults to LIST-ONLY β€” it won’t touch anything unless you explicitly pass --all or --vmid
  • :prohibited: No auto-delete, ever β€” old snapshots never get silently pruned. That’s a deliberate choice, not a missing feature.
  • :eyes: 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
  • :white_check_mark: Actually tested on a real 3-container Proxmox host β€” list, dry-run, real snapshot, manifest, verification all confirmed working
  • :receipt: Every run logs a manifest β€” a paper trail of what got snapshotted and when, under /var/log/bcbc-lxc-snapshots/

:rocket: 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

:locked_with_key: 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)

2 Likes