ProxMenux v1.2.4
Adds two in-dashboard improvements (Update Now button + mobile PWA install prompt), extends the Backups restore flow with atomic pmxcfs (config.db) snapshots and automatic ZFS data-pool import, sharpens Log2RAM behaviour on PBS-as-a-service hosts, hardens the firewall bridge sysctl tuning across VM lifecycle events, narrows the ZFS ARC optimization to its own scope, makes the persistent NIC naming idempotent, rebuilds DKMS drivers automatically when a new kernel is staged, keeps the Monitor terminal session intact when a ProxMenux update is available, and reinforces five notification templates + three Health panel checks.
✨ What's new
- Update Now button in the Health Monitor → System Updates section. Runs
proxmox_update.shin an in-dashboard terminal (apt update + dist-upgrade + post-cleanup). On close it forces a fresh Health scan (/api/health/full?refresh=1) so the pending-update count and kernel row reflect the post-update state right away instead of the cached pre-update value. Only appears when there are pending updates. - In-app Install prompt for mobile — bottom-sheet with iOS Safari / Android instructions. "Not now" (30d) or "Don't show again" (permanent, localStorage).
- Safe update flow — new
scripts/global/update-pve-safe.shinvoked by the Update Now button and the CLI utility. Respects user-authored repos (never disables enterprise/ceph, never purges alternate NTP, never force-installs zfsutils/chrony); on a bare host lays down only the missing base repos viaensure_repositories. Detects a newly installed kernel that isn't the running one (viadpkg-query) and prompts for reboot even whenneedrestartisn't installed.cleanup_duplicate_reposreworked to compare URI + Suite + Component exactly againstproxmox.sources/debian.sourcesand only touch a fixed allowlist of legacypve-*.listfiles — so customdownload.proxmox.com/*entries (PBS server on host, local mirror) survive intact. is_apt_active_on_host()— gate in_check_service_failurethat suppressesservice_failnotifications while apt/dpkg is running (marker +fuserondpkg-lock-frontend+ 60s grace). PVE services restarted as part of the upgrade cycle stay quiet during the window.
🔔 Notification content — five rendering refinements
- Backup destination shown — vzdump emails/Telegram carry the
--storagetarget in title + body row, so users with several PBS/Local destinations can tell at a glance which one produced the event. - Migration bodies carry the real target node — pulled from the PVE task log for
qmigrate/vzmigrateevents. - Snapshot bodies carry the real snapshot name — pulled from the PVE task log for
qmsnapshot/vzsnapshotevents. - Generic
system_problemnotifications include the PVE payload message as the reason. - NVIDIA / Coral driver update emails render the New Version row correctly by aligning the template placeholder with the field the renderer reads.
🩹 Health panel — three checks reinforced
- Dismiss now silences storage alerts — the acknowledge flow includes
storage_unavailable_*,mount_stale,mount_readonly,lxc_disk_low_*,lxc_mount_low_*,pve_storage_full_*andzfs_pool_full_*under thestoragecategory, and the storage cache is invalidated on dismiss so the panel refreshes immediately. - VMs & Containers check tolerates NULL
detailsrows (#255) —_check_vms_cts_with_persistencecoalesces persisted errors with a missingdetailscolumn to an empty dict before reading nested keys, so a single sparse row no longer takes the whole VM/CT check offline. system_startupfires once per boot —_check_startup_aggregationnow marks aggregation as done right after queuing the event, so the boot summary lands one time regardless of how many polling ticks fit inside the current session.
🛠 Mobile & webhook
- Mobile dashboard polling stays live on HTTPS + reverse proxy setups —
pwa-register.tsxauto-unregisters any Service Worker on load, and the PWA installability path is now driven by the new in-app install prompt described above. - Webhook auth trusts every host-local IP — the internal webhook accepts requests from any interface IP the host owns (Tailscale, WireGuard, LAN, IPv6, including v4-mapped IPv6
::ffff:x.x.x.xthat Flask emits on dual-stack binds), so PVE Test buttons work over any of them.
🛡 Update flow — Safe path when launched from the Monitor terminal
- The Monitor's WebSocket terminal now identifies itself via
PROXMENUX_TERMINAL=monitor, inherited by every child shell it spawns. - When
menudetects a new ProxMenux version and the session is running from that terminal, the yes/no update prompt is replaced by an informational msgbox naming the new version and pointing to the canonicalbash -c "$(wget -qLO - …)"one-liner to run from SSH / the host console. The menu keeps working — the user can continue using ProxMenux from the current terminal; only the update path is routed to a session that survives the Monitor service restart. - Same guard on the release-channel switch in Settings → Release Channel (
config_menu.sh). Selecting Stable ↔ Beta from the Monitor terminal shows the informational msgbox with the exactwgetone-liner for the target channel and returns to the menu instead of running the installer in place. - SSH sessions, the Proxmox host console, and any environment where
PROXMENUX_TERMINALisn'tmonitorkeep the previous yes/no prompt and can update or switch channels as always.
🔧 Update flow — DKMS drivers rebuilt when a new kernel lands
update-pve-safe.sh(Update Now button in the Health Monitor and the CLI utility that delegates to it) now rebuilds ProxMenux-installed DKMS drivers wheneverapt full-upgradestages a kernel newer than the one currently running. Detects the case viadpkg-queryonproxmox-kernel-*/pve-kernel-*vsuname -r. Installs the matching headers (proxmox-headers-<newkver>orpve-headers-<newkver>) if missing, runsdkms autoinstall -k <newkver>, then verifies each expected module (gasketfor Coral,nvidiafor the NVIDIA driver) actually reachedinstalledfor the new kernel. If any module was left behind, falls back to each installer's--auto-reinstallpath.- Whiptail msgbox announces the rebuild before it runs — single OK button, no yes/no. Names the incoming kernel and the components that will be rebuilt, so the user sees what's coming without a false "cancel" option that would leave the system with dead drivers at boot. Non-interactive runs (cron, headless) skip the msgbox and log the same information.
- Only components already registered as
installedincomponents_status.jsonare considered. Hosts without NVIDIA / Coral drivers see no msgbox and no rebuild step. - Shared helper
pmx_rebuild_dkms_after_kernellives inscripts/global/utils-install-functions.sh, so the CLI utility (utilities/proxmox_update.sh) inherits the behaviour by delegation and no other call site needs to duplicate the logic.
🔌 Post-install — Persistent NIC naming becomes idempotent
- ProxMenux-owned
.linkfiles now carry a distinctive prefix (10-proxmenux-<iface>.link) and a first-line marker (# Managed by ProxMenux — do not edit) so the reconciliation and uninstall paths can identify their own files with certainty and leave every other.linkin/etc/systemd/network/(user-authored, other packages) strictly alone. - Reruns reconcile stale ProxMenux entries. On every execution the function walks the existing
10-proxmenux-*.linkfiles, compares the recordedMACAddress=against the MACs currently present under/sys/class/net/, and removes only the ProxMenux-owned entries whose MAC is gone. Replacing or migrating a NIC no longer leaves an orphan mapping behind. - Legacy 1.0-format files (
10-<iface>.linkwith the plain two-section template) are migrated automatically on the first run of 1.1, so an existing install ends up on the new naming without leaving duplicates or manual cleanup for the user. - The uninstall path now only removes files that carry both the
10-proxmenux-filename prefix and the marker on the first line. The previous blanketrm -f *.linkis gone — anything the user wrote by hand or that came from another package is preserved. - Single shared implementation in
scripts/global/utils-install-functions.sh(pmx_setup_persistent_network+pmx_uninstall_persistent_network). The three call sites (auto_post_install.sh,customizable_post_install.sh,network_menu.sh) plus the uninstall path all delegate to the same helper, so the logic can't drift across variants any more. FUNC_VERSIONbumped 1.0 → 1.1 so the update detector re-runs the function on hosts installed with 1.0 — that first re-run performs the legacy migration and the reconciliation in one shot.
🧮 Post-install — ZFS ARC optimization narrowed to its scope
optimize_zfs_arcnow sets onlyzfs_arc_max—zfs_arc_minstays managed by OpenZFS defaults, and the L2ARC (l2arc_noprefetch,l2arc_write_max) and TXG (zfs_txg_timeout) tunables that the previous implementation wrote unconditionally are no longer touched. The function does exactly what its name says: caps the ARC maximum.update-initramfs -u -k allruns after writing/etc/modprobe.d/99-zfsarc.conf(plusproxmox-boot-tool refreshon systemd-boot hosts), so on ZFS-on-root systems the new cap actually takes effect on the next boot instead of being shadowed by the initramfs's stale copy.- Guarded on live pools — the function short-circuits when no ZFS pool is present, so nothing gets written on a host that doesn't use ZFS.
- Cap uses clean binary sizes — 512 MiB for hosts up to 16 GB RAM, 1 GiB up to 32 GB, RAM/8 above that (never below 512 MiB).
FUNC_VERSIONbumped 1.0 → 1.1 so the update detector re-runs the function on existing installs; the fresh write of99-zfsarc.confcleans out any leftover L2ARC/TXG lines from earlier runs. Uninstall path also runsupdate-initramfs+proxmox-boot-tool refreshso restoring or removing the tuning is picked up at boot too.
🔥 Post-install — Firewall bridge sysctl tuning hardened
rp_filter=0/log_martians=0onfwbr*,fwln*,fwpr*,tap*now applies to interfaces Proxmox creates on every VM start / stop / reboot / migrate. New/etc/udev/rules.d/99-proxmenux-fwbr-tune.rulesfires a helper on each matchingnet/addevent, so fresh interfaces pick up the value immediately — no reboot, no rerun.- Tuning logic centralised in
/usr/local/sbin/proxmenux-fwbr-tune— shared by the initial sweep (proxmenux-fwbr-tune.service, oneshot) and the udev rule. Explicit invocation at install time so the current session sees the change without waiting for the next VM cycle. - Customizable post-install flow now installs the same helper + service + udev rule + initial sweep as the automatic flow — both variants leave the system in the same end state.
- Both
apply_network_optimizationsbumpedFUNC_VERSION1.0 → 1.1 so the update detector re-runs them. Uninstall extended to remove the helper and udev rule and reload udev.
🧰 Post-install — Log2RAM + PBS
- PBS API log rotation applied when
proxmox-backup-serverruns as a service on the same host. Bothinstall_log2ram_autoandconfigure_log2ramnow drop/etc/logrotate.d/proxmox-backup-api+/etc/cron.hourly/proxmox-backup-logrotatesoaccess.logandauth.logstay bounded at 20MB × 3 rotations — hourly cadence keeps the tmpfs backing Log2RAM comfortably under budget on hosts wherepvestatdpolls the local datastore every few seconds. No-op on hosts without PBS. - Upstream
log2ramscript patched torsync -aXv --no-acls. Both installers rewrite the call in place right afterinstall.shruns, solog2ram writefinishes cleanly on/var/log.hddfilesystems that don't accept POSIX ACLs (ZFS withacltype=off, ext4 without theacloption). Guarded bygrep -qso a future upstream release that already dropped-Aisn't patched again. - Emergency block in
log2ram-check.shrotates PBS logs before truncating. When/var/logcrosses the 92% threshold, the script runslogrotate -f /etc/logrotate.d/proxmox-backup-api(only if the rule exists) before the existingpveproxy/pveamtruncate — recent PBS access/auth history is preserved in the rotated.gzfiles instead of being lost. BothFUNC_VERSIONbumped 1.2 → 1.3.
🗄 Backup restore — pmxcfs / ZFS data pools
/var/lib/pve-cluster/config.dbis captured viasqlite3 .backup— the canonical way to snapshot the pmxcfs SQLite store consistently withpve-clusterrunning, matching Proxmox's own documented advice. The raw file is skipped from the general rsync so nothing overwrites the atomic dump. Araw-fallbackcopy is kept whensqlite3isn't installed, and the recovery helper promotes it toconfig.dbautomatically. The restore path continues to use the canonicalstop pve-cluster → cp → startpattern with the DB the user brings back now guaranteed consistent.- Data pools listed in the backup are now imported automatically during restore. New
_rs_import_data_poolsstep runs after config apply, iteratesstorage_inventory.zfs_pools[]and doeszpool import <name>for every non-root pool whose disks are all present on this host. On a foreign hostid (fresh PVE install regrabbed the label) it retries with-fand flags the pool as forced so the user has trace. Pools with missing disks are skipped with a clear warning. - Result persists to the post-restore progress card. The auto-import writes a
data_pools_importsection into/var/lib/proxmenux/restore-state.jsonand a raw log at/var/log/proxmenux/restore-datapools-<timestamp>.log. The Backups tab card renders a dedicated block (Imported / Forced / Skipped partial / Skipped missing / Failed) so the summary stays consultable after the restore terminal is closed. - Recognises pools created with
by-partuuidor raw/dev/sdX— the auto-import step andvalidate_storage.shtreatdevices_by_identries starting with/as absolute paths, so pools built against partition UUIDs or raw block devices are detected as present when their disks are on the host. /etc/systemd/networkadded to the default backup paths. That directory holds systemd.linkfiles that pin NIC names to their MAC —setup_persistent_networkin the post_install writes them, and users can drop their own. Preserving them across a fresh-install restore keeps the source host's NIC naming policy intact on the target.
🙏 Acknowledgments
- @PepenAI — mobile dashboard on HTTPS + reverse proxy
- Pepo — webhook auth from a Tailscale FQDN
- @ash34 (#255) — VM/CT check with a NULL
detailsrow - @f3rs3n (#256, #257, #258) — firewall bridge sysctl tuning, ZFS ARC optimization scope, persistent NIC naming reconciliation
- Juan C. — ZFS data pool auto-import after a fresh install
- David Barbero (@sikete) — DKMS driver rebuild on kernel upgrade