Да, все верно, ошибка в скрипте, спасибо за найденный баг. Я поправил в после, не использовал эту переменную, поэтому не заметил ошибку.
В качестве логического продолжения темы выкладываю описание сценраия восстанволения конфигурации PVE из бэкапа.
Вводные:
- Сценарий описывает восстановление из бэкапа для однонодовой архитектуры single node (без кластера)
- Исходный скрипт
pbs_backup.shавтора статьи был модифицирован (все изменения сопровождены комментариями) - Скорректированы настройки в
pbs_backup_settings - В моём случае файлы с настройками и скриптами расположены в директории:
/root/pbs/scripts - Команды выполняются с учётом использования Namespace=“
hosts”, в соответствии с исходной статьёй Романа. При необходимости используйте свои значения.
Бэкапирование
Изменение директорий для бэкапирования в pbs_backup_settings
Из исходной конфигурации автора удалил corosync.pxar:/etc/pve, т.к. буду бэкапить конкретные файлы из этой директории, а не всю директорию.
Целевой вариант:
BACKUPS="\
etc.pxar:/etc \
crontab.pxar:/var/spool/cron/ \
pmxcfs-SQLite-PVE.pxar:/root/pbs/pmxcfs \
etc-pve-configs.pxar:/root/pbs/etc-pve \
scripts.pxar:/root/pbs/scripts \
"
Где:
# etc.pxar:/etc # Все системные конфигурации
# crontab.pxar:/root/pbs/cron # Конфиг crontab
# pmxcfs-SQLite-PVE.pxar:/root/pbs/pmxcfs # БД PVE SQLite (pmxcfs)
# etc-pve-configs_.pxar:/root/pbs/etc-pve # Конфиги storage, datacenter, user (для просмотра, не для recovery)
# scripts.pxar:/root/pbs/scripts # Скрипты для PBS
Изменение скрипта pbs_backup.sh
Перед командами экспорта содержимого директорий $BACKUPS в PBS добавлено:
# PVE database and "/etc/pve" configs backup
mkdir -p /root/pbs/{pmxcfs,scripts,etc-pve}
cp -a /etc/pve/{storage.cfg,datacenter.cfg,user.cfg} /root/pbs/etc-pve/
sqlite3 /var/lib/pve-cluster/config.db ".backup /root/pbs/pmxcfs/config.db"
Что изменилось:
mkdir- создаём структуру директорий, которая соответствует переменной$BACKUPScp -a- копируем файлы конфигурации (storage.cfg,datacenter.cfg,user.cfg) из/etc/pvesqlite3- делаем бэкап настроек PVE из БД SQLite штатным средствами SQLite
После процедуры экспорта бэкапов в PBS добавлено удаление временных директорий:
# Clearing the temporary directory with configs
rm -rf /root/pbs/{etc-pve,pmxcfs}
Итоговый скрипт
#!/bin/bash
set -euo pipefail
# Function to check files
check_files() {
for f in "$@"; do
if [[ ! -f "$f" ]]; then
echo "Error: file not found: $f" >&2
exit 1
fi
done
}
# Function to check environment variables
check_env() {
for var in "$@"; do
if [[ -z "${!var:-}" ]]; then
echo "Error: environment variable not set: $var" >&2
exit 1
fi
done
}
dir=$(dirname $0)
check_files "$dir/pbs_backup_secrets" "$dir/pbs_backup_settings"
. $dir/pbs_backup_secrets
. $dir/pbs_backup_settings
check_env "BACKUPS" "PBS_REPOSITORY" "PBS_PASSWORD" "PBS_FINGERPRINT" "DETECTION_MODE"
if [[ -z "$BACKUP_ID" ]]; then
BACKUP_ID=$(hostname)
fi
ts=$(date -u +%s)
iso=$(date -u -d "@$ts" +"%Y-%m-%dT%H:%M:%SZ")
SNAPSHOT="host/${BACKUP_ID}/${iso}"
if [ -n "$NS" ]; then
ns_op="--ns ${NS}"
else
ns_op=""
fi
excludes=""
for item in $EXCLUDE ; do
excludes="${excludes} --exclude ${item}"
done
BACKUPS="$BACKUPS pbs_backup.conf:$dir/pbs_backup_settings"
export PBS_PASSWORD
export PBS_FINGERPRINT
# PVE database and "/etc/pve" configs backup
mkdir -p /root/pbs/{pmxcfs,scripts,etc-pve}
cp -a /etc/pve/{storage.cfg,datacenter.cfg,user.cfg} /root/pbs/etc-pve/
sqlite3 /var/lib/pve-cluster/config.db ".backup /root/pbs/pmxcfs/config.db"
$PBC \
backup \
$BACKUPS \
$excludes \
--backup-time ${ts} \
--backup-id $BACKUP_ID \
${ns_op} \
--change-detection-mode $DETECTION_MODE \
--repository $PBS_REPOSITORY \
$EXTRA_ARGS \
> $LOG_FILE 2>&1
# Clearing the temporary directory with configs
rm -rf /root/pbs/{etc-pve,pmxcfs}
if [[ -n "$LOG_FILE" ]]; then
$PBC snapshot upload-log $SNAPSHOT $LOG_FILE ${ns_op} --repository ${PBS_REPOSITORY} >/dev/null 2>&1
fi
if [[ -n "$NOTES" ]]; then
$PBC snapshot notes update $SNAPSHOT $NOTES ${ns_op} --repository ${PBS_REPOSITORY} >/dev/null 2>&1
fi
Восстановление
Подготовить директории для монтирования
mkdir -p /mnt/restore/{etc,pmxcfs,etc-pve,scripts}
mkdir -p /root/pbs/scripts
Задать параметры PBS-репозитория для текущей сессии в shell
export PBS_REPOSITORY="YOUR_PBS_REPOSITORY"
export PBS_NAMESPACE="hosts"
Проверить присвоение переменных
echo "PBS_REPOSITORY=$PBS_REPOSITORY / PBS_NAMESPACE=$PBS_NAMESPACE"
Пример вывода:
PBS_REPOSITORY=root@pam@192.168.1.198:backups / PBS_NAMESPACE=hosts
Вывести список бэкапов
proxmox-backup-client snapshot list --ns hosts
Пример вывода:
Password for "root@pam": ******
fingerprint: 83:cc:c3:63:c1:dd:12:fd:83:3b:f1:47:bc:47:f2:03:90:0d:f8:bf:e0:75:e0:2e:15:4a:97:bb:87:5d:16:ed
Are you sure you want to continue connecting? (y/n): y
fingerprint: 83:cc:c3:63:c1:dd:12:fd:83:3b:f1:47:bc:47:f2:03:90:0d:f8:bf:e0:75:e0:2e:15:4a:97:bb:87:5d:16:ed
Are you sure you want to continue connecting? (y/n): y
y
┌───────────────────────────────────┬───────────┬─────────────────────────────────────────────────────────────────────────────────────────────────────────────
│ snapshot │ size │ files
╞═══════════════════════════════════╪═══════════╪═════════════════════════════════════════════════════════════════════════════════════════════════════════════
│ host/sandbox/2026-03-22T08:35:09Z │ 2.469 MiB │ client.log etc-pve-configs.mpxar etc-pve-configs.ppxar etc.mpxar etc.ppxar index.json pbs_backup.conf pmxcfs
├───────────────────────────────────┼───────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────
│ host/sandbox/2026-03-22T09:03:45Z │ 2.469 MiB │ client.log etc-pve-configs.mpxar etc-pve-configs.ppxar etc.mpxar etc.ppxar index.json pbs_backup.conf pmxcfs
├───────────────────────────────────┼───────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────
│ host/sandbox/2026-03-22T09:03:47Z │ 2.469 MiB │ client.log etc-pve-configs.mpxar etc-pve-configs.ppxar etc.mpxar etc.ppxar index.json pbs_backup.conf pmxcfs
├───────────────────────────────────┼───────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────
│ host/sandbox/2026-03-22T09:05:18Z │ 2.469 MiB │ client.log etc-pve-configs.mpxar etc-pve-configs.ppxar etc.mpxar etc.ppxar index.json pbs_backup.conf pmxcfs
├───────────────────────────────────┼───────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────
│ host/sandbox/2026-03-22T09:05:19Z │ 2.41 MiB │ client.log etc-pve-configs.mpxar etc-pve-configs.ppxar etc.mpxar etc.ppxar index.json pbs_backup.conf pmxcfs
├───────────────────────────────────┼───────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────
│ host/sandbox/2026-03-22T10:09:43Z │ 2.396 MiB │ client.log etc-pve-configs.mpxar etc-pve-configs.ppxar etc.mpxar etc.ppxar index.json pbs_backup.conf pmxcfs
├───────────────────────────────────┼───────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────
│ host/sandbox/2026-03-22T16:59:19Z │ 2.398 MiB │ client.log crontab.mpxar crontab.ppxar etc-pve-configs.mpxar etc-pve-configs.ppxar etc.mpxar etc.ppxar index
└───────────────────────────────────┴───────────┴─────────────────────────────────────────────────────────────────────────────────────────────────────────────
Вывести список файлов в целевом бэкаппе, чтобы убедиться что бэкап читается (необязательный шаг)
proxmox-backup-client snapshot files host/sandbox/2026-03-22T16:59:19Z --ns hosts
Пример вывода:
┌──────────────────────────────┬────────────┬─────────┐
│ filename │ crypt-mode │ size │
╞══════════════════════════════╪════════════╪═════════╡
│ client.log.blob │ │ │
├──────────────────────────────┼────────────┼─────────┤
│ crontab.mpxar.didx │ none │ 398 │
├──────────────────────────────┼────────────┼─────────┤
│ crontab.ppxar.didx │ none │ 1182 │
├──────────────────────────────┼────────────┼─────────┤
│ etc-pve-configs.mpxar.didx │ none │ 540 │
├──────────────────────────────┼────────────┼─────────┤
│ etc-pve-configs.ppxar.didx │ none │ 1082 │
├──────────────────────────────┼────────────┼─────────┤
│ etc.mpxar.didx │ none │ 249446 │
├──────────────────────────────┼────────────┼─────────┤
│ etc.ppxar.didx │ none │ 2192132 │
├──────────────────────────────┼────────────┼─────────┤
│ index.json.blob │ none │ 861 │
├──────────────────────────────┼────────────┼─────────┤
│ pbs_backup.conf.blob │ none │ 834 │
├──────────────────────────────┼────────────┼─────────┤
│ pmxcfs-SQLite-PVE.mpxar.didx │ none │ 258 │
├──────────────────────────────┼────────────┼─────────┤
│ pmxcfs-SQLite-PVE.ppxar.didx │ none │ 61488 │
├──────────────────────────────┼────────────┼─────────┤
│ scripts.mpxar.didx │ none │ 707 │
├──────────────────────────────┼────────────┼─────────┤
│ scripts.ppxar.didx │ none │ 5282 │
└──────────────────────────────┴────────────┴─────────┘
Смонтировать архивы из PBS в PVE (вместо демонстрационного названия бэкапа host/sandbox/2026-03-22T16:59:19Z укажите свой в соответствии с информацией из пунктов выше)
proxmox-backup-client mount host/sandbox/2026-03-22T16:59:19Z etc.pxar /mnt/restore/etc --ns hosts
proxmox-backup-client mount host/sandbox/2026-03-22T16:59:19Z crontab.pxar /mnt/restore/cron --ns hosts
proxmox-backup-client mount host/sandbox/2026-03-22T16:59:19Z pmxcfs-SQLite-PVE.pxar /mnt/restore/pmxcfs --ns hosts
proxmox-backup-client mount host/sandbox/2026-03-22T16:59:19Z etc-pve-configs.pxar /mnt/restore/etc-pve --ns hosts
proxmox-backup-client mount host/sandbox/2026-03-22T16:59:19Z scripts.pxar /mnt/restore/scripts --ns hosts
Пример вывода:
FUSE library version: 3.17.2
FUSE library version: 3.17.2
FUSE library version: 3.17.2
FUSE library version: 3.17.2
FUSE library version: 3.17.2
Остановить сервис кластера Proxmox VE
systemctl stop pve-cluster
Восстановить файлы конфигурации PVE из бэкапа
rsync -aHAX --numeric-ids /mnt/restore/etc/ /etc/
rsync -aHAX --numeric-ids /mnt/restore/cron/ /var/spool/cron/
rsync -aHAX --numeric-ids /mnt/restore/pmxcfs/ /var/lib/pve-cluster/
rsync -aHAX --numeric-ids /mnt/restore/scripts/ /root/pbs/scripts/
Запустить сервис кластера Proxmox VE
systemctl start pve-cluster
Проверить сервис кластера Proxmox VE
systemctl status pve-cluster
Пример вывода:
● pve-cluster.service - The Proxmox VE cluster filesystem
Loaded: loaded (/usr/lib/systemd/system/pve-cluster.service; enabled; preset: enabled)
Active: active (running) since Sun 2026-03-22 20:17:42 MSK; 3s ago
Invocation: f97a24b3f64049f8b4484838e6733b82
Process: 2471 ExecStart=/usr/bin/pmxcfs (code=exited, status=0/SUCCESS)
Main PID: 2472 (pmxcfs)
Tasks: 5 (limit: 9351)
Memory: 23.7M (peak: 24.2M)
CPU: 34ms
CGroup: /system.slice/pve-cluster.service
└─2472 /usr/bin/pmxcfs
Mar 22 20:17:41 sandbox systemd[1]: Starting pve-cluster.service - The Proxmox VE cluster filesystem...
Mar 22 20:17:41 sandbox pmxcfs[2471]: [main] notice: resolved node name 'sandbox' to '192.168.1.220' for default node IP address
Mar 22 20:17:41 sandbox pmxcfs[2471]: [main] notice: resolved node name 'sandbox' to '192.168.1.220' for default node IP address
Mar 22 20:17:42 sandbox systemd[1]: Started pve-cluster.service - The Proxmox VE cluster filesystem.
Размонтировать архивы из PBS
umount /mnt/restore/{etc,pmxcfs,etc-pve,cron,scripts}
Удалить временные директории
rm -rf /mnt/restore
Важно
Архив etc-pve-configs.pxar восстанавливать не нужно, т.к. директория /etc/pve является примонтированной FUSE файловой системой (некое представление SQLite БД config.db) и любая попытка перезаписать файлы в этой директории приводит к поломке службы pve-cluster.
Данный архив нужен для того, чтобы была возможность прочитать файлы конфигурации и перенести настройки вручную в PVE при необходимости.
Скрипт восстановления бэкапа
Все вышеописанные действия свёл в скрипт.
Что делает:
- Предлагает удалить файл скрипта после восстановления бэкапа
- Предлагает выполнить обновление системы (отключает Ceph и enterprise репозитории, подключает no-subscription)
- Создаёт и удаляет временные директории после восстановления бэкапа
- Подключается к PBS, получает последний актуальный бэкап и монтирует его в временные директории в PVE
- Выполняет корректное восстановление бэкапа, после чего размонтирует бэкап.
- Очищает конфиг файлы для VM и CT, которые вы отдельно можете восстановить из бэкапов.
Как запустить:
- Создать файл в любой удобной директории в PVE, например:
nano /home/restore.sh
- Сделать файл исполняемым
chmod 700 /home/restore.sh
- Скопировать скрипт в файл restore.sh
- Скорректировать переменные в шапке скрипта (да, указывать авторизационные данные не секьюрно, но исходим из того, что скрипт исполняем 1 раз, после чего он автоматически удаляется из системы, в связи с чем городить отдельный файл с секретами нецелесообразно)
PBS_PASSWORD="YOUR_PASSWORD" # PBS password
PBS_REPOSITORY="YOUR_PBS_REPOSITORY" # PBS Repository
PBS_FINGERPRINT="YOUR_PBS_FINGERPRINT" # PBS fingerprint
NS="hosts" # PBS Namespace
RESTORE_DIR="/mnt/restore" # Temporary restore directory
- Запустить скрипт
/home/restore.sh
Сам скрипт:
#!/bin/bash
# ============================
# Paths and settings
# ============================
PBS_PASSWORD="YOUR_PASSWORD" # PBS password
PBS_REPOSITORY="YOUR_PBS_REPOSITORY" # PBS Repository
PBS_FINGERPRINT="YOUR_PBS_FINGERPRINT" # PBS fingerprint
NS="hosts" # PBS Namespace
RESTORE_DIR="/mnt/restore" # Temporary restore directory
# ============================
# Step check function
# ============================
check_step() {
if [ $? -eq 0 ]; then
echo -e "\e[32m✅ $1\e[0m"
else
echo -e "\e[31m❌ $1 (error)\e[0m"
exit 1
fi
}
# ============================
# Load PBS settings
# ============================
export PBS_PASSWORD
export PBS_REPOSITORY
export PBS_FINGERPRINT
# ============================
# 1. System update with confirmation
# ============================
# Allow script self-removal after execution
read -p "Delete the script after execution? [y/N]: " selfdel_choice
read -p "Do you want to update the system (apt update && apt upgrade -y)? [y/N]: " update_choice
if [[ "$update_choice" =~ ^[Yy]$ ]]; then
echo "Preparing system update..."
# ============================
# 1a. Disable enterprise repositories
# ============================
# Disable Ceph repo by adding Enabled: false (if not already present)
if grep -q "^Enabled: false" /etc/apt/sources.list.d/ceph.sources 2>/dev/null; then
echo "✅ Ceph repository already disabled"
else
echo "✅ Disabling Ceph enterprise repository..."
echo "Enabled: false" >> /etc/apt/sources.list.d/ceph.sources
fi
# Disable PVE enterprise repo
if grep -q "^Enabled: false" /etc/apt/sources.list.d/pve-enterprise.sources 2>/dev/null; then
echo "✅ PVE enterprise repository already disabled"
else
echo "✅ Disabling PVE enterprise repository..."
echo "Enabled: false" >> /etc/apt/sources.list.d/pve-enterprise.sources
fi
# ============================
# 1b. Add or enable public Proxmox repository
# ============================
PROXMOX_REPO_FILE="/etc/apt/sources.list.d/proxmox.sources"
if [ ! -f "$PROXMOX_REPO_FILE" ]; then
echo "✅ Adding public Proxmox repository..."
cat <<EOF > "$PROXMOX_REPO_FILE"
Types: deb
URIs: http://download.proxmox.com/debian/pve
Suites: trixie
Components: pve-no-subscription
Signed-By: /usr/share/keyrings/proxmox-archive-keyring.gpg
EOF
else
if grep -q "^Enabled: false" "$PROXMOX_REPO_FILE"; then
echo "✅ Proxmox repository exists but is disabled. Enabling..."
sed -i '/^Enabled: false/d' "$PROXMOX_REPO_FILE"
else
echo "✅ Public Proxmox repository already exists and enabled"
fi
fi
# ============================
# 1c. Run update and upgrade
# ============================
export DEBIAN_FRONTEND=noninteractive
apt update && apt upgrade -y
check_step "System update"
else
echo -e "\e[33m⏭️ Update skipped\e[0m"
fi
# ============================
# 2. Create directories
# ============================
mkdir -p "$RESTORE_DIR"/{etc,pmxcfs,etc-pve,cron,scripts}
mkdir -p /root/pbs/scripts
check_step "Create directories"
# ============================
# 3. Get latest PBS snapshot
# ============================
pbs_output=$(proxmox-backup-client snapshot list --repository "$PBS_REPOSITORY" --ns "$NS" 2>&1)
cmd_status=$?
if [ $cmd_status -ne 0 ]; then
echo -e "\e[31m❌ Failed to fetch snapshots from PBS\e[0m"
echo "❌ $pbs_output"
exit 1
fi
snapshots=$(echo "$pbs_output" \
| grep '^│' \
| awk '{print $2}' \
| grep '^host/' )
if [[ -z "$snapshots" ]]; then
echo -e "\e[31m❌ Connection to PBS established, no host-type backups found\e[0m"
exit 1
fi
check_step "Fetch snapshot list (host only)"
latest_snapshot=$(echo "$snapshots" | sort -t'/' -k3 | tail -n 1)
check_step "Determine latest snapshot"
echo "Latest backup: $latest_snapshot"
# ============================
# 3b. Mount archives from PBS
# ============================
proxmox-backup-client mount "$latest_snapshot" etc.pxar "$RESTORE_DIR/etc" --ns "$NS"
check_step "Mount etc"
proxmox-backup-client mount "$latest_snapshot" crontab.pxar "$RESTORE_DIR/cron" --ns "$NS"
check_step "Mount cron"
proxmox-backup-client mount "$latest_snapshot" pmxcfs-SQLite-PVE.pxar "$RESTORE_DIR/pmxcfs" --ns "$NS"
check_step "Mount pmxcfs"
proxmox-backup-client mount "$latest_snapshot" scripts.pxar "$RESTORE_DIR/scripts" --ns "$NS"
check_step "Mount scripts"
# ============================
# 4. Stop Proxmox VE cluster service
# ============================
systemctl stop pve-cluster
check_step "Stop pve-cluster"
# ============================
# 5. Restore configuration files
# ============================
rsync -aHAX --numeric-ids "$RESTORE_DIR/etc/" /etc/
check_step "Restore etc"
rsync -aHAX --numeric-ids "$RESTORE_DIR/cron/" /var/spool/cron/
check_step "Restore cron"
rsync -aHAX --numeric-ids "$RESTORE_DIR/pmxcfs/" /var/lib/pve-cluster/
check_step "Restore pmxcfs"
rsync -aHAX --numeric-ids "$RESTORE_DIR/scripts/" /root/pbs/scripts/
check_step "Restore scripts"
# ============================
# 6. Unmount archives
# ============================
umount "$RESTORE_DIR/etc"
check_step "Umount etc"
umount "$RESTORE_DIR/cron"
check_step "Umount cron"
umount "$RESTORE_DIR/pmxcfs"
check_step "Umount pmxcfs"
umount "$RESTORE_DIR/scripts"
check_step "Umount scripts"
# ============================
# 7. Remove temporary directories
# ============================
rm -rf "$RESTORE_DIR"
check_step "Remove temporary directories"
# ============================
# 8. Reload systemd configuration
# ============================
systemctl daemon-reload
check_step "Reload systemd configuration"
# ============================
# 9. Start Proxmox VE cluster service
# ============================
sleep 2
systemctl start pve-cluster
check_step "Start pve-cluster"
# ============================
# 10. Clean VM/CT configurations (without disks)
# ============================
rm -f /etc/pve/lxc/*.conf
check_step "Clean LXC configs"
rm -f /etc/pve/qemu-server/*.conf
check_step "Clean VM configs"
# ============================
# 11. Check cluster status
# ============================
systemctl is-active --quiet pve-cluster
check_step "Check pve-cluster service status (active)"
check_step "Backup restore completed successfully ✅"
# ============================
# 12. Self-delete script
# ============================
if [[ "$selfdel_choice" =~ ^[Yy]$ ]]; then
script_path="$(realpath "$0")"
rm -- "$script_path"
echo -e "\e[32m✅ Script removed\e[0m"
else
echo -e "\e[33m⏭️ Self-removal skipped\e[0m"
fi
Работу скрипта проверял на PVE версии 9.
После всех действий рекомендую перезагрузить хост.