85°F Knoxville
Mon–Fri 9–5 ETAnswered by a human
Infrastructure · IMPLEMENTED

Automated Proxmox Host Configuration Backups to SMB Share

Deployed automated monthly backup solution for Proxmox VE host configurations using bash script and cron.monthly.

Summary

Rolled out automated monthly backups of Proxmox VE host configuration to a centralized SMB share across a multi-node cluster. Bash script in cron.monthly, tar.gz archives of the directories that actually matter when a host dies, 6 archives of retention per directory, automatic cleanup. Because if you can't rebuild the host, you can't restore the VMs running on it.

The challenge

Proxmox has excellent built-in backup for VMs and containers. Host-level config is the part everyone forgets until a node dies and they're staring at a fresh install trying to remember whether the bond was LACP or active-backup, or what the SMB credentials for the backup NAS were. The stuff you actually need to rebuild a Proxmox host includes:

  • network config — bridges, bonds, VLANs
  • custom scripts and tooling in /root and /opt
  • cluster certificates and membership data
  • storage mount points and their credentials
  • any /etc customizations — sysctl tweaks, systemd units, cron entries

Without host-level backups, rebuilding a failed node means reconstructing all of that from memory or from documentation that's probably out of date. Neither is fast, neither is reliable, and both happen at 2 AM.

Solution architecture

Bash script in /etc/cron.monthly/ runs on the first of every month, tars up the critical directories into timestamped archives, and drops them on an SMB share. Old archives get pruned automatically so storage stays bounded.

  • monthly execution via cron.monthly
  • central SMB share reachable by every Proxmox host
  • timestamped archives for point-in-time recovery
  • keep the last 6 per directory, delete the rest
  • logs everything to /var/log/proxmox_config_backup.log
  • pre-flight check bails out if the SMB share isn't mounted instead of silently dumping backups to local disk

Directories backed up

  • /etc — network config, storage mounts, services, sysctl, systemd, everything else
  • /root — admin home: custom scripts, SSH keys, tooling
  • /opt — third-party: monitoring agents, custom apps
  • /var/lib/pve-cluster — cluster database: VM configs, resource pools, user permissions, datacenter settings, cluster membership

The script

Placed in /etc/cron.monthly/ so cron runs it on the first of each month without anything extra in crontab.

proxmox_config_to_nas
#!/bin/bash

# Define backup location (Proxmox SMB mount point)
SMB_FOLDER="/mnt/pve/proxmox_hosts"
LOG_FILE="/var/log/proxmox_config_backup.log"
TIMESTAMP=$(date +'%Y-%m-%d_%I-%M-%S%p')
HOSTNAME=$(hostname)

# Directories to back up (Absolute Paths)
DIRS_TO_BACKUP=(
  "/etc"
  "/root"
  "/opt"
  "/var/lib/pve-cluster"
)

# Ensure SMB mount is available before proceeding
if ! mount | grep -q "$SMB_FOLDER"; then
  echo "$(date +'%Y-%m-%d %H:%M:%S') - ERROR: SMB Share ${SMB_FOLDER} not mounted!" | tee -a "${LOG_FILE}"
  exit 1
fi

echo "$(date +'%Y-%m-%d %H:%M:%S') - Starting Proxmox config backup for ${HOSTNAME}..." | tee -a "${LOG_FILE}"

# Backup each directory
for dir in "${DIRS_TO_BACKUP[@]}"; do
  if [ -d "$dir" ]; then
    BACKUP_FILE="${SMB_FOLDER}/${HOSTNAME}_$(basename $dir)_${TIMESTAMP}.tgz"
    echo "$(date +'%Y-%m-%d %H:%M:%S') - Backing up ${dir} to ${BACKUP_FILE}" | tee -a "${LOG_FILE}"

    tar --ignore-failed-read -czf "${BACKUP_FILE}" "${dir}" \
      && echo "$(date +'%Y-%m-%d %H:%M:%S') - Backup successful: ${BACKUP_FILE}" | tee -a "${LOG_FILE}" \
      || echo "$(date +'%Y-%m-%d %H:%M:%S') - ERROR: Failed to backup ${dir}" | tee -a "${LOG_FILE}"
  else
    echo "$(date +'%Y-%m-%d %H:%M:%S') - INFO: Skipping ${dir} (not found)" | tee -a "${LOG_FILE}"
  fi
done

# Cleanup: Keep only the last 6 backups per directory
echo "$(date +'%Y-%m-%d %H:%M:%S') - Cleaning up old backups..." | tee -a "${LOG_FILE}"
for dir in "${DIRS_TO_BACKUP[@]}"; do
  BACKUP_PREFIX="${SMB_FOLDER}/${HOSTNAME}_$(basename $dir)_"
  ls -t ${BACKUP_PREFIX}*.tgz | tail -n +7 | xargs rm -f 2>/dev/null
  echo "$(date +'%Y-%m-%d %H:%M:%S') - Cleanup done for ${dir}" | tee -a "${LOG_FILE}"
done

echo "$(date +'%Y-%m-%d %H:%M:%S') - Backup completed successfully for ${HOSTNAME}." | tee -a "${LOG_FILE}"

Key bits:

  • pre-flight check: if the SMB share isn't mounted, it logs an error and exits instead of silently dumping backups to local disk
  • --ignore-failed-read: keeps tar going if it hits something it can't read (open files, sockets) instead of aborting the whole archive
  • cleanup loop uses ls -t | tail -n +7 to keep the 6 most recent per directory and delete the rest

File naming

Format is {hostname}{directory}{timestamp}.tgz. From a single host you end up with:

  • proxmox01_etc_2025-01-28_11-06-13PM.tgz
  • proxmox01_root_2025-01-28_11-06-13PM.tgz
  • proxmox01_opt_2025-01-28_11-06-13PM.tgz
  • proxmox01_pve-cluster_2025-01-28_11-06-13PM.tgz

Implementation steps

  1. Mount the SMB share persistently. Add to /etc/fstab with a credentials file (not a plaintext password in the fstab line). Create /root/.smbcredentials with permissions 600 containing username/password, then reference it with credentials=/root/.smbcredentials. Verify with ls /mnt/pve/proxmox_hosts.
  2. Drop the script in /etc/cron.monthly/proxmox_config_to_nas and chmod +x it.
  3. Run it manually once. Confirm archives appear on the share and /var/log/proxmox_config_backup.log shows the expected output.
  4. If scripts in cron.monthly aren't firing on your install (it happens), add an explicit crontab entry: 0 2 1 * * root /etc/cron.monthly/proxmox_config_to_nas >/dev/null 2>&1, then systemctl restart cron.

Multi-host deployment

The script uses $(hostname) everywhere the hostname appears, so it's host-agnostic. Deploy the exact same file to every Proxmox node — backups land on the shared SMB target already namespaced by hostname, retention is managed per host, and nothing needs per-node customization.

Outcome

Every host in the cluster gets its config snapshotted monthly, with six months of retention, to one SMB share. Zero ongoing maintenance. If we lose a node, the rebuild path is: install Proxmox, restore /etc, /root, /opt, and /var/lib/pve-cluster from the latest archive, rejoin the cluster. The VMs — already backed up by Proxmox's built-in tooling — land back on the rebuilt host and come up.

  • zero ongoing maintenance, once per month log check in monitoring
  • rapid rebuild capability covering everything the Proxmox installer doesn't
  • same script across the cluster, no per-host customization
  • timestamped archives give you an audit trail and the ability to roll back a config change

Key takeaways