- Shell 100%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
Port of the pve8to9-upgrade.sh approach to PBS, following the official wiki: preflight checks (root, PBS 3.x, detachable session, interrupted upgrade resume, running tasks, free space), backup of /etc/proxmox-backup, update to the latest 3.4, pbs3to4 checklist, optional read-only maintenance mode on the datastores, apt repository switch to trixie in deb822 format, dist-upgrade, post-upgrade checks and DKMS check for the kernel that will boot. Hosts with Proxmox VE co-installed are refused. Unit tests cover the pure helpers (version comparison, apt sources handling, JSON parsing of tasks and datastores). |
||
| tests | ||
| .env.example | ||
| .gitignore | ||
| disable_swap.sh | ||
| exec-all | ||
| fix_chrony_notify.sh | ||
| install_proxsave_rclone.sh | ||
| LICENSE | ||
| lxc-exec | ||
| mise.toml | ||
| pbs3to4-upgrade.sh | ||
| pve8to9-upgrade.sh | ||
| pve_clean.sh | ||
| README.md | ||
| remove_openssh.sh | ||
| updater.sh | ||
proxmox_scripts
Maintenance scripts for self-hosted Proxmox VE and Proxmox Backup Server nodes: LXC mass update, PVE 8 → 9 and PBS 3 → 4 migrations, kernel cleanup, backup setup and a few one-shot fixes.
Everything is plain bash, meant to be run as root, directly on the PVE
node. No daemon, no agent, no dependency beyond what a standard PVE install
already ships (pct, qm, vzdump, whiptail).
Install
git clone ssh://git@git.deditoolbox.fr:22226/freesker/proxmox_scripts.git
cd proxmox_scripts
cp .env.example .env # only needed for updater.sh and install_proxsave_rclone.sh
chmod +x *.sh lxc-exec exec-all
Scripts
| Script | What it does |
|---|---|
updater.sh |
Snapshots, updates and cleans every LXC container of the node |
pve8to9-upgrade.sh |
Interactive, step-by-step migration from Proxmox VE 8 to 9 |
pbs3to4-upgrade.sh |
Interactive, step-by-step migration from Proxmox Backup Server 3 to 4 |
pve_clean.sh |
Purges old PVE kernels to free /boot |
fix_chrony_notify.sh |
Repairs chronyd failing to start after a PVE 9 migration |
install_proxsave_rclone.sh |
Installs proxsave + rclone and configures an SFTP backup remote |
remove_openssh.sh |
Uninstalls openssh-server from every LXC container |
disable_swap.sh |
Sets swap: 0 in every LXC configuration file |
lxc-exec / exec-all |
Runs a command in one / in every running container |
updater.sh
Loops over the LXC containers of the node and, for each of them:
- prunes
before-update*snapshots older than 7 days, but only when more than two of them exist; - takes a new
before-updateNsnapshot — or falls back tovzdumpon the storage defined bySTORAGEwhen the container's storage has no snapshot support; - starts the container if it was stopped;
- runs the distribution upgrade (
apt,apk,dnf,pacmandepending onostype); - updates the Docker stacks that were running when the script started —
a
/root/**/update_docker.shscript takes precedence over the defaultdocker compose pull && down && up -d && system prune; - purges caches, logs and apt lists, then runs
pct fstrim; - reboots the container if
/var/run/reboot-requiredexists, or shuts it down again if it was stopped initially.
Only debian and ubuntu containers are processed; the others are skipped.
At startup the script checks its own git remote and re-execs itself if a newer
version is available. The full session is logged to
/tmp/lxd-updater-<date>.
./updater.sh # interactive: whiptail menu to pick containers to skip
./updater.sh -i # non-interactive, process every container
./updater.sh -i 101 105 # non-interactive, skip containers 101 and 105
pve8to9-upgrade.sh
Guided migration of a single node from Proxmox VE 8 to 9, following the official procedure. The script pauses before every critical step and never reboots on its own.
Steps: preflight checks → backup of /etc → upgrade to the latest PVE 8.4 →
Ceph migration (Quincy → Reef → Squid) → pve8to9 checklist → apt repository
switch (bookworm → trixie) → dist-upgrade → post-upgrade checklist.
It refuses to start outside a detachable session (tmux or screen): an
SSH drop during the dist-upgrade leaves apt stuck on a debconf question
asked to a dead terminal, and the node half-migrated. An interrupted migration
is detected on the next run and can be resumed instead of restarted.
On a cluster, run the Ceph migration manually on each node beforehand — the script only handles the single-node case.
tmux new -s pve8to9
./pve8to9-upgrade.sh
Logs land in /var/log/pve8to9-upgrade/, configuration backups in
/root/pve8to9-backup-<timestamp>/.
pbs3to4-upgrade.sh
Guided in-place upgrade of a Proxmox Backup Server host from 3 to 4,
following the official procedure.
Same philosophy as pve8to9-upgrade.sh: the script pauses before every
critical step, never reboots on its own, refuses to start outside tmux /
screen, and resumes an interrupted upgrade on the next run.
Steps: preflight checks (root, PBS 3.x, running tasks, free space) → backup
of /etc/proxmox-backup and /etc → upgrade to the latest PBS 3.4 →
pbs3to4 checklist → optional read-only maintenance mode on every datastore
→ apt repository switch (bookworm → trixie, deb822 files) → dist-upgrade →
post-upgrade checklist and service status → DKMS check for the kernel that
will boot.
Datastores put in read-only mode by the script are listed again at the end, with the exact commands to lift the mode after the reboot. Hosts where Proxmox VE is co-installed are refused: both products must then be upgraded together, by hand.
tmux new -s pbs3to4
./pbs3to4-upgrade.sh
Logs land in /var/log/pbs3to4-upgrade/, configuration backups in
/root/pbs3to4-backup-<timestamp>/.
pve_clean.sh
Derived from pvekclean v2.0.2 by
Jordan Hillis (MIT). Lists the installed PVE kernels, keeps the running one and
purges the others (image + headers), then runs update-grub. Useful before a
major upgrade: a full /boot makes apt fail in the middle of a
dist-upgrade.
./pve_clean.sh # interactive
./pve_clean.sh --dry-run # simulate, remove nothing (do this first)
./pve_clean.sh --keep 2 # keep the 2 most recent kernels
./pve_clean.sh --force # no confirmation (cron)
./pve_clean.sh --remove-newer # also allow purging kernels newer than the running one
./pve_clean.sh --scheduler # schedule the purge through cron
Two inherited behaviours worth knowing: the script offers to overwrite
itself with the upstream version from GitHub (set check_for_updates=false
to disable), and to install itself into /usr/local/sbin/pvekclean.
fix_chrony_notify.sh
After a migration to PVE 9 (Debian 13, systemd 257), chronyd can fail with
Fatal error : Could not send notification to $NOTIFY_SOCKET, leaving the node
without time synchronisation. The script installs a systemd drop-in switching
the unit to Type=simple, and only does so when the failure signature matches.
./fix_chrony_notify.sh # diagnose, fix if needed, verify sync
./fix_chrony_notify.sh --check # diagnose only, change nothing
./fix_chrony_notify.sh --revert # remove the drop-in and retest
install_proxsave_rclone.sh
Installs rclone and proxsave, then configures an SFTP remote end to end:
- prompts for remote name, host, port and user;
- generates a dedicated ed25519 key pair and prints the public key, so it
can be added to the server's
authorized_keysbefore continuing; - tests the remote in read and write mode — a real upload in both the backup and the log directory, re-downloaded and compared byte for byte;
- writes
CLOUD_REMOTE,CLOUD_REMOTE_PATH,CLOUD_LOG_PATHandWEBHOOK_HEALTHCHECK_URLinto/opt/proxsave/configs/backup.env.
Any PROXSAVE_* variable missing from .env is prompted at runtime.
./install_proxsave_rclone.sh
remove_openssh.sh
Purges openssh-server from every LXC container (apt, apk or dnf
depending on the OS). Stopped containers are started, cleaned, then shut down
again; templates are skipped. A whiptail menu allows excluding containers, and
the session is logged to /tmp/remove-openssh-<date>.
disable_swap.sh
Rewrites swap: 0 in every /etc/pve/nodes/*/lxc/*.conf. Containers must be
restarted for the change to apply.
lxc-exec / exec-all
source lxc-exec && lxc-exec 101 "apt update" # one container, by ID
./exec-all "apt update" # every running container
Configuration
Copy .env.example to .env and adjust it. .env is git-ignored; it is
sourced by updater.sh and install_proxsave_rclone.sh from the script
directory.
| Variable | Used by | Purpose |
|---|---|---|
STORAGE |
updater.sh |
vzdump target storage when snapshots are unavailable |
PROXSAVE_REMOTE_PATH |
install_proxsave_rclone.sh |
Backup path on the SFTP remote (after <remote>:) |
PROXSAVE_LOG_BASE |
install_proxsave_rclone.sh |
Base path holding the per-host log directories |
PROXSAVE_HEALTHCHECK_URL |
install_proxsave_rclone.sh |
Healthcheck ping URL (e.g. healthchecks.io) |
Development
Tooling is pinned with mise:
mise install # installs shellcheck
shellcheck *.sh lxc-exec exec-all
Unit tests cover the pure helpers of the upgrade scripts — parsing and decision functions only, no system call:
bash tests/test_ceph_helpers.sh # pve8to9-upgrade.sh
bash tests/test_pbs_helpers.sh # pbs3to4-upgrade.sh
License
MIT — see LICENSE. pve_clean.sh derives from pvekclean, also MIT.