BCBC Technician Tools & Learning System Proxmox VE LXC Free / Inspectable

BCBC Proxmox Backup & LXC Snapshot Safety Toolkit

A practical pre-change safety workflow for Proxmox administrators: verify a real backup exists, create an LXC rollback checkpoint, record what happened, then begin the change.

Verify Before You Trust

BCBC publishes cryptographic SHA-256 checksums with downloadable releases, and we strongly recommend verifying them before using the files.

A matching checksum is strong evidence that the file you downloaded is bit-for-bit identical to the file represented by the trusted published checksum. It can detect corrupted downloads, incomplete transfers, accidental changes, the wrong build, or unexpected modification.

sha256sum -c SHA256SUMS

A checksum does not independently prove software is safe. Read the source, understand the commands, and obtain the checksum from a trusted BCBC release page, repository, or forum post.

Core Rule

COMPLETE BACKUP
+
LXC SNAPSHOT CHECKPOINT
+
MANIFEST / LOG

BEGIN CHANGE

Snapshots are not backups. A Proxmox LXC snapshot is a rollback checkpoint tied to the underlying storage. It does not replace an independent backup archive, off-host copy, or restore test.

What This Toolkit Does

The included bcbc-lxc-snapshot helper provides a deliberately small and inspectable interface around Proxmox pct snapshot. Its default action is read-only: it lists containers and snapshots. Snapshot creation requires an explicit target, and a dry-run mode lets you see the intended commands first.

ActionCommandEffect
List onlysudo bcbc-lxc-snapshot listRead-only inventory. This is the default behavior.
Dry-run allsudo bcbc-lxc-snapshot --all --dry-runShows planned snapshot commands without creating snapshots.
Snapshot one CTsudo bcbc-lxc-snapshot --vmid 123Creates a timestamped checkpoint for one LXC.
Snapshot all CTssudo bcbc-lxc-snapshot --allCreates a timestamped checkpoint for every listed LXC.
Custom prefixsudo bcbc-lxc-snapshot --vmid 123 --prefix pre-upgradeUses a readable prefix in the snapshot name.

What It Intentionally Does Not Do

This public starter does not automatically prune, delete, or rotate snapshots. Destructive cleanup deserves separate validation and should never be hidden inside a convenience command. It also does not pretend that snapshots are independent backups.

Recommended Proxmox Backup Layer

Use Proxmox vzdump or an equivalent supported backup mechanism to create an independent backup archive before risky changes. Store important backups away from the same failure domain when possible, and periodically prove that they can be restored.

# Example only — choose storage, retention, mode, and schedule for your environment.
vzdump 123 --mode snapshot --storage YOUR_BACKUP_STORAGE

# Inspect available storage first:
pvesm status
Do not copy an example backup command blindly. Confirm the target storage, available capacity, container ID, backup mode, retention policy, and restore plan for your own Proxmox environment.

Install the Snapshot Helper

sudo install -m 0755 bcbc-lxc-snapshot /usr/local/sbin/bcbc-lxc-snapshot

# Read-only first:
sudo bcbc-lxc-snapshot list

# Then dry-run:
sudo bcbc-lxc-snapshot --all --dry-run

# Only after checking the plan:
sudo bcbc-lxc-snapshot --all

Audit Trail

Each run writes a run log and a tab-separated manifest under:

/var/log/bcbc-lxc-snapshots/

That gives you a simple record of the timestamp, VMID, container name, snapshot name, and success/failure state. Logs should still be sanitized before public sharing because environment-specific names or infrastructure details may appear in them.

Safe Change Workflow

  1. Inventory. Confirm the target container(s), storage, and current state.
  2. Back up. Create or verify a current independent backup using your normal Proxmox backup process.
  3. Dry-run. Run the BCBC helper with --dry-run.
  4. Checkpoint. Create the intended LXC snapshot(s).
  5. Verify. Confirm the snapshot exists and review the generated manifest/log.
  6. Change. Begin the upgrade, configuration change, migration, or experiment.
  7. Validate. Test the service from the user's point of view, not only from the command line.
  8. Recover if needed. Use the appropriate rollback or restore method only after understanding its consequences.

Testing Status

The snapshot workflow was exercised on a real Proxmox host with three running LXC containers: list, dry-run, real snapshot creation, manifest generation, and post-run snapshot verification were all completed successfully. Private hostnames, addresses, and operational details are intentionally omitted from this public release.

Security & Public-Sharing Checklist

Before publishing logs, screenshots, container backups, or configuration, remove or regenerate sensitive and environment-specific material such as API keys, passwords, tokens, .env files, SSH keys, shell history, machine identity, private hostnames/IP addresses, session material, browser data, logs, caches, and test credentials.

For distributable LXC appliances: do not simply delete a few obvious secrets and call the container sanitized. Build a deliberate first-boot process that generates new identity and requires the new owner to supply their own network settings and credentials.

Complete Standalone Snapshot Helper Source

The complete helper is included below so the tool can be inspected before installation. Save it as bcbc-lxc-snapshot, review it, then make it executable.

Show complete Bash source
#!/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"

Release Philosophy

Build → Test → Document → Sanitize → Package → SHA-256 → Publish → Verify Download → Improve Together

Developers & Contributors Welcome

BCBC tools are released to be used, tested, inspected, learned from, improved, and shared. Developers, system administrators, students, educators, and other contributors are welcome.

If you find a bug, see a safer or cleaner implementation, want another platform supported, can improve documentation or tests, or have an idea that makes the tool more useful, please contribute or share constructive technical criticism.

Related Public BCBC Work

BCBC also publishes technical discussions, experiments, templates, and free tools in developer communities. Current references include the BCBC public GitHub profile, OneHack releases/discussions, and OpenAI Developer Community model-behavior evaluation work.