Merge remote-tracking branch 'origin/develop' into HEAD

This commit is contained in:
MacRimi
2026-10-18 14:09:51 +02:00
658 changed files with 77297 additions and 2004 deletions
+6 -1
View File
@@ -28,7 +28,12 @@
"switcher": "Language",
"en": "English",
"es": "Español",
"sk": "Slovenčina"
"sk": "Slovenčina",
"de": "Deutsch",
"fr": "Français",
"it": "Italiano",
"pt": "Português",
"sv": "Svenska"
},
"resources": {
"documentation": {
@@ -8,5 +8,6 @@
"errors": {
"notFound": "Error: CODE_OF_CONDUCT.md file not found.",
"loadFailed": "Error: Unable to load the Code of Conduct content."
}
},
"contentMarkdown": "# 📝 Code of Conduct & Best Practices\n\nThis document defines the **principles and guidelines** for the use of **ProxMenux**, aiming to foster **respect, collaboration, and security** in development.\n\n## 📌 1. Code Attribution & Recognition\n\n- If **scripts, code snippets, or ideas** from other repositories or developers are used, the original author will always be credited. \n- A comment will be added in the code or documentation mentioning the source and linking to the original repository if possible. \n- When using code with a specific **license**, its terms must be respected. \n\n### ✅ Example of proper attribution in code:\n\n```bash\n# Script based on the work of [Author] at [Repository Link]\n```\n\n## 🔒 2. Security & Code Responsibility\n\n- All code in this repository follows secure development best practices.\n- Contributions introducing intentional vulnerabilities or unsafe practices are not allowed.\n- Scripts should never compromise system security or user data.\n- Before implementation, contributions should be reviewed to ensure security and stability.\n\n### ⚠️ Responsible Disclosure of Vulnerabilities\n\nIf you discover a security vulnerability, DO NOT report it in public Issues or forums.\n\nInstead, please report it privately via email:\n\n📧 proxmenux@macrimi.pro\n\nFor detailed information on security vulnerabilities and how to report them, please refer to our [Security Policy](./SECURITY.md).\n\n## 🤝 3. Community Guidelines\n\nTo maintain a respectful and professional environment, all users must follow these guidelines:\n\n- ✅ Be respectful and constructive in discussions.\n- ❌ No harassment, hate speech, or offensive behavior will be tolerated.\n- ✅ Provide feedback professionally, avoiding personal attacks.\n- ❌ Violating these guidelines may result in warnings or bans, depending on severity.\n\n\n##\n\nWith this document, we promote ethical code use, security awareness, and a respectful community.\n\n📢 Let's build something great, together!\n\n"
}
+6 -6
View File
@@ -45,7 +45,7 @@
},
"q4": {
"question": "Can I customise ProxMenux?",
"p1Rich": "The core scripts can't be modified directly because they're hosted on GitHub and refreshed by the auto-update flow. What you <em>can</em> customise:",
"p1Rich": "Local changes to the core scripts can be overwritten by the update flow, so avoid editing those files directly. What you <em>can</em> customise:",
"items": [
"The console banner via the <strong>FastFetch</strong> tool in the Post-Install options.",
"Which optional components are installed (Monitor, Fail2Ban, Lynis…) — pick during install or via the relevant menu later."
@@ -63,15 +63,15 @@
},
"q7": {
"question": "Can I contribute to ProxMenux?",
"p1Rich": "<strong>Absolutely.</strong> ProxMenux is open source and collaborative. Contributions can be code (bash, Next.js / TypeScript, the translation Python venv), testing, documentation, design, or just ideas.",
"p1Rich": "<strong>Absolutely.</strong> ProxMenux is open source and collaborative. Contributions can be code (Bash, Next.js/TypeScript and Python translation tools), testing, documentation, design, or just ideas.",
"item1Rich": "Discuss ideas in <discusslink>GitHub Discussions</discusslink> before opening a feature request.",
"item2Rich": "For PRs, read the <coclink>Code of Conduct & Best Practices</coclink> first.",
"item3Rich": "See the <contriblink>Contributors page</contriblink> for current testers and reviewers."
},
"q8": {
"question": "Does ProxMenux modify critical system files?",
"p1Rich": "<strong>No.</strong> ProxMenux installs:",
"item1Rich": "Dependencies via apt: <code>dialog</code>, <code>curl</code>, <code>jq</code>, <code>git</code> (and <code>python3</code> + <code>googletrans</code> on the Translation install).",
"p1Rich": "<strong>Installing ProxMenux adds the following components.</strong>",
"item1Rich": "Dependencies via apt: <code>dialog</code>, <code>curl</code>, <code>jq</code> and <code>git</code>. Translations use pre-built JSON catalogs; the installer does not add a translation virtualenv to the host.",
"item2Rich": "The script tree at <code>/usr/local/share/proxmenux/</code>.",
"item3Rich": "The launcher at <code>/usr/local/bin/menu</code>.",
"item4Rich": "Optional: the Monitor systemd service at <code>proxmenux-monitor.service</code>.",
@@ -83,12 +83,12 @@
},
"q10": {
"question": "How do I uninstall ProxMenux?",
"p1Rich": "From inside ProxMenux: <strong>Settings → Uninstall ProxMenux</strong>. The uninstaller removes the script tree, the launcher and the Monitor service, optionally removes selected dependencies, and restores <code>/root/.bashrc</code> and <code>/etc/motd</code> backups taken at install time.",
"p1Rich": "From inside ProxMenux: <strong>Settings → Uninstall ProxMenux</strong>. The uninstaller removes the script tree, the launcher and the Monitor service, optionally removes selected dependencies, and restores <code>/root/.bashrc</code> and <code>/etc/motd</code> from install-time backups if those backups exist.",
"p2Rich": "Full step-by-step: <uninstalllink>Uninstall ProxMenux</uninstalllink>."
},
"q11": {
"question": "VirusTotal flags the install URL — is it safe?",
"p1Rich": "A 1/95 detection by heuristic engines (e.g. <em>Chong Lua Dao</em>) is a known false positive. The installer uses the standard <code>curl | bash</code> pattern and downloads legitimate binaries (like <code>jq</code> from its official GitHub release), which aggressive scanners flag based on <em>behaviour</em> rather than actual malicious code.",
"p1Rich": "The 1/95 result reported in Issue #162, from <em>Chong Lua Dao</em>, was identified by the maintainer as a false positive. The installer uses the <code>curl | bash</code> pattern and can download <code>jq</code> from its official GitHub release. Such <em>behaviour</em> can trigger heuristic scanners; the historical report is not a verdict on every future download.",
"p2Rich": "ProxMenux is 100% open source and the install script is reviewable line by line: <scriptlink>install_proxmenux.sh</scriptlink>. More context in <issuelink>Issue #162</issuelink>."
}
}
@@ -20,9 +20,21 @@
"heading": "The direction check",
"intro": "During the compatibility check, <code>hb_compat_check</code> compares the kernel recorded in the backup's manifest against the target's current kernel (<code>uname -r</code>) and classifies the restore into one of three cases, stored in the internal variable <code>HB_COMPAT_KERNEL_DIRECTION</code>:",
"rows": [
{ "direction": "same", "condition": "The major kernel version matches between backup and target.", "behavior": "The full restore flow runs unchanged. No extra filter, no hydration, no special notice in the UI." },
{ "direction": "bk_newer", "condition": "Backup kernel is NEWER than the target's (for example: the backup was made on kernel 7.0 and the target runs kernel 6.17).", "behavior": "The full restore flow runs unchanged, exactly like <code>same</code>. No filter is applied. Verified empirically: drivers auto-reinstall against the target's kernel, IOMMU config applies cleanly, VMs with GPU passthrough come up without issue." },
{ "direction": "bk_older", "condition": "Backup kernel is OLDER than the target's (for example: the backup was made on kernel 6.17 and the target runs kernel 7.0).", "behavior": "The safe-subset filter and the four-phase hydration below are activated. The restore proceeds — the target reproduces the source, but boot-critical files are not copied verbatim." }
{
"direction": "same",
"condition": "The major kernel version matches between backup and target.",
"behavior": "The full restore flow runs unchanged. No extra filter, no hydration, no special notice in the UI."
},
{
"direction": "bk_newer",
"condition": "Backup kernel is NEWER than the target's (for example: the backup was made on kernel 7.0 and the target runs kernel 6.17).",
"behavior": "The full restore flow runs unchanged, exactly like <code>same</code>. No filter is applied. Verified empirically: drivers auto-reinstall against the target's kernel, IOMMU config applies cleanly, VMs with GPU passthrough come up without issue."
},
{
"direction": "bk_older",
"condition": "Backup kernel is OLDER than the target's (for example: the backup was made on kernel 6.17 and the target runs kernel 7.0).",
"behavior": "The safe-subset filter and the four-phase hydration below are activated. The restore proceeds — the target reproduces the source, but boot-critical files are not copied verbatim."
}
]
},
"whyBkNewerIsSafe": {
@@ -31,25 +43,60 @@
},
"safeSubsetFilter": {
"heading": "The safe-subset filter (only when the target kernel is newer)",
"intro": "When the target kernel is newer than the backup's, the compatibility check adds 16 boot-critical paths from <code>hb_unsafe_paths_cross_version</code> to <code>RS_SKIP_PATHS</code>. These paths are excluded from the restore because writing a version of them from an older kernel over a target running a newer one has caused kernel panics in bench tests. The paths cover four categories:",
"intro": "When the target kernel is newer than the backup's, the compatibility check adds 16 boot-critical paths from <code>hb_unsafe_paths_cross_version</code> to <code>RS_SKIP_PATHS</code>. These paths are excluded from the restore because writing a version of them from an older kernel over a target running a newer one has caused kernel panics in bench tests. The paths cover five categories:",
"categoryRows": [
{ "category": "Bootloader", "paths": "<code>/etc/default/grub</code>, <code>/etc/kernel</code>", "reason": "GRUB defaults tied to the prior kernel order, and proxmox-boot-tool state (cmdline, ESP UUIDs, hooks) that references paths and identifiers from the older install." },
{ "category": "Kernel modules and boot artifacts", "paths": "<code>/etc/modules-load.d</code>, <code>/etc/modprobe.d</code>, <code>/etc/initramfs-tools</code>", "reason": "Autoload lists that may reference modules renamed between kernel majors, module options that may not apply, initramfs hooks written for the older kernel." },
{ "category": "Storage stack and filesystem identity", "paths": "<code>/etc/fstab</code>, <code>/etc/multipath</code>, <code>/etc/iscsi</code>, <code>/etc/udev/rules.d</code>, <code>/etc/zfs</code>", "reason": "UUIDs that may not exist on this install, multipath drivers that change between kernels, iSCSI parameters that evolve, udev rules that may bind to nonexistent subsystems, ZFS state (<code>zpool.cache</code> + <code>hostid</code>) that can lock the pool as foreign." },
{ "category": "APT sources", "paths": "<code>/etc/apt</code>", "reason": "APT source suites may trigger a downgrade of critical packages on the next upgrade." },
{ "category": "systemd", "paths": "<code>/etc/systemd/system</code>, <code>/etc/systemd/journald.conf</code>, <code>/etc/systemd/logind.conf</code>, <code>/etc/systemd/system.conf</code>, <code>/etc/systemd/user.conf</code>", "reason": "Unit overrides and .wants tied to the older systemd major; configuration keys that may not parse on a newer systemd." }
{
"category": "Bootloader",
"paths": "<code>/etc/default/grub</code>, <code>/etc/kernel</code>",
"reason": "GRUB defaults tied to the prior kernel order, and proxmox-boot-tool state (cmdline, ESP UUIDs, hooks) that references paths and identifiers from the older install."
},
{
"category": "Kernel modules and boot artifacts",
"paths": "<code>/etc/modules-load.d</code>, <code>/etc/modprobe.d</code>, <code>/etc/initramfs-tools</code>",
"reason": "Autoload lists that may reference modules renamed between kernel majors, module options that may not apply, initramfs hooks written for the older kernel."
},
{
"category": "Storage stack and filesystem identity",
"paths": "<code>/etc/fstab</code>, <code>/etc/multipath</code>, <code>/etc/iscsi</code>, <code>/etc/udev/rules.d</code>, <code>/etc/zfs</code>",
"reason": "UUIDs that may not exist on this install, multipath drivers that change between kernels, iSCSI parameters that evolve, udev rules that may bind to nonexistent subsystems, ZFS state (<code>zpool.cache</code> + <code>hostid</code>) that can lock the pool as foreign."
},
{
"category": "APT sources",
"paths": "<code>/etc/apt</code>",
"reason": "APT source suites may trigger a downgrade of critical packages on the next upgrade."
},
{
"category": "systemd",
"paths": "<code>/etc/systemd/system</code>, <code>/etc/systemd/journald.conf</code>, <code>/etc/systemd/logind.conf</code>, <code>/etc/systemd/system.conf</code>, <code>/etc/systemd/user.conf</code>",
"reason": "Unit overrides and .wants tied to the older systemd major; configuration keys that may not parse on a newer systemd."
}
],
"outroBody": "The filter runs BEFORE the confirmation dialog so the user sees the exact list of paths that will be skipped, categorised by reason. The restore still proceeds — everything else (VMs, LXCs, network, /etc/pve, users, cron, ProxMenux state, packages, drivers, /root) is restored normally."
"outroBody": "The filter runs BEFORE the confirmation dialog so the user sees the exact list of paths that will be skipped, categorised by reason. The restore still proceeds — everything else (VM/LXC configuration, network, /etc/pve, users, cron, ProxMenux state, packages, drivers, /root) is restored normally."
},
"hydration": {
"heading": "Kernel-agnostic hydration",
"intro": "The safe-subset filter alone would leave the target without the tuning the user had inside those boot-critical files: IOMMU cmdline for GPU passthrough, VFIO device IDs, custom <code>GRUB_TIMEOUT</code>, nvidia blacklists. The hydration pass re-applies those bits kernel-agnostically. Four phases run when the target kernel is newer than the backup's, each additive (never overwrites a value the target already carries) and idempotent (running twice is a no-op).",
"intro": "The filter omits boot-critical files, so hydration reapplies selected user tuning: IOMMU cmdline, VFIO IDs, custom <code>GRUB_TIMEOUT</code> and nvidia blacklists. It runs when the target kernel is newer. Cmdline merges preserve existing target keys and module merges append missing entries. Whitelisted GRUB values and user files may replace differing target content. Repeating the same input is idempotent.",
"phaseRows": [
{ "phase": "1a — GRUB path", "detail": "For hosts using GRUB (ext4/lvm installs). <code>_rs_hyd_grub</code> merges every token from the backup's <code>manifest.kernel_params.cmdline_extra</code> into the target's live <code>GRUB_CMDLINE_LINUX_DEFAULT</code>, skipping tokens whose key the target already carries. Then merges whitelisted <code>GRUB_*</code> keys (<code>GRUB_TIMEOUT</code>, <code>GRUB_TIMEOUT_STYLE</code>, <code>GRUB_DEFAULT</code>, <code>GRUB_TERMINAL</code>, <code>GRUB_DISABLE_OS_PROBER</code>, <code>GRUB_SERIAL_COMMAND</code>, <code>GRUB_GFXMODE</code>, <code>GRUB_GFXPAYLOAD_LINUX</code>) from the backup's <code>/etc/default/grub</code> if they differ from the target's." },
{ "phase": "1b — systemd-boot / ZFS path", "detail": "For hosts using systemd-boot (typically ZFS-on-root). <code>_rs_hyd_kernel_cmdline</code> merges user tokens from <code>cmdline_extra</code> into the target's <code>/etc/kernel/cmdline</code>, keeping the target's own <code>root=</code>, <code>boot=</code> and <code>rootflags=</code> boilerplate intact." },
{ "phase": "2 — /etc/modules merge", "detail": "<code>_rs_hyd_modules</code> appends modules from <code>manifest.kernel_params.modules_loaded_at_boot</code> that are in the whitelist (<code>vfio</code>, <code>vfio_pci</code>, <code>vfio_iommu_type1</code>, <code>vfio_virqfd</code>, <code>kvm</code>, <code>kvm_intel</code>, <code>kvm_amd</code>, <code>nvidia</code>, <code>nvidia_drm</code>, <code>nvidia_modeset</code>, <code>nvidia_uvm</code>, <code>i915</code>, <code>xe</code>) AND not already present in the target's <code>/etc/modules</code>." },
{ "phase": "3 — Whitelisted files copy", "detail": "<code>_rs_hyd_files</code> copies user-authored files from the staging rootfs to the live target when the content differs. Whitelist covers VFIO/nvidia/blacklist files under <code>/etc/modprobe.d</code>, <code>/etc/modules-load.d</code>, and the ProxMenux VFIO bind rule + nvidia udev rules under <code>/etc/udev/rules.d</code>. Distro-owned files (<code>pve-blacklist.conf</code>, <code>mdadm.conf</code>, <code>nvme.conf</code>) are intentionally excluded — their contents evolve between releases." },
{ "phase": "4 — Force post-boot reflows", "detail": "The four phases write directly to the live target OUTSIDE the normal restore pipeline. To make the merged tokens/modules/files take effect on the next boot, <code>HB_HYDRATION_APPLIED=1</code> propagates through <code>plan.env</code> to <code>apply_pending_restore.sh</code>, which forces <code>NEEDS_INITRAMFS=1</code> and <code>NEEDS_GRUB=1</code> regardless of what was in the apply list. The post-boot dispatcher then regenerates initramfs and refreshes the bootloader." }
{
"phase": "1a — GRUB path",
"detail": "For hosts using GRUB (ext4/lvm installs). <code>_rs_hyd_grub</code> merges every token from the backup's <code>manifest.kernel_params.cmdline_extra</code> into the target's live <code>GRUB_CMDLINE_LINUX_DEFAULT</code>, skipping tokens whose key the target already carries. Then merges whitelisted <code>GRUB_*</code> keys (<code>GRUB_TIMEOUT</code>, <code>GRUB_TIMEOUT_STYLE</code>, <code>GRUB_DEFAULT</code>, <code>GRUB_TERMINAL</code>, <code>GRUB_DISABLE_OS_PROBER</code>, <code>GRUB_SERIAL_COMMAND</code>, <code>GRUB_GFXMODE</code>, <code>GRUB_GFXPAYLOAD_LINUX</code>) from the backup's <code>/etc/default/grub</code> if they differ from the target's."
},
{
"phase": "1b — systemd-boot / ZFS path",
"detail": "For hosts using systemd-boot (typically ZFS-on-root). <code>_rs_hyd_kernel_cmdline</code> merges user tokens from <code>cmdline_extra</code> into the target's <code>/etc/kernel/cmdline</code>, keeping the target's own <code>root=</code>, <code>boot=</code> and <code>rootflags=</code> boilerplate intact."
},
{
"phase": "2 — /etc/modules merge",
"detail": "<code>_rs_hyd_modules</code> appends modules from <code>manifest.kernel_params.modules_loaded_at_boot</code> that are in the whitelist (<code>vfio</code>, <code>vfio_pci</code>, <code>vfio_iommu_type1</code>, <code>vfio_virqfd</code>, <code>kvm</code>, <code>kvm_intel</code>, <code>kvm_amd</code>, <code>nvidia</code>, <code>nvidia_drm</code>, <code>nvidia_modeset</code>, <code>nvidia_uvm</code>, <code>i915</code>, <code>xe</code>) AND not already present in the target's <code>/etc/modules</code>."
},
{
"phase": "3 — Whitelisted files copy",
"detail": "<code>_rs_hyd_files</code> copies user-authored files from the staging rootfs to the live target when the content differs. Whitelist covers VFIO/nvidia/blacklist files under <code>/etc/modprobe.d</code>, <code>/etc/modules-load.d</code>, and the ProxMenux VFIO bind rule + nvidia udev rules under <code>/etc/udev/rules.d</code>. Distro-owned files (<code>pve-blacklist.conf</code>, <code>mdadm.conf</code>, <code>nvme.conf</code>) are intentionally excluded — their contents evolve between releases."
},
{
"phase": "4 — Force post-boot reflows",
"detail": "The four phases write directly to the live target OUTSIDE the normal restore pipeline. To make the merged tokens/modules/files take effect on the next boot, <code>HB_HYDRATION_APPLIED=1</code> propagates through <code>plan.env</code> to <code>apply_pending_restore.sh</code>, which forces <code>NEEDS_INITRAMFS=1</code> and <code>NEEDS_GRUB=1</code> regardless of what was in the apply list. The post-boot dispatcher then regenerates initramfs and refreshes the bootloader."
}
]
},
"planCommit": {
@@ -65,8 +112,14 @@
"heading": "Concrete examples",
"intro": "The hydration pass is not abstract — it produces observable, correct outcomes for common scenarios. Two examples that hydration handles automatically:",
"rows": [
{ "scenario": "GPU passthrough (VFIO)", "detail": "Source had <code>intel_iommu=on iommu=pt</code> in the cmdline, <code>vfio</code>/<code>vfio_pci</code>/<code>vfio_iommu_type1</code> in <code>/etc/modules</code>, an <code>/etc/modprobe.d/vfio.conf</code> with <code>options vfio-pci ids=10de:2216</code>, and a <code>/etc/modprobe.d/blacklist-nvidia.conf</code>. Hydration merges the cmdline tokens into the target's GRUB or kernel cmdline, appends the vfio modules to <code>/etc/modules</code>, and copies the two user-authored modprobe files. On the next boot, IOMMU is active, VFIO modules load, the GPU is bound to vfio-pci and the VM comes up with passthrough working." },
{ "scenario": "Custom GRUB defaults", "detail": "Source had <code>GRUB_TIMEOUT=1</code> and <code>GRUB_DISABLE_OS_PROBER=true</code>. Hydration reads both keys from the backup's <code>/etc/default/grub</code>, sees they differ from the fresh install's defaults, and rewrites those two lines in the target's file (leaving everything else, including <code>GRUB_DISTRIBUTOR</code>, untouched)." }
{
"scenario": "GPU passthrough (VFIO)",
"detail": "Source had <code>intel_iommu=on iommu=pt</code> in the cmdline, <code>vfio</code>/<code>vfio_pci</code>/<code>vfio_iommu_type1</code> in <code>/etc/modules</code>, an <code>/etc/modprobe.d/vfio.conf</code> with <code>options vfio-pci ids=10de:2216</code>, and a <code>/etc/modprobe.d/blacklist-nvidia.conf</code>. Hydration merges the cmdline tokens into the target's GRUB or kernel cmdline, appends the vfio modules to <code>/etc/modules</code>, and copies the two user-authored modprobe files. On the next boot, IOMMU is active, VFIO modules load, the GPU is bound to vfio-pci and the VM comes up with passthrough working."
},
{
"scenario": "Custom GRUB defaults",
"detail": "Source had <code>GRUB_TIMEOUT=1</code> and <code>GRUB_DISABLE_OS_PROBER=true</code>. Hydration reads both keys from the backup's <code>/etc/default/grub</code>, sees they differ from the fresh install's defaults, and rewrites those two lines in the target's file (leaving everything else, including <code>GRUB_DISTRIBUTOR</code>, untouched)."
}
]
},
"callout": {
@@ -77,18 +130,41 @@
"heading": "Where the mechanisms live",
"intro": "For developers looking to trace or extend the cross-kernel behaviour:",
"rows": [
{ "component": "Direction detection", "location": "<code>hb_compat_check</code> in <code>lib_host_backup_common.sh</code>. Sets <code>HB_COMPAT_KERNEL_DIRECTION</code>." },
{ "component": "Safe-subset path list", "location": "<code>hb_unsafe_paths_cross_version</code> in <code>lib_host_backup_common.sh</code>. Emits <code>path\\treason</code> lines used by both the CLI filter and the Web endpoint <code>/api/host-backups/restore/prepare</code>." },
{ "component": "Hydration phases", "location": "<code>_rs_hyd_grub</code>, <code>_rs_hyd_kernel_cmdline</code>, <code>_rs_hyd_modules</code>, <code>_rs_hyd_files</code> in <code>backup_host.sh</code>. Orchestrated by <code>_rs_apply_bk_older_hydration</code>." },
{ "component": "Post-boot reflow forcing", "location": "<code>apply_pending_restore.sh</code> reads <code>HB_HYDRATION_APPLIED</code> from <code>plan.env</code> and forces <code>NEEDS_INITRAMFS=1</code>/<code>NEEDS_GRUB=1</code>." },
{ "component": "Web preview", "location": "<code>/api/host-backups/restore/prepare</code> in <code>flask_server.py</code>. Sources the helper library, runs <code>_rs_apply_bk_older_hydration</code> in plan mode, returns the actions to the Web modal." }
{
"component": "Direction detection",
"location": "<code>hb_compat_check</code> in <code>lib_host_backup_common.sh</code>. Sets <code>HB_COMPAT_KERNEL_DIRECTION</code>."
},
{
"component": "Safe-subset path list",
"location": "<code>hb_unsafe_paths_cross_version</code> in <code>lib_host_backup_common.sh</code>. Emits <code>path\\treason</code> lines used by both the CLI filter and the Web endpoint <code>/api/host-backups/restore/prepare</code>."
},
{
"component": "Hydration phases",
"location": "<code>_rs_hyd_grub</code>, <code>_rs_hyd_kernel_cmdline</code>, <code>_rs_hyd_modules</code>, <code>_rs_hyd_files</code> in <code>backup_host.sh</code>. Orchestrated by <code>_rs_apply_bk_older_hydration</code>."
},
{
"component": "Post-boot reflow forcing",
"location": "<code>apply_pending_restore.sh</code> reads <code>HB_HYDRATION_APPLIED</code> from <code>plan.env</code> and forces <code>NEEDS_INITRAMFS=1</code>/<code>NEEDS_GRUB=1</code>."
},
{
"component": "Web preview",
"location": "<code>/api/host-backups/restore/prepare</code> in <code>flask_server.py</code>. Sources the helper library, runs <code>_rs_apply_bk_older_hydration</code> in plan mode, returns the actions to the Web modal."
}
]
},
"whereNext": {
"heading": "Where to go next",
"items": [
{ "label": "Restoring", "href": "/docs/backup-restore/restoring", "tail": " — the full restore pipeline this page's mechanisms plug into." },
{ "label": "How it works", "href": "/docs/backup-restore/how-it-works", "tail": " — the manifest and collectors that produce the <code>kernel_params</code> block hydration reads from." }
{
"label": "Restoring",
"href": "/docs/backup-restore/restoring",
"tail": " — the full restore pipeline this page's mechanisms plug into."
},
{
"label": "How it works",
"href": "/docs/backup-restore/how-it-works",
"tail": " — the manifest and collectors that produce the <code>kernel_params</code> block hydration reads from."
}
]
}
}
@@ -24,19 +24,47 @@
"heading": "The borg binary",
"intro": "Borg is not a base dependency of the ProxMenux installer. When a backup runs, <code>hb_ensure_borg</code> resolves the binary from four sources in order and returns the first that works:",
"rows": [
{ "priority": "1", "source": "System <code>borg</code>", "detail": "If the host already has <code>borg</code> installed via APT (<code>which borg</code> resolves), that binary is used." },
{ "priority": "2", "source": "State-dir cache", "detail": "<code>/usr/local/share/proxmenux/borg</code> — kept from a prior GitHub download on this host." },
{ "priority": "3", "source": "Monitor AppImage bundle", "detail": "<code>/usr/local/share/proxmenux/monitor-app/usr/bin/borg</code> — the AppImage bundles a signed borg-linux64. This is the offline-safe path: a host with no internet still has a working <code>borg</code>." },
{ "priority": "4", "source": "GitHub download", "detail": "<code>wget</code> against the pinned <code>borg-linux64</code> URL under <code>github.com/borgbackup/borg/releases/</code>, verified against a constant SHA-256 in <code>lib_host_backup_common.sh</code> (<code>HB_BORG_LINUX64_SHA256</code>). On checksum mismatch the binary is discarded and the backup aborts. The downloaded file is cached in the state dir so subsequent backups use path 2." }
{
"priority": "1",
"source": "System <code>borg</code>",
"detail": "If the host already has <code>borg</code> installed via APT (<code>which borg</code> resolves), that binary is used."
},
{
"priority": "2",
"source": "State-dir cache",
"detail": "<code>/usr/local/share/proxmenux/borg</code> — kept from a prior GitHub download on this host."
},
{
"priority": "3",
"source": "Monitor AppImage bundle",
"detail": "<code>/usr/local/share/proxmenux/monitor-app/usr/bin/borg</code> — the AppImage bundles a signed borg-linux64. This is the offline-safe path: a host with no internet still has a working <code>borg</code>."
},
{
"priority": "4",
"source": "GitHub download",
"detail": "<code>wget</code> against the pinned <code>borg-linux64</code> URL under <code>github.com/borgbackup/borg/releases/</code>, verified against a constant SHA-256 in <code>lib_host_backup_common.sh</code> (<code>HB_BORG_LINUX64_SHA256</code>). On checksum mismatch the binary is discarded and the backup aborts. The downloaded file is cached in the state dir so subsequent backups use path 2."
}
]
},
"repoTypes": {
"heading": "Repository types",
"intro": "The repository type is chosen when a target is added. Each type resolves to a different repository URL that Borg understands.",
"rows": [
{ "type": "remote", "url": "ssh://USER@HOST/RPATH", "detail": "Repository lives on a remote server that runs <code>borg serve</code>. Requires an SSH connection to that server." },
{ "type": "usb", "url": "/mnt/MOUNTPOINT/borgbackup", "detail": "Repository lives on a mounted external disk (typically USB). The mount is resolved via <code>hb_prompt_mounted_path</code>, which detects, mounts or formats USB partitions as needed." },
{ "type": "local", "url": "/backup/borgbackup (any absolute path)", "detail": "Repository lives on a local directory. Meaningful only when the target directory is on a separate physical disk — a repository on the same disk as the source protects against operator error but not against disk failure." }
{
"type": "remote",
"url": "ssh://USER@HOST/RPATH",
"detail": "Repository lives on a remote server that runs <code>borg serve</code>. Requires an SSH connection to that server."
},
{
"type": "usb",
"url": "/mnt/MOUNTPOINT/borgbackup",
"detail": "Repository lives on a mounted external disk (typically USB). The mount is resolved via <code>hb_prompt_mounted_path</code>, which detects, mounts or formats USB partitions as needed."
},
{
"type": "local",
"url": "/backup/borgbackup (any absolute path)",
"detail": "Repository lives on a local directory. Meaningful only when the target directory is on a separate physical disk — a repository on the same disk as the source protects against operator error but not against disk failure."
}
]
},
"serverSetup": {
@@ -50,30 +78,62 @@
"Another Proxmox host on the same LAN, or a VM anywhere reachable over SSH."
],
"lxcWarningTitle": "LXC as Borg server",
"lxcWarningBody": "If the Borg server runs inside an LXC, the container must have a user account with a password.",
"lxcWarningBody": "If the Borg server runs inside an LXC, generate-auto requires an account that accepts SSH password authentication during key installation. If password login is unavailable, use manual key installation or the generate-pct alternative through the PVE host.",
"requirementsTitle": "Requirements on the server",
"requirementsRows": [
{ "requirement": "borg binary at <code>/usr/bin/borg</code>", "detail": "The <code>command=\"/usr/bin/borg serve ...\"</code> line ProxMenux writes into <code>authorized_keys</code> hard-codes that path. Installing via APT (<code>apt install borgbackup</code>) puts it there. A standalone binary must be symlinked to <code>/usr/bin/borg</code>." },
{ "requirement": "A dedicated user account (typically <code>borg</code>)", "detail": "Owns the repository directory and receives incoming SSH connections. Does not need sudo or shell access — the authorized_keys line disables interactive shells anyway." },
{ "requirement": "A writable repository directory", "detail": "The path the operator gives ProxMenux (for example <code>/backup/borgbackup</code>) must exist on the server and be owned by the borg user." },
{ "requirement": "SSH daemon accepting the borg user", "detail": "<code>PubkeyAuthentication yes</code> (default). Password authentication is only needed for the one-shot <em>generate-auto</em> flow — after the key is installed, the server can disable password auth entirely." }
{
"requirement": "borg binary at <code>/usr/bin/borg</code>",
"detail": "The <code>command=\"/usr/bin/borg serve ...\"</code> line ProxMenux writes into <code>authorized_keys</code> hard-codes that path. Installing via APT (<code>apt install borgbackup</code>) puts it there. A standalone binary must be symlinked to <code>/usr/bin/borg</code>."
},
{
"requirement": "A dedicated user account (typically <code>borg</code>)",
"detail": "Owns the repository directory and receives incoming SSH connections. Does not need sudo or shell access — the authorized_keys line disables interactive shells anyway."
},
{
"requirement": "A writable repository directory",
"detail": "The path the operator gives ProxMenux (for example <code>/backup/borgbackup</code>) must exist on the server and be owned by the borg user."
},
{
"requirement": "SSH daemon accepting the borg user",
"detail": "<code>PubkeyAuthentication yes</code> (default). Password authentication is only needed for the one-shot <em>generate-auto</em> flow — after the key is installed, the server can disable password auth entirely."
}
],
"minimalSetupTitle": "Minimal server setup",
"minimalSetupBody": "On a Debian or Ubuntu Borg server, a working baseline is four commands as root:",
"minimalSetupCmd": "apt install borgbackup\nuseradd -m -d /home/borg -s /bin/bash borg\nmkdir -p /backup/borgbackup\nchown borg:borg /backup/borgbackup",
"minimalSetupNote": "After this, ProxMenux's <code>generate-auto</code> mode can connect using the borg user's password once, install its own SSH key, and every subsequent backup uses that key. No further server-side configuration is needed — the key restricts itself to <code>borg serve</code> on that path."
"minimalSetupNote": "With the server prepared and SSH password authentication available, ProxMenux’s <code>generate-auto</code> mode can install its SSH key using an account permitted to write the borg user’s authorized_keys file. Subsequent backups use that key, restricted to <code>borg serve</code> on the configured path. Without password login, install the key manually or use the PVE-host alternative."
},
"sshAuth": {
"heading": "SSH authentication (client side)",
"intro": "Remote Borg repositories are accessed over SSH. The connection runs from the ProxMenux host to a user account on the Borg server that runs <code>borg serve</code>. This user is typically named <code>borg</code>, NOT the admin/root user of the server — the borg-serve command line locks the connection to that specific repository path (see the key strategies below).",
"strategiesTitle": "The four key strategies",
"strategiesIntro": "When a remote target is added, ProxMenux prompts for the SSH user, host and remote path, then asks how to authenticate. Four modes:",
"strategiesTitle": "SSH key strategies",
"strategiesIntro": "When a remote target is added, ProxMenux asks for the SSH user, host, port and remote path, then offers four authentication choices. The additional generate-pct path is offered when password authentication is refused. The modes are described below:",
"strategyRows": [
{ "mode": "generate-auto", "label": "Recommended", "detail": "ProxMenux generates a new ed25519 keypair at <code>~/.ssh/borg_proxmenux_HOST_ed25519</code>, then uses <code>sshpass</code> to log in ONCE to the server with the admin password and append the public key to <code>~borg/.ssh/authorized_keys</code>. The admin password is only used for this one call — it is never stored." },
{ "mode": "generate-manual", "label": "No admin password on this host", "detail": "ProxMenux generates the keypair as above but displays the full <code>authorized_keys</code> line for the operator to paste manually into the server. The admin password never leaves the ProxMenux host because it is never asked for." },
{ "mode": "generate-pct", "label": "Borg server is a PVE LXC", "detail": "The Borg server runs inside an LXC on a PVE node. ProxMenux authorises the key via <code>pct exec</code> from the PVE host — root on the PVE host writes into the LXC's <code>~borg/.ssh/authorized_keys</code> without needing SSH into the LXC itself." },
{ "mode": "existing", "label": "Use an existing key", "detail": "ProxMenux scans <code>/root/.ssh/</code> and <code>$HOME/.ssh/</code> for parseable ed25519/RSA private keys and lists them. The operator picks one or browses manually to a non-standard path." },
{ "mode": "none", "label": "Default SSH config", "detail": "No custom key — Borg relies on the host's default SSH configuration (typically <code>~/.ssh/id_rsa</code> or an SSH agent)." }
{
"mode": "generate-auto",
"label": "Recommended",
"detail": "ProxMenux generates or reuses an ed25519 keypair at <code>~/.ssh/borg_proxmenux_HOST_ed25519</code>, then uses <code>sshpass</code> for a login probe and installation of the public key in <code>~borg/.ssh/authorized_keys</code>. The password belongs to an account allowed to write that file, usually root or the borg user. It is used during setup and is not saved."
},
{
"mode": "generate-manual",
"label": "No admin password on this host",
"detail": "ProxMenux generates the keypair as above but displays the full <code>authorized_keys</code> line for the operator to paste manually into the server. The admin password never leaves the ProxMenux host because it is never asked for."
},
{
"mode": "generate-pct",
"label": "Borg server is a PVE LXC",
"detail": "The Borg server runs inside an LXC on a PVE node. ProxMenux authorises the key via <code>pct exec</code> from the PVE host — root on the PVE host writes into the LXC's <code>~borg/.ssh/authorized_keys</code> without needing SSH into the LXC itself."
},
{
"mode": "existing",
"label": "Use an existing key",
"detail": "ProxMenux scans <code>/root/.ssh/</code> and <code>$HOME/.ssh/</code> for parseable ed25519/RSA private keys and lists them. The operator picks one or browses manually to a non-standard path."
},
{
"mode": "none",
"label": "Default SSH config",
"detail": "No custom key — Borg relies on the host's default SSH configuration (typically <code>~/.ssh/id_rsa</code> or an SSH agent)."
}
],
"restrictTitle": "The authorized_keys line",
"restrictBody": "For every generated key, the <code>authorized_keys</code> line ProxMenux writes on the server locks the key to a single borg-serve invocation against the configured repository path:",
@@ -84,24 +144,50 @@
"heading": "Saved targets",
"intro": "A saved target persists the repository configuration under a friendly name so the details do not have to be re-entered. Storage layout in the ProxMenux state directory:",
"rows": [
{ "file": "borg-targets.txt", "content": "One line per target: <code>NAME|REPO|SSH_KEY_PATH|ENCRYPT_MODE</code>. Read by <code>hb_collect_borg_configs</code> to populate the target-selection menu." },
{ "file": "borg-pass-NAME.txt", "content": "Passphrase for the target NAMEd above (<code>chmod 600</code>). Only present when the operator chose repokey encryption." }
{
"file": "borg-targets.txt",
"content": "One line per target: <code>NAME|REPO|SSH_KEY_PATH|ENCRYPT_MODE</code>. Read by <code>hb_collect_borg_configs</code> to populate the target-selection menu."
},
{
"file": "borg-pass-NAME.txt",
"content": "Passphrase for the target NAMEd above (<code>chmod 600</code>). Only present when the operator chose repokey encryption."
}
],
"outro": "Saving is optional — the operator can decline the save prompt for a one-shot backup that leaves no credentials on the host."
"outro": "Saving the target is optional. Declining the save prompt omits the named target configuration, but does not remove a generated SSH key. If repokey encryption is enabled without a saved target, ProxMenux still saves the passphrase in the legacy borg-pass.txt file."
},
"encryption": {
"heading": "Encryption",
"body": "Repositories are initialised with an encryption mode via <code>hb_borg_init_if_needed</code>: the modern <code>borg repo-create -e MODE</code> when available, or the legacy <code>borg init --encryption=MODE</code>. ProxMenux exposes two modes: <code>repokey</code> (default) and <code>none</code>. In <code>repokey</code> mode Borg stores the encryption key inside the repository itself; access requires a passphrase, which ProxMenux prompts for twice with match validation and stores at <code>borg-pass-NAME.txt</code>. A mandatory acknowledgement dialog is shown after the passphrase is saved: the passphrase is the only way to access the encrypted archives, and losing it makes every archive in the repository unrecoverable."
"body": "Repositories are initialised with an encryption mode via <code>hb_borg_init_if_needed</code>: the modern <code>borg repo-create -e MODE</code> when available, or the legacy <code>borg init --encryption=MODE</code>. ProxMenux exposes two modes: <code>repokey</code> (default) and <code>none</code>. In <code>repokey</code> mode Borg stores the encryption key inside the repository itself; access requires a passphrase, which ProxMenux prompts for twice with match validation and stores at <code>borg-pass-NAME.txt</code> for saved targets (or in the legacy file described above). A mandatory acknowledgement dialog is shown after the passphrase is saved: the passphrase is the only way to access the encrypted archives, and losing it makes every archive in the repository unrecoverable."
},
"runtimeEnv": {
"heading": "Runtime environment",
"intro": "ProxMenux exports the following environment variables before invoking <code>borg</code>. They configure the connection and unlock the repository without embedding secrets in the command line.",
"rows": [
{ "var": "BORG_RSH", "value": "ssh -i SSH_KEY_PATH -o StrictHostKeyChecking=accept-new", "purpose": "The remote-shell command Borg uses for SSH-served repositories. Set only when a custom key was selected; otherwise unset so Borg uses the default SSH configuration." },
{ "var": "BORG_PASSPHRASE", "value": "(passphrase from borg-pass-NAME.txt)", "purpose": "Unlocks the repokey. Set only when the target uses <code>repokey</code> encryption. Never appears in the process argument list." },
{ "var": "BORG_ENCRYPT_MODE", "value": "repokey | none", "purpose": "Used by <code>hb_borg_init_if_needed</code> when initialising a repository that does not yet exist. Ignored by <code>borg create</code> on existing repositories." },
{ "var": "BORG_RELOCATED_REPO_ACCESS_IS_OK", "value": "yes", "purpose": "Suppresses the interactive prompt Borg raises when the repository URL differs from the URL it was originally reached from (common after a mount-point rename or SSH-host reachable via new address)." },
{ "var": "BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK", "value": "yes", "purpose": "Suppresses the interactive prompt Borg raises the first time an unencrypted repository is accessed from a new client." }
{
"var": "BORG_RSH",
"value": "ssh -i SSH_KEY_PATH -o IdentitiesOnly=yes -o StrictHostKeyChecking=accept-new [-p PORT]",
"purpose": "The remote-shell command Borg uses for SSH repositories. A selected key adds the identity options. When a target is first added with a non-default port, ProxMenux also appends -p PORT; saved targets, scheduled jobs and the Monitor rely on the port in the ssh:// URL instead. Without a custom key, and for a newly added target with the default port, the variable is unset so Borg uses the host’s default SSH configuration."
},
{
"var": "BORG_PASSPHRASE",
"value": "(passphrase from borg-pass-NAME.txt)",
"purpose": "Unlocks the repokey. Set only when the target uses <code>repokey</code> encryption. Never appears in the process argument list."
},
{
"var": "BORG_ENCRYPT_MODE",
"value": "repokey | none",
"purpose": "Used by <code>hb_borg_init_if_needed</code> when initialising a repository that does not yet exist. Ignored by <code>borg create</code> on existing repositories."
},
{
"var": "BORG_RELOCATED_REPO_ACCESS_IS_OK",
"value": "yes",
"purpose": "Suppresses the interactive prompt Borg raises when the repository URL differs from the URL it was originally reached from (common after a mount-point rename or SSH-host reachable via new address)."
},
{
"var": "BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK",
"value": "yes",
"purpose": "Suppresses the interactive prompt Borg raises the first time an unencrypted repository is accessed from a new client."
}
]
},
"archiveFormat": {
@@ -14,7 +14,7 @@
},
"intro": {
"title": "Same payload, three backends",
"body": "Every backup produces the same three payloads (rootfs, manifest, applications). What changes between destinations is how those payloads are stored and how the operator retrieves them for a restore. Each backend is implemented as a separate function in <code>backup_host.sh</code> — <code>_bk_local</code>, <code>_bk_pbs</code>, <code>_bk_borg</code> — but all four share the same staging step (<code>hb_prepare_staging</code>) and the same restore code path. The choice of destination only affects the write and the retrieval; it does not change what a restore does or how it consumes the archive."
"body": "Every backup produces the same three payloads (rootfs, manifest, applications). What changes between destinations is how those payloads are stored and how the operator retrieves them for a restore. Each backend is implemented as a separate function in <code>backup_host.sh</code> — <code>_bk_local</code>, <code>_bk_pbs</code>, <code>_bk_borg</code> — but all three share the same staging step (<code>hb_prepare_staging</code>) and the same restore code path. The choice of destination only affects the write and the retrieval; it does not change what a restore does or how it consumes the archive."
},
"comparison": {
"heading": "Feature comparison",
@@ -29,8 +29,8 @@
{
"feature": "Compression",
"local": "zstd (default level) via <code>tar --zstd</code>. gzip fallback.",
"pbs": "PBS-managed. The client streams uncompressed; the server chunks + compresses.",
"borg": "Borg's built-in (lz4 default). Configurable at repo init."
"pbs": "Managed by proxmox-backup-client. ProxMenux does not override compression settings.",
"borg": "Borg’s built-in compression. ProxMenux invokes borg create without overriding compression settings."
},
{
"feature": "Deduplication",
@@ -41,7 +41,7 @@
},
"backupCommand": {
"heading": "The backup command",
"intro": "The upload is a single invocation of <code>proxmox-backup-client backup</code>. ProxMenux runs it inside an <code>env</code> wrapper so credentials never appear in the process argument list.",
"intro": "The upload is a single invocation of <code>proxmox-backup-client backup</code>. ProxMenux uses <code>env</code> to pass the password, encryption password and fingerprint to the client as environment variables.",
"cmd": "env \\\n PBS_PASSWORD=\"$HB_PBS_SECRET\" \\\n PBS_ENCRYPTION_PASSWORD=\"$HB_PBS_ENC_PASS\" \\\n PBS_FINGERPRINT=\"$HB_PBS_FINGERPRINT\" \\\n proxmox-backup-client backup \\\n hostcfg.pxar:$staging_root \\\n --repository USER@REALM@HOST:DATASTORE \\\n --backup-type host \\\n --backup-id hostcfg-HOSTNAME \\\n --backup-time BACKUP-EPOCH \\\n [--keyfile /usr/local/share/proxmenux/pbs-key.conf]",
"backupIdTitle": "Backup ID naming",
"backupIdBody": "The default backup ID is <code>hostcfg-HOSTNAME</code>. The operator is asked to confirm or edit it before the upload; any characters outside <code>[A-Za-z0-9_-]</code> are stripped and trailing dashes are trimmed. Reusing the same ID across runs is intentional — PBS treats the ID as a <em>group</em>, and every subsequent backup appears as a new backup inside that group, sharing dedup with prior runs.",
@@ -61,13 +61,13 @@
"modesSharedTitle": "Shared keyfile (import on every host)",
"modesSharedBody": "One master keyfile generated once and installed on every host via the <em>Use an existing keyfile</em> option. Management is simpler: a single secret to safeguard, and any host can decrypt the archives of any other (useful for consolidation, cross-host restore drills or centralised backup verification). The trade-off is that a leak of the shared keyfile exposes every host at once. Recommended for homelabs and for fleets where all hosts have the same owner and trust boundary.",
"recoveryTitle": "Upload key to PBS — the operator decides",
"recoveryBody": "Right after the keyfile is chosen, ProxMenux asks a single explicit question — <em>Upload key to PBS?</em> — with two answers. <strong>Nothing gets uploaded to PBS until the operator answers Yes.</strong> The default is <em>No, keep local only</em>: the keyfile stays only at the canonical local path, ProxMenux never touches PBS with any recovery artefact, and the operator handles the offsite copy themselves (the Monitor's <em>Download keyfile</em> button in the PBS destination row provides one on demand). Choosing <em>Yes, upload</em> prompts for a recovery passphrase — twice, with match validation — and ProxMenux wraps the keyfile with that passphrase using AES-256-CBC and PBKDF2 (600 000 iterations, random salt) to produce <code>pbs-key.recovery.enc</code>. Every subsequent encrypted backup uploads that envelope to PBS as a paired backup group. Neither the passphrase nor a plaintext copy of the keyfile ever leaves the host. The choice can be changed later from the Monitor at any time — the escrow toggle inline in the PBS destination row flips Yes/No and applies immediately (Start uploading, Stop uploading, or Update passphrase, matching the current state).",
"recoveryBody": "Right after the keyfile is chosen, ProxMenux asks a single explicit question — <em>Upload key to PBS?</em> — with two answers. <strong>No recovery artifact is uploaded to PBS until the operator answers Yes.</strong> The default is <em>No, keep local only</em>: the keyfile stays at the canonical local path, ProxMenux uploads no recovery artifact to PBS, and the operator handles the offsite copy themselves (the Monitor’s <em>Download keyfile</em> button in the PBS destination row provides one on demand). Choosing <em>Yes, upload</em> prompts for a recovery passphrase — twice, with match validation — and ProxMenux wraps the keyfile with that passphrase using AES-256-CBC and PBKDF2 (600 000 iterations, random salt) to produce <code>pbs-key.recovery.enc</code>. Every subsequent encrypted backup uploads that envelope to PBS as a paired backup group. Neither the passphrase nor a plaintext copy of the keyfile is sent to PBS during these uploads. The choice can be changed later from the Monitor at any time — the escrow toggle inline in the PBS destination row flips Yes/No and applies immediately (Start uploading, Stop uploading, or Update passphrase, matching the current state).",
"blobUploadTitle": "Paired backup group on PBS",
"blobUploadBody1": "When the escrow mode is <em>Yes, upload</em>, ProxMenux uploads the recovery envelope to PBS after every encrypted backup as a second, paired backup group with the same name as the host group but ending in <code>-keyrecovery</code>. The two groups sit next to each other in the datastore listing — one for host backups, one for recovery envelopes. <strong>The envelope never leaves the host in plaintext</strong>: encryption happens on the host itself with AES-256-CBC and PBKDF2 before any bytes go on the wire (<code>openssl enc -aes-256-cbc -pbkdf2 -iter 600000 -salt</code>). PBS receives only the already-encrypted envelope.",
"envelopeSecurityTitle": "Is uploading the keyrecovery file to PBS a security risk?",
"envelopeSecurityBody": "The keyrecovery file is encrypted in transit and at rest. The recovery passphrase never leaves the host: encryption happens locally before the upload, and PBS only ever sees the already-encrypted result. A PBS administrator — or anyone with access to the datastore — can download the keyrecovery file, but without the recovery passphrase they cannot read the keyfile inside: it is opaque ciphertext. Reconstructing the keyfile requires both pieces at once, the keyrecovery file and the passphrase, and only the operator holds both.",
"blobUploadConstraintTitle": "Why two groups instead of a single encrypted one",
"blobUploadConstraintBody": "A single <code>proxmox-backup-client backup</code> invocation encrypts every one of its archives with the same keyfile, or none at all — there is no in-between. The host backup has to be encrypted with the host keyfile, but the recovery envelope cannot ride along in that same upload, because it would end up encrypted with the very keyfile it contains: on a freshly reinstalled host there would be no way to open it (you would need the keyfile to decrypt the keyfile). That is why the two artefacts are uploaded independently and appear as two separate groups: the host backup is encrypted by PBS with the keyfile, and the recovery envelope is encrypted by ProxMenux with the passphrase before upload. Two different encryption layers protecting two different assets.",
"blobUploadConstraintBody": "A single <code>proxmox-backup-client backup</code> invocation encrypts every one of its archives with the same keyfile, or none at all — there is no in-between. The host backup has to be encrypted with the host keyfile, but the recovery envelope cannot ride along in that same upload, because it would end up encrypted with the very keyfile it contains: on a freshly reinstalled host there would be no way to open it (you would need the keyfile to decrypt the keyfile). That is why the two artefacts are uploaded independently and appear as two separate groups: the host backup is encrypted by the PBS client with the keyfile, and the recovery envelope is encrypted by ProxMenux with the passphrase before upload. Two different encryption layers protecting two different assets.",
"blobUploadImageAlt": "PBS UI showing the hostcfg-HOSTNAME and hostcfg-HOSTNAME-keyrecovery backup groups adjacent in the datastore listing.",
"blobUploadImageCaption": "PBS UI — the paired backup groups. The main group holds the host backups; the -keyrecovery group holds the passphrase-wrapped keyfile.",
"recoverTitle": "Recovery on a fresh host",
@@ -28,7 +28,7 @@
{
"id": "clave-cifrado",
"term": "Keyfile (encryption key)",
"def": "Binary file that Proxmox Backup Server uses to encrypt the <a href=\"#chunk\">chunks</a> of each backup before uploading them. Without this file, no encrypted backup can be read. ProxMenux stores it at <code>/usr/local/share/proxmenux/pbs-key.conf</code> with mode <code>600</code>. This is the asset that has to be protected; the <a href=\"#sobre-recuperacion\">recovery envelope</a> exists precisely so that this file can be rescued if it is lost."
"def": "Key file used by proxmox-backup-client to encrypt the <a href=\"#chunk\">chunks</a> before uploading them to PBS. Without the matching key, encrypted backup data cannot be read. ProxMenux stores the file at <code>/usr/local/share/proxmenux/pbs-key.conf</code> with mode <code>600</code>. Protect it: the <a href=\"#sobre-recuperacion\">recovery envelope</a> is designed to recover this file if it is lost."
},
{
"id": "contrasena-keyfile",
@@ -156,7 +156,7 @@
{
"id": "modo-adjunto",
"term": "Attach mode",
"def": "Scheduled job with no schedule of its own; it fires automatically whenever an existing PVE <code>vzdump</code> task runs. It inherits the schedule and retention from that parent task. Supported for Local and PBS destinations only."
"def": "Job without its own timer. A PVE <code>vzdump</code> job-end hook triggers it after guest backups when the destination storage matches. The selected parent supplies the storage and the retention, which is re-read from the parent job on every run, but the hook matches storage rather than parent job ID; another task using that storage may also trigger it. Supported for Local and PBS only."
},
{
"id": "timer-systemd",
@@ -14,7 +14,7 @@
},
"intro": {
"title": "One archive, three payloads",
"body": "Every backup produces a directory layout with three well-defined payloads under a single staging root. The archive uploaded to the destination (local <code>.tar.zst</code>, PBS backup or Borg archive) contains this exact layout. The restore reads the three payloads independently, in a specific order that guarantees correctness: <strong>rootfs</strong> is copied first to lay down configuration, <strong>the manifest</strong> is consulted to detect drift and decide what to skip, and <strong>the application inventory</strong> drives the post-boot reinstall pass. No cross-payload dependencies — each one can be inspected or extracted independently."
"body": "Every backup produces a directory layout with three well-defined payloads under a single staging root. The archive uploaded to the destination (local <code>.tar.zst</code>, PBS backup or Borg archive) contains this exact layout. The restore uses all three: <strong>the manifest</strong> is consulted before applying changes to detect drift and decide what to skip; <strong>rootfs</strong> supplies the configuration files, applied live or deferred until reboot; and <strong>the application inventory</strong> drives package installation and the post-boot component reinstall. Each payload can be inspected or extracted independently."
},
"layout": {
"heading": "Archive layout",
@@ -26,7 +26,7 @@
"heading": "The rootfs payload",
"intro": "The <code>rootfs/</code> tree is a <strong>plain filesystem copy</strong> produced by <code>rsync</code> from the source host. It contains a curated <strong>default profile</strong> of paths that matter for a Proxmox restore, plus any <strong>custom paths</strong> added by the user to the backup job or interactive session. The set is deliberately narrow: only paths that either <em>hold configuration</em> or <em>hold state that Proxmox cannot regenerate on its own</em>.",
"defaultProfileTitle": "The default profile",
"defaultProfileBody": "The default profile is defined by <code>hb_default_profile_paths</code> in <code>lib_host_backup_common.sh</code>. It covers eight categories that together describe a working Proxmox host:",
"defaultProfileBody": "The default profile is defined by <code>hb_default_profile_paths</code> in <code>lib_host_backup_common.sh</code>. It covers eight fixed categories, plus conditional ZFS state, that together describe a working Proxmox host:",
"categoriesTitle": "Path categories",
"categoryRows": [
{
@@ -125,7 +125,7 @@
},
"applications": {
"heading": "The application inventory",
"intro": "Two files under <code>metadata/</code> catalogue everything installed on the source that is not part of the base Proxmox VE package set. The restore uses them to reproduce the exact set of user-installed software on the target, using either APT or ProxMenux's own installers depending on how the software was originally installed.",
"intro": "Two inventories catalogue the software installed on the source beyond the base Proxmox VE package set: the package list under <code>metadata/</code> and the component registry inside rootfs, detailed below. The restore uses them to reproduce user-installed software with APT or ProxMenux’s own installers, depending on how it was originally installed.",
"packagesTitle": "packages.manual.list",
"packagesBody": "A plain-text list produced by <code>apt-mark showmanual</code>: every APT package that was <em>explicitly installed</em> on the source host, sorted alphabetically. This excludes packages installed as dependencies of the base Proxmox VE ISO (they are re-pulled automatically by the target's own APT). Read by <code>_rs_run_complete_extras</code> during restore, filtered through a three-pass <em>cascade-safe</em> filter (<code>dpkg -s</code> for already-installed, sibling-major detection for library packages, <code>apt-get install --simulate</code> for cascade-remove risk), then installed with <code>apt-get install -y</code>.",
"componentsTitle": "components_status.json (part of the rootfs)",
@@ -14,7 +14,7 @@
},
"intro": {
"title": "The restore reproduces the source, not just the files",
"body": "Restoring a ProxMenux backup is not an extraction. Files are only the first step: after the filesystem is in place, the restore reads the manifest to detect differences between source and target, filters paths that would break the target's boot, hydrates user-authored tuning that cannot be copied verbatim, and — after the mandatory reboot — reinstalls every component the source had (NVIDIA driver, Coral TPU, GPU tools) against the target's own kernel. What the user picks from the menu is a single archive; what actually happens involves the compatibility check, the path classification, the on-boot dispatcher and the post-boot pass working together to produce a host that behaves like the source."
"body": "Restoring a ProxMenux backup involves more than extracting files. Before applying changes, the restore reads the manifest to detect differences between source and target, filters paths that could break the target’s boot and adapts user-authored tuning that cannot be copied verbatim. After the required reboot, it attempts to reinstall the recorded supported components (NVIDIA driver, Coral TPU, GPU tools) for the target’s running kernel. A single selected archive drives the compatibility check, path classification, on-boot dispatcher and post-boot work. Review the per-component results and logs to confirm the outcome."
},
"threeActions": {
"heading": "Three actions on a backup",
@@ -52,7 +52,7 @@
},
{
"output": "Rollback plan",
"detail": "Computed by <code>compute_rollback_plan.sh</code>. Lists VMs, LXCs and components present on the target but not in the backup. The user opts in during the confirmation dialog to have these removed as part of the restore, so the target ends up matching the backup exactly."
"detail": "Computed by <code>compute_rollback_plan.sh</code>. Lists VMs, LXCs and components present on the target but absent from the backup. The confirmation dialog offers optional removal of the extra guests before reboot. Component removal remains manual, and guest deletion can fail, so this does not guarantee an exact match with the backup."
}
],
"reportBody": "The check also emits a structured report (<code>HB_COMPAT_RESULTS</code>) categorised as PASS / INFO / WARN / FAIL. WARN and FAIL entries surface in the pre-restore panel; the restore refuses to proceed only when a FAIL is present that the user cannot resolve by clicking Continue."
@@ -81,11 +81,11 @@
},
{
"class": "reboot",
"detail": "Paths applied <strong>immediately</strong> as well, but whose actual effect only kicks in on the <strong>next boot</strong>: the kernel only reads <code>/etc/default/grub</code> when the bootloader starts, <code>/etc/fstab</code> when filesystems are mounted at boot, <code>/etc/modules</code> when modules are loaded, etc. The file is in place the moment it is applied but the system has to reboot to consume it. Examples: <code>/etc/default/grub</code>, <code>/etc/kernel</code>, <code>/etc/modules</code>, <code>/etc/fstab</code>, <code>/etc/zfs</code>, <code>/etc/initramfs-tools</code>."
"detail": "Paths deferred to the <strong>next boot</strong>, rather than applied <strong>immediately</strong> by this restore flow. They affect boot-time configuration: bootloader generation reads <code>/etc/default/grub</code>, filesystem mounting uses <code>/etc/fstab</code>, and module loading uses <code>/etc/modules</code>. The pending files are applied after reboot and the required boot artifacts are then regenerated. Examples: <code>/etc/default/grub</code>, <code>/etc/kernel</code>, <code>/etc/modules</code>, <code>/etc/fstab</code>, <code>/etc/zfs</code>, <code>/etc/modprobe.d</code>."
},
{
"class": "dangerous",
"detail": "Paths that are <strong>NOT applied on the running system</strong> — a live write could corrupt state or drop the active connection. These are staged in the pending set and written by the post-boot dispatcher after the reboot, when the cluster is up but the system is not yet fully in use. Examples: <code>/etc/pve</code> (pmxcfs is a live FUSE mount; writing directly to it bypasses it), <code>/var/lib/pve-cluster</code> (live cluster data), <code>/etc/network</code> (could reconfigure the very interface the user is connected on over SSH and drop the session)."
"detail": "Paths that are <strong>NOT applied on the running system</strong> — a live write could corrupt state or drop the active connection. These are staged in the pending set: <code>/etc/network</code> is written early in the next boot, before networking starts, and <code>/etc/pve</code> is written by the post-boot dispatcher once the cluster filesystem is up. Examples: <code>/etc/pve</code> (pmxcfs is a live FUSE mount holding active cluster configuration), <code>/var/lib/pve-cluster</code> (live cluster data, kept in the recovery directory for manual recovery only), <code>/etc/network</code> (could reconfigure the very interface the user is connected on over SSH and drop the session)."
}
]
},
@@ -131,7 +131,7 @@
},
{
"task": "Component auto-reinstall",
"detail": "Reads the restored <code>components_status.json</code> and iterates over the registered installers (nvidia_driver, coral_driver, amdgpu_top, intel_gpu_tools). Each installer runs in <code>--auto-reinstall</code> mode, reads its previously-recorded version from the state file, and rebuilds against the target's current kernel. The interactive flow does not do this — reinstalling drivers against a kernel that has not yet booted is meaningless."
"detail": "Reads the restored <code>components_status.json</code> and iterates over the registered supported installers (nvidia_driver, coral_driver, amdgpu_top, intel_gpu_tools). Each selected installer runs in <code>--auto-reinstall</code> mode, reads its recorded state and reinstalls for the target’s environment, rebuilding kernel modules when applicable. This is deferred until after reboot so the installers use the kernel then running on the target. Individual failures are recorded and do not stop the next component."
},
{
"task": "Boot sanity check",
@@ -145,7 +145,7 @@
},
"liveProgress": {
"heading": "Live progress in the Monitor's Backups tab",
"body": "After the reboot the Backups tab of ProxMenux Monitor renders a live post-restore card that reflects the state of <code>apply_cluster_postboot.service</code> as it runs. The card is driven by <code>/var/lib/proxmenux/restore-state.json</code>, which the dispatcher updates at every milestone (apply cluster config, rebuild initramfs, update bootloader, per-component reinstall, boot sanity check, finalize).",
"body": "After the reboot the Backups tab of ProxMenux Monitor renders a live post-restore card that reflects the state of <code>proxmenux-apply-cluster-postboot.service</code> as it runs. The card is driven by <code>/var/lib/proxmenux/restore-state.json</code>, which the dispatcher updates at every milestone (apply cluster config, rebuild initramfs, update bootloader, per-component reinstall, boot sanity check, finalize).",
"fields": [
{
"name": "Status badge",
@@ -184,9 +184,9 @@
},
"postbootExample": {
"heading": "What the post-boot looks like on the console",
"body": "On the host's physical console, a successful dispatcher run ends with a <code>[ OK ] Finished proxmenux-apply-cluster-postboot.service</code> line. When that line appears — usually alongside the <code>OK</code>s for <code>multi-user.target</code> and <code>graphical.target</code> — the restore is completely finished: components reinstalled, boot artifacts regenerated, cluster reconciled.",
"body": "On the host’s physical console, a dispatcher run that exits successfully ends with <code>[ OK ] Finished proxmenux-apply-cluster-postboot.service</code>, often near the <code>OK</code>s for <code>multi-user.target</code> and <code>graphical.target</code>. This marks the end of the dispatcher process. Individual component failures or boot warnings can still have been recorded; check the Monitor’s component states and logs before treating the host as fully restored.",
"imageAlt": "Proxmox physical console showing '[ OK ] Finished proxmenux-apply-cluster-postboot.service - ProxMenux Apply Cluster Configs (post-boot).' followed by the OK for multi-user.target and graphical.target, with the host login prompt above.",
"imageCaption": "Physical console after a successful post-boot. The 'Finished proxmenux-apply-cluster-postboot.service' line is the signal that the restore flow is fully complete — the same event the 'Host restore finished' notification surfaces in the Monitor."
"imageCaption": "Physical console after the dispatcher process finishes. 'Finished proxmenux-apply-cluster-postboot.service' marks process completion, also reported by 'Host restore finished' in the Monitor. Component results and warnings still need review."
},
"tenMinutes": {
"heading": "Why the last ten minutes matter",
@@ -228,11 +228,11 @@
"detail": "Cheap. The user learns the run completed the moment the notification fires."
}
],
"outroBody": "The window matters because the user can log in during this time and see missing tools (<code>nvidia-smi</code> not found, <code>coral</code> not detected). This is expected — the reinstall is still running in the background. The completion notification signals when everything is ready."
"outroBody": "During this window a login may show missing tools (<code>nvidia-smi</code> not found, <code>coral</code> not detected) because reinstall work is still running in the background. The completion notification marks the end of that work; check component results, warnings and logs to distinguish success from individual failures."
},
"destructiveRollback": {
"heading": "The optional destructive rollback",
"body": "A restore is additive by default: if the target has VMs, LXCs or components that are not in the backup, they survive. This preserves work but can leave stale hostpci entries, orphan components or VMs the user no longer wants. When drift is detected between target and backup, the confirmation dialog offers a second yes/no: <em>Execute destructive rollback?</em>. Accepting it triggers <code>_rs_execute_rollback</code>, which deletes the VMs, LXCs and components that exist on the target but not in the backup. The rollback runs BEFORE the reboot so that the pending restore machinery sees a clean state. This is opt-in — the default is No, and the user sees exactly what would be removed listed by name (VMID and CTID) before deciding."
"body": "A restore is additive by default: extra VMs, LXCs and components on the target remain. When differences are detected, the confirmation dialog offers <em>Execute destructive rollback?</em>, defaulting to No. Accepting calls <code>_rs_execute_rollback</code> before reboot to attempt deletion of listed extra VMs and LXCs. Components are flagged for manual uninstall because automatic uninstall is not implemented. Guest deletion failures are reported and processing continues, so an exact match with the backup is not guaranteed. Review the listed names and VMIDs/CTIDs before choosing this destructive option."
},
"logs": {
"heading": "Where the logs live",
@@ -22,7 +22,7 @@
},
"attachBadge": {
"title": "Attach mode — recommended when a PVE vzdump task already exists",
"body": "When a PVE vzdump backup task is already configured for the VMs and LXCs on this node, attaching the host backup to that task guarantees the host configuration is captured in the <strong>same window</strong> as the guests. On restore, the guest configs come from the host backup (they live under <code>/etc/pve</code>), and the guest disks come from the vzdump backup taken alongside — both sets are consistent with each other, so a full-node recovery can reproduce the host and re-attach every guest without version drift."
"body": "When a PVE vzdump backup task is already configured for the VMs and LXCs on this node, attaching the host backup runs it after the guest backup job, in the <strong>same backup cycle</strong>. On restore, guest configurations come from the host backup (under <code>/etc/pve</code>) and guest disks come from the vzdump backups taken in that cycle. This helps coordinate a full-node recovery, but is not an atomic snapshot: changes made during the backup cycle can leave differences between host configuration and guest disk state."
},
"frequencyBadge": {
"title": "Recommended frequency",
@@ -31,29 +31,63 @@
"modes": {
"heading": "The two modes",
"rows": [
{ "mode": "New scheduled job", "backends": "Local, PBS, Borg", "schedule": "Own <code>OnCalendar</code> expression (systemd calendar syntax — e.g. <code>daily</code>, <code>Mon..Fri 03:00</code>).", "retention": "Prompted separately: <code>keep-last</code>, <code>keep-hourly</code>, <code>keep-daily</code>, <code>keep-weekly</code>, <code>keep-monthly</code>, <code>keep-yearly</code>. Applied by the runner after each successful backup." },
{ "mode": "Attach to a PVE vzdump job", "backends": "Local, PBS (Borg has no PVE-side scheduler)", "schedule": "Inherited from the parent PVE job. No systemd timer is installed on the ProxMenux side.", "retention": "Inherited from the parent's <code>prune-backups</code> configuration (mapped one-to-one to the runner's <code>KEEP_*</code> variables via <code>hb_pve_prune_to_keep_env</code>)." }
{
"mode": "New scheduled job",
"backends": "Local, PBS, Borg",
"schedule": "Own <code>OnCalendar</code> expression (systemd calendar syntax — e.g. <code>daily</code>, <code>Mon..Fri 03:00</code>).",
"retention": "Prompted separately: <code>keep-last</code>, <code>keep-hourly</code>, <code>keep-daily</code>, <code>keep-weekly</code>, <code>keep-monthly</code>, <code>keep-yearly</code>. Applied by the runner after each successful backup."
},
{
"mode": "Attach to a PVE vzdump job",
"backends": "Local, PBS (Borg has no PVE-side scheduler)",
"schedule": "Inherited from the parent PVE job. No systemd timer is installed on the ProxMenux side.",
"retention": "Inherited from the parent's <code>prune-backups</code> configuration (mapped one-to-one to the runner's <code>KEEP_*</code> variables via <code>hb_pve_prune_to_keep_env</code>)."
}
]
},
"attachDetail": {
"heading": "How attach mode works",
"intro": "PVE writes <code>vzdump</code> tasks to <code>/etc/pve/jobs.cfg</code> — one stanza per task, each pointing at a storage where the VM and LXC dumps land. Attach mode requires that storage to be a backend ProxMenux understands (Local or PBS); when the task fires, ProxMenux runs alongside it.",
"intro": "PVE writes <code>vzdump</code> tasks to <code>/etc/pve/jobs.cfg</code> — one stanza per task, each pointing at a storage where the VM and LXC dumps land. Attach mode requires a backend ProxMenux understands (Local or PBS). The host backup runs from the job-end hook after the guest backup job finishes.",
"steps": [
{ "step": "1", "detail": "During job creation, ProxMenux lists compatible parent PVE tasks via <code>hb_pve_list_vzdump_jobs_for_backend</code>. The user picks one." },
{ "step": "2", "detail": "The job's <code>.env</code> is written with <code>PVE_PARENT_JOB</code>, <code>PVE_STORAGE</code> and the inherited <code>KEEP_*</code> values; no systemd timer is created." },
{ "step": "3", "detail": "<code>hb_install_vzdump_hook</code> registers a script-hook in <code>/etc/vzdump.conf</code>. When PVE runs any vzdump task, the hook script fires; if the <code>$STOREID</code> passed to it matches an attached job's <code>PVE_STORAGE</code>, the runner is invoked for that job." },
{ "step": "4", "detail": "The archive lands in the same storage the vzdump dumps just wrote to: <code>path/dump/</code> for Local, or the PBS repository configured on the PVE storage entry." }
{
"step": "1",
"detail": "During job creation, ProxMenux lists compatible parent PVE tasks via <code>hb_pve_list_vzdump_jobs_for_backend</code>. The user picks one."
},
{
"step": "2",
"detail": "The job's <code>.env</code> is written with <code>PVE_PARENT_JOB</code>, <code>PVE_STORAGE</code> and the inherited <code>KEEP_*</code> values; no systemd timer is created."
},
{
"step": "3",
"detail": "<code>hb_install_vzdump_hook</code> registers a script-hook in <code>/etc/vzdump.conf</code>. In the job-end phase of any vzdump task, it compares the supplied <code>$STOREID</code> with each enabled attached job’s <code>PVE_STORAGE</code> and invokes the runner for matching jobs. The match is by storage, not by the selected parent job ID."
},
{
"step": "4",
"detail": "The archive lands in the same storage the vzdump dumps just wrote to: <code>path/dump/</code> for Local, or the PBS repository configured on the PVE storage entry."
}
],
"outroBody": "Attach mode has one operational implication: disabling or deleting the parent PVE task also disables the host backup — there is no timer of its own to fall back on. The job entry stays on disk so it can be re-attached later."
"outroBody": "Attach mode has no timer of its own. Disabling or deleting the selected parent PVE task stops that task’s trigger, but another vzdump task using the same storage can still trigger the attached host backup. Disable or delete the ProxMenux job itself to prevent that. The job entry remains on disk when disabled and can be re-enabled or re-attached later."
},
"storageLayout": {
"heading": "Files that make up a job",
"intro": "Every scheduled job is fully described by three or four files on disk. Reading them gives the full configuration of the job without relying on the interface.",
"rows": [
{ "path": "/var/lib/proxmenux/backup-jobs/JOB_ID.env", "content": "Backend, backup ID or destination, schedule (New mode) or PVE parent (Attach mode), enabled flag, retention <code>KEEP_*</code> values, credentials pointers." },
{ "path": "/var/lib/proxmenux/backup-jobs/JOB_ID.paths", "content": "One absolute path per line — the frozen selection resolved from the profile at job-creation time. Editing the file re-runs the job with the new selection on the next timer fire." },
{ "path": "/etc/systemd/system/proxmenux-backup-JOB_ID.timer + .service", "content": "New mode only. The service invokes <code>run_scheduled_backup.sh JOB_ID</code>. The timer schedules it with <code>Persistent=true</code> (missed fires run at next boot) and a small <code>RandomizedDelaySec=120</code> to spread load when multiple jobs share the same OnCalendar expression." },
{ "path": "/etc/vzdump.conf script-hook", "content": "Attach mode only. Installed once by <code>hb_install_vzdump_hook</code>; matches every attached job's <code>PVE_STORAGE</code> against the <code>$STOREID</code> PVE passes to the hook script." }
{
"path": "/var/lib/proxmenux/backup-jobs/JOB_ID.env",
"content": "Backend, backup ID or destination, schedule (New mode) or PVE parent (Attach mode), enabled flag, retention <code>KEEP_*</code> values, credentials pointers."
},
{
"path": "/var/lib/proxmenux/backup-jobs/JOB_ID.paths",
"content": "One absolute path per line — the frozen selection resolved from the profile at job-creation time. Editing the file re-runs the job with the new selection on the next timer fire."
},
{
"path": "/etc/systemd/system/proxmenux-backup-JOB_ID.timer + .service",
"content": "New mode only. The service invokes <code>run_scheduled_backup.sh JOB_ID</code>. The timer schedules it with <code>Persistent=true</code> (missed fires run at next boot) and a small <code>RandomizedDelaySec=120</code> to spread load when multiple jobs share the same OnCalendar expression."
},
{
"path": "/etc/vzdump.conf script-hook",
"content": "Attach mode only. Installed once by <code>hb_install_vzdump_hook</code>; matches every attached job's <code>PVE_STORAGE</code> against the <code>$STOREID</code> PVE passes to the hook script."
}
]
},
"runner": {
@@ -69,11 +103,26 @@
"heading": "Managing jobs",
"intro": "The scheduler menu (Scripts TUI) and the Monitor Backups tab expose the same actions on any job.",
"rows": [
{ "action": "Run now", "detail": "Invokes the runner immediately, bypassing the timer / vzdump hook. Useful to verify a job configuration without waiting for the next scheduled fire." },
{ "action": "Enable / Disable", "detail": "New mode: <code>systemctl enable/disable --now</code> the timer. Attach mode: flips the <code>ENABLED</code> flag in the job env — the hook script honours it on the next PVE fire." },
{ "action": "Edit", "detail": "Reopens the destination, schedule, profile and retention prompts and rewrites the job files. Preserves the job ID." },
{ "action": "Delete", "detail": "Removes the job env, paths file, systemd timer + service (New mode), and disables the hook binding (Attach mode). Does not touch archives already on the destination." },
{ "action": "View log", "detail": "Streams <code>/var/log/proxmenux/backup-jobs/JOB_ID-YYYYMMDD_HHMMSS.log</code> — one log file per run. The runner also emits a compact status line to journald under the systemd service." }
{
"action": "Run now",
"detail": "Invokes the runner immediately, bypassing the timer / vzdump hook. Useful to verify a job configuration without waiting for the next scheduled fire."
},
{
"action": "Enable / Disable",
"detail": "New mode: <code>systemctl enable/disable --now</code> the timer. Attach mode: flips the <code>ENABLED</code> flag in the job env — the hook script honours it on the next PVE fire."
},
{
"action": "Edit",
"detail": "Reopens the destination, schedule, profile and retention prompts and rewrites the job files. Preserves the job ID."
},
{
"action": "Delete",
"detail": "Removes the job env, paths file, systemd timer + service (New mode), and disables the hook binding (Attach mode). Does not touch archives already on the destination."
},
{
"action": "View log",
"detail": "Streams <code>/var/log/proxmenux/backup-jobs/JOB_ID-YYYYMMDD_HHMMSS.log</code> — one log file per run. The runner also emits a compact status line to journald under the systemd service."
}
]
},
"notifications": {
@@ -83,9 +132,21 @@
"whereNext": {
"heading": "Where to go next",
"items": [
{ "label": "Creating backups", "href": "/docs/backup-restore/creating-backups", "tail": " — the interactive flow that shares the backend with the scheduler." },
{ "label": "Destinations", "href": "/docs/backup-restore/destinations", "tail": " — per-backend configuration used by both the interactive flow and the scheduler." },
{ "label": "Restoring", "href": "/docs/backup-restore/restoring", "tail": " — the flow that consumes what jobs produce." }
{
"label": "Creating backups",
"href": "/docs/backup-restore/creating-backups",
"tail": " — the interactive flow that shares the backend with the scheduler."
},
{
"label": "Destinations",
"href": "/docs/backup-restore/destinations",
"tail": " — per-backend configuration used by both the interactive flow and the scheduler."
},
{
"label": "Restoring",
"href": "/docs/backup-restore/restoring",
"tail": " — the flow that consumes what jobs produce."
}
]
}
}
@@ -20,7 +20,7 @@
},
"iommu": {
"heading": "IOMMU groups — why the whole card leaves at once",
"body": "PCI passthrough is done per <strong>IOMMU group</strong>, which is the smallest unit the CPU's IOMMU can isolate. Everything inside a group moves together. If your SATA controller shares its IOMMU group with the USB controller, passing the SATA card to a VM also takes the USB ports with it — often undesirable.",
"body": "The isolation boundary for PCI passthrough is the <strong>IOMMU group</strong>, the smallest unit the CPU’s IOMMU can isolate. Devices in the same group cannot be isolated from each other. If a SATA controller shares its group with a USB controller, passing through SATA may also require releasing USB from the host, or may prevent passthrough. It does not automatically assign every device in the group to the VM.",
"outro": "ProxMenux shows the IOMMU group next to each device and warns you when a group carries more than just the controller you want. You can still proceed if the extra devices in the group are acceptable (all disk controllers, for example), or back out if they are not."
},
"prereqs": {
@@ -97,7 +97,7 @@
"busyBody": "Another process still holds the PCI device. Most common causes: a previous VM that crashed without releasing it, or the host kernel reclaimed it on the last boot. Run <code>lspci -nnk -s &lt;addr&gt;</code> to see which driver is bound; it should be <code>vfio-pci</code>. A full host reboot usually clears this.",
"noDisksTitle": "Guest sees the controller but not the disks",
"noDisksBody": "The controller model lacks an in-guest driver. Linux almost never hits this; Windows does — install the vendor driver (e.g. LSI / Broadcom for older SAS HBAs) while the card is detached, then reboot with the card attached.",
"sharedTitle": "Shared IOMMU group also exports an unrelated device",
"sharedTitle": "The IOMMU group includes an unrelated device",
"sharedBody": "Options: (a) move the card to a different PCIe slot to change its group, (b) enable <em>ACS override</em> in the kernel to split groups (security trade-off — research before applying), or (c) pick a different card in a cleaner group."
},
"related": {
@@ -46,19 +46,19 @@
"mode": "1. Wipe all",
"part": "Destroyed",
"data": "Signatures wiped",
"useCase": "Starting fresh: everything on the disk goes, ready for a new layout."
"useCase": "Removes the partition table and the filesystem and RAID metadata, zeroes the first and last 16 MiB and, on drives that support discard, trims the whole disk. The rest of the data is not overwritten and may still be recoverable on HDDs; this is not a secure erase."
},
{
"mode": "2. Remove FS labels",
"part": "Preserved",
"data": "Preserved",
"useCase": "Clears stale ZFS / LVM / RAID signatures so the disk stops being auto-claimed, without touching the data."
"useCase": "Removes stale ZFS / LVM / RAID signatures so the disk is no longer auto-claimed. Bulk user data is not overwritten, but removing signatures can make it inaccessible."
},
{
"mode": "3. Zero all data",
"part": "Preserved",
"data": "Destroyed (zeroed)",
"useCase": "Sanitise before handing the disk over, keeping the existing partition layout."
"useCase": "Overwrite the existing partitions with zeros while keeping the partition table. Unallocated space and SSD spare areas are not covered; this is not a complete device sanitization guarantee."
},
{
"mode": "4. Full format",
@@ -52,7 +52,7 @@
"intro": "For every selected image ProxMenux asks:",
"items": [
"<strong>Interface</strong> — <code>scsi</code> (default), <code>virtio</code>, <code>sata</code> or <code>ide</code>. Matches how the guest will see the disk.",
"<strong>SSD emulation</strong> — only offered for non-VirtIO interfaces. Adds <code>ssd=1</code> so the guest advertises the disk as solid-state (useful for OS-level TRIM / alignment).",
"<strong>SSD emulation</strong> — only offered for non-VirtIO interfaces. Adds <code>ssd=1</code> so the guest recognizes a solid-state disk. Discard/TRIM support must be configured separately.",
"<strong>Bootable</strong> — if yes, ProxMenux adds the disk to the boot order so it becomes the primary boot device of the VM."
]
},
@@ -73,7 +73,7 @@
"noImagesTitle": "\"No compatible disk images found\"",
"noImagesBody": "The script scanned the directory but found no <code>.img</code> / <code>.qcow2</code> / <code>.vmdk</code> / <code>.raw</code> file. Check the extension (case matters), that the file sits directly in the selected directory (no sub-directories are searched) and that permissions allow root to read it.",
"slowTitle": "Import is slow",
"slowBody": "VMDK → QCOW2 → raw conversions are CPU- and I/O-bound. Import speed depends on the source read speed, the target storage write speed and CPU for decompression. Prefer raw or qcow2 sources when possible.",
"slowBody": "Conversions between VMDK, QCOW2 and raw can be limited by CPU and I/O. Import speed depends on source reads, target writes and any decompression work. Prefer raw or qcow2 sources when possible.",
"uefiTitle": "Imported disk boots into UEFI shell",
"uefiBody": "The image was built for legacy (SeaBIOS) but the VM uses OVMF (or vice versa). Either switch the VM's BIOS in Proxmox to match the image, or rebuild the image in the correct mode before importing."
},
@@ -27,7 +27,7 @@
"At least one physical disk not in use by the host or any other guest."
],
"warnTitle": "Privileged container required",
"warnBody": "Direct device passthrough to an LXC container only works if the container is <strong>privileged</strong>. If the script detects <code>unprivileged: 1</code> in the config it offers to convert it in place (edits <code>/etc/pve/lxc/&lt;CTID&gt;.conf</code>). Cancel if you cannot accept the security trade-off of a privileged container."
"warnBody": "This script requires a <strong>privileged</strong> container for its direct disk attachment. If it detects <code>unprivileged: 1</code>, it offers to edit <code>/etc/pve/lxc/&lt;CTID&gt;.conf</code> in place. This changes the security model and does not remap existing file ownership. Back up the container and verify ownership before accepting, or rebuild it as privileged."
},
"steps": {
"heading": "Step-by-step",
@@ -83,9 +83,9 @@
"troubleshoot": {
"heading": "Troubleshooting",
"unprivTitle": "\"Cannot continue with an unprivileged container\"",
"unprivBody": "You declined the conversion offer. Either re-run and accept the conversion, or convert the container manually by editing <code>/etc/pve/lxc/&lt;CTID&gt;.conf</code> and replacing <code>unprivileged: 1</code> with <code>unprivileged: 0</code>. You can also rebuild the container as privileged if you prefer not to convert in place.",
"unprivBody": "You declined the conversion, so the script stopped without changing anything. To continue, run it again and accept: the script stops the container, backs up its configuration to <code>/etc/pve/lxc/&lt;CTID&gt;.conf.bak</code> and replaces <code>unprivileged: 1</code> with <code>unprivileged: 0</code>. That flag change alone does not convert the UID/GID ownership of existing files, so back up the container and fix the ownership first; alternatively, restore or rebuild the container as privileged.",
"permsTitle": "Permissions inside the container are wrong",
"permsBody": "Mounts from the host into a privileged container land as <code>root:root</code>. If the service inside the CT runs as a different user (e.g. <code>www-data</code> for Nextcloud), <code>chown</code>/<code>chmod</code> inside the container to match its UID/GID after the first mount."
"permsBody": "Existing file ownership is retained when a partition is mounted into a privileged container; a newly formatted filesystem is often owned by <code>root:root</code>. If the service runs as another user (e.g. <code>www-data</code> for Nextcloud), inspect its UID/GID and adjust the required files with <code>chown</code>/<code>chmod</code>. Changes also affect the underlying disk."
},
"related": {
"heading": "Related",
@@ -12,7 +12,7 @@
},
"intro": {
"title": "What this does",
"body": "This is a raw-disk passthrough: the guest OS sees the drive as a real block device (with its SMART data and native geometry). The disk is attached via its <code>/dev/disk/by-id/…</code> path — not <code>/dev/sdX</code> — so the assignment does not break if disk controllers enumerate devices in a different order on the next boot."
"body": "This attaches a raw physical disk as the backing device for a virtual disk interface. The guest does not automatically receive native SMART data or the physical controller’s features; use controller PCI passthrough when those are required. The disk is attached through its <code>/dev/disk/by-id/…</code> path when one exists, so a changed device enumeration order at boot does not break the assignment; otherwise <code>/dev/sdX</code> is used."
},
"howRuns": {
"heading": "How the script runs",
@@ -49,7 +49,7 @@
"items": [
"<strong>SCSI</strong> — modern default for Linux and Windows with VirtIO-SCSI drivers installed.",
"<strong>SATA</strong> — broad compatibility, works out of the box on virtually every guest.",
"<strong>VirtIO</strong> — paravirtualised block device, fastest but requires guest drivers.",
"<strong>VirtIO</strong> — efficient paravirtualised block device; requires guest drivers.",
"<strong>IDE</strong> — for legacy guests that lack any of the above drivers."
]
},
@@ -63,7 +63,7 @@
"heading": "Manual equivalent",
"body": "The script is a wrapper around <code>qm set</code>. The exact command for a single disk looks like this:",
"migrationTitle": "Live migration is not possible",
"migrationBody": "A VM with a passthrough disk is <strong>tied to the host that exposes that disk</strong>. Live migration to another Proxmox node will fail because the target node does not see the same <code>/dev/disk/by-id/…</code> path. Use replication or backup/restore instead, or pick virtual disks on shared storage if you need mobility.",
"migrationBody": "A VM with a physical passthrough disk is <strong>tied to the host that exposes that disk</strong>. Another node normally lacks the same <code>/dev/disk/by-id/…</code> path and disk data. Back up and transfer that data separately, or use managed virtual disks on shared or supported replicated storage for mobility; do not assume Proxmox storage replication covers this device.",
"shareTitle": "Do not share a physical disk between VMs",
"shareBody": "Attaching the same physical disk to two VMs at the same time will corrupt data on next write. ProxMenux detects this situation and blocks it, but if you bypass the script (direct <code>qm set</code>), make sure only one guest ever mounts the disk."
},
+6 -6
View File
@@ -1,7 +1,7 @@
{
"meta": {
"title": "Proxmox Disk Manager — Attach Disks to VMs / LXC, SMART Tests, Wipe | ProxMenux",
"description": "Manage physical disks on a Proxmox VE host with ProxMenux: pass disks through to VMs or LXC containers (qm set scsi/sata/virtio, pct mp), import qcow2 / vmdk / vdi disk images, pass through whole HBAs or NVMe controllers, safely wipe disks and run SMART self-tests.",
"description": "Manage physical disks on a Proxmox VE host with ProxMenux: attach disks to VMs or LXC containers (qm set scsi/sata/virtio, pct mp), import img / qcow2 / vmdk / raw disk images, pass through whole HBAs or NVMe controllers, wipe disks with safety checks and run SMART self-tests.",
"ogTitle": "Proxmox Disk Manager — Attach Disks to VMs / LXC, SMART Tests, Wipe",
"ogDescription": "Attach disks to VMs / LXC, import disk images, PCI passthrough for controllers, safe wipe and SMART tests on Proxmox VE.",
"twitterTitle": "Proxmox Disk Manager | ProxMenux",
@@ -14,7 +14,7 @@
},
"intro": {
"title": "What this menu is for",
"body": "Every option here operates on <strong>physical disks already visible to the Proxmox host</strong>. The menu is split in three groups — <strong>VM</strong> (attach disks or import images into a virtual machine), <strong>LXC</strong> (attach disks to a container), and <strong>Utilities</strong> (safe format, SMART health). Safety checks apply everywhere: system disks and disks already in use are hidden or blocked."
"body": "These tools work with <strong>physical disks visible to the Proxmox host and image files stored on it</strong>. The menu has three groups: <strong>VM</strong> (attach disks or import images), <strong>LXC</strong> (attach disks to a container), and <strong>Utilities</strong> (formatting and SMART health). Attachment and wipe operations hide or block system disks and disks already in use; SMART checks can inspect those disks without wiping them."
},
"opening": {
"heading": "Opening the menu",
@@ -32,7 +32,7 @@
"Add Controller or NVMe to VM"
],
"lxcTitle": "LXC",
"lxcBody": "Tools that operate on existing containers. Direct device passthrough needs a privileged container; the script offers to convert it if necessary.",
"lxcBody": "Tools for existing LXC containers. This script requires a privileged container and offers to change the flag when needed. Read the conversion and file-ownership cautions on the tool page first.",
"lxcItems": [
"Import Disk to LXC"
],
@@ -69,7 +69,7 @@
},
"lxc": {
"heading": "LXC",
"intro": "Tools that operate on existing LXC containers. Direct device passthrough requires a privileged container; the script offers to convert the container if needed.",
"intro": "Tools for existing LXC containers. This script requires a privileged container and offers to change the flag when needed. Read the conversion and file-ownership cautions on the tool page first.",
"options": [
{
"icon": "Boxes",
@@ -98,8 +98,8 @@
]
},
"safety": {
"title": "Safety rules shared by every option",
"intro": "ProxMenux filters out anything that could harm the host:",
"title": "Safety rules for disk attachment and wiping",
"intro": "Attachment and wipe operations filter out devices that could harm the host:",
"items": [
"The <strong>system disk</strong> (root pool, swap, active ZFS / LVM / RAID members) is always hidden.",
"Disks <strong>mounted</strong> anywhere on the host are hidden or blocked at execution.",
@@ -55,11 +55,11 @@
}
],
"tipTitle": "Long tests survive terminal close",
"tipBody": "A long test is queued <em>inside the drive</em> — it does not depend on the ProxMenux process staying alive. You can close the terminal, reboot the Proxmox host (the test picks up where it left off on some drives) or just come back later and use <strong>Check test progress</strong> to see the result."
"tipBody": "A long test runs <em>inside the drive</em> and does not depend on the ProxMenux process staying alive. You can close the terminal and return later to <strong>Check test progress</strong>. A host reboot, controller reset or power loss may interrupt the test and its result collector; do not assume it will resume automatically."
},
"json": {
"heading": "JSON export for the Monitor",
"intro": "Every run writes a timestamped JSON file to <code>/usr/local/share/proxmenux/smart/&lt;disk&gt;/</code>. The structure is:",
"intro": "Status, short-test and progress actions write a timestamped JSON file to <code>/usr/local/share/proxmenux/smart/&lt;disk&gt;/</code>; long-test results are collected in the background. The full-report action only displays its report. The structure is:",
"outro": "The ProxMenux Monitor picks these up to render health trends per disk; old files are rotated out automatically once the retention limit is reached."
},
"steps": {
@@ -89,7 +89,7 @@
},
{
"title": "Review results",
"bodyRich": "Status and report are printed to the terminal and written to JSON. For long tests, return later and run <strong>Check test progress</strong> on the same disk to see the outcome.",
"bodyRich": "Status, test and progress output is shown in the terminal; <strong>Full report</strong> opens in a scrollable dialog. JSON is exported for the actions described above, not for Full report. For long tests, return later and run <strong>Check test progress</strong> on the same disk to see the outcome.",
"img": "/disk/smart/quick-status.png",
"alt": "Quick health status output",
"caption": "Quick health status output (SATA — smartctl -H + -A)"
@@ -106,7 +106,7 @@
"noSmartTitle": "\"Could not read SMART data from /dev/sdX\"",
"noSmartBody": "The disk is probably behind a RAID / SAS controller that does not pass SMART through. With megaraid-based cards, try <code>smartctl -d megaraid,N /dev/sdX</code>. For HBAs in IT mode the direct invocation works.",
"longTitle": "Long test never completes",
"longBody": "The test is queued on the drive firmware and pauses if the disk is under heavy load. Running it overnight on an idle system usually works. You can also check <code>smartctl -c</code> to see percentage remaining; if it is stuck at a fixed LBA, the disk is failing at that sector."
"longBody": "The drive runs the test in firmware, and heavy I/O can delay completion. Try an idle period such as overnight. For SATA, <code>smartctl -c</code> shows the remaining percentage, and for SAS, <code>smartctl -l selftest</code>; inspect the self-test log for completion status and any failing LBA. A reported read failure needs investigation; slow progress alone does not prove a bad sector."
},
"related": {
"heading": "Related",
@@ -124,7 +124,7 @@
{
"href": "/docs/help-info/zfs-commands",
"label": "ZFS Management commands",
"tail": " — zpool scrub for the storage-layer equivalent of SMART tests."
"tail": " — zpool scrub verifies ZFS data checksums and complements hardware SMART tests."
},
{
"href": "/docs/monitor/dashboard/storage",
+1 -1
View File
@@ -7,7 +7,7 @@
},
"header": {
"title": "Live monitoring tools",
"description": "Three interactive launchers for real-time network observation and bandwidth measurement: iftop, iptraf-ng and iperf3. Each tool is auto-installed from apt on first use, runs in the foreground, and is exited with the documented keystroke. Read-only against the host configuration — they only observe traffic.",
"description": "Three interactive launchers for real-time network observation and bandwidth measurement: iftop, iptraf-ng and iperf3. Each tool is auto-installed from apt on first use, runs in the foreground, and is exited with the documented keystroke. They do not change the host network configuration; iperf3 generates test traffic.",
"section": "Network"
},
"intro": {
@@ -192,7 +192,7 @@
"list": {
"items": [
"A Dockerfile without a published image: the image has to be built and published in an OCI registry first.",
"A Compose file with several services: it is blocked because it describes more than one image. This option installs a single container.",
"A Compose file with several services is blocked: this option installs one service in one container at a time, even when several services use the same image.",
"<code>configs</code>, external secrets or device formats that cannot be translated unambiguously.",
"Shell substitutions such as <code>$(command)</code>: the resulting value has to be written instead.",
"<code>docker run</code> options that are not recognised, and <code>--env-file</code>: an explicit Compose file is read instead."
@@ -215,7 +215,7 @@
{
"id": "restore",
"title": "Backup, restore and another host",
"intro": "A backup made with vzdump or Proxmox Backup Server includes the container, its disks and the copy of its contract. What the installation keeps on the host is not part of it. <strong>Manage installed OCI applications</strong> detects the containers restored on a host that has no contract for them, a new Proxmox installation or another host, and offers to register them again.",
"intro": "A backup made with vzdump or Proxmox Backup Server includes the container, the container disks selected for backup and the copy of its contract. What the installation keeps on the host is not part of it. <strong>Manage installed OCI applications</strong> detects the containers restored on a host that has no contract for them, a new Proxmox installation or another host, and offers to register them again.",
"blocks": [
{
"steps": {
@@ -462,7 +462,7 @@
],
[
"Container disks",
"Proxmox moves them with the container. HA needs them on shared storage."
"Proxmox moves them with the container. HA needs their data available on the target node, through shared storage or a supported replication setup."
],
[
"Host directories and devices",
@@ -179,7 +179,7 @@
["OCI rootfs", "Yes", "The CT backup; it can also be rebuilt from the image and the contract"],
["Container disk with <code>backup=1</code>", "Yes", "The CT backup"],
["Host directory", "No", "The backup of the host storage"],
["Instance contract", "No", "<code>/usr/local/share/proxmenux/oci/instances/VMID/</code> on the host"],
["Instance contract", "Yes, the copy carried inside the CT", "<code>/.proxmenux/oci-record.json</code> inside the CT; additional host and cluster copies are kept separately"],
["Multi-container application", "Each member in its own backup", "The backups of every member, plus the stack contract and its bridge on the host"]
]
}
@@ -1,13 +1,13 @@
{
"meta": {
"title": "Automated Post-Install Script | ProxMenux Documentation",
"description": "The ProxMenux Automated post-install script applies a curated set of 14 safe, hardware-aware optimizations to a fresh Proxmox VE host with zero prompts. Reversible configuration changes are registered for later restoration.",
"description": "The ProxMenux Automated post-install script runs a fixed set of 14 hardware-aware optimizations on a fresh Proxmox VE host. It has no feature-selection checklist, but the system update, banner removal, some Log2RAM installations and rebooting can ask for confirmation. Reversible configuration changes are registered for later restoration.",
"ogTitle": "Automated Post-Install Script | ProxMenux Documentation",
"ogDescription": "14 curated optimizations applied to a fresh Proxmox VE host with zero prompts. Hardware-aware, with reversible configuration changes tracked."
"ogDescription": "14 fixed, hardware-aware optimizations for a fresh Proxmox VE host, with confirmations for some actions and reversible configuration changes tracked."
},
"header": {
"title": "Automated Post-Install Script",
"description": "One click, zero prompts — ProxMenux applies a curated set of 14 safe optimizations that almost every Proxmox host benefits from. Reversible configuration changes are registered for Uninstall Optimizations; package upgrades are not described as reversible.",
"description": "ProxMenux runs a fixed set of 14 baseline optimizations without a feature-selection checklist. Some actions still ask for confirmation. Reversible configuration changes are registered for Uninstall Optimizations; package upgrades are not described as reversible.",
"section": "Post-Install · Automated"
},
"intro": {
@@ -43,7 +43,7 @@
},
{
"tool": "Skip language packages",
"what": "Writes /etc/apt/apt.conf.d/99-disable-translations to stop downloading locale-specific packages. Faster, lighter apt updates.",
"what": "Writes /etc/apt/apt.conf.d/99-disable-translations to stop downloading translated package-description indexes, reducing the data fetched by apt updates.",
"category": "Basic Settings",
"categorySlug": "basic-settings"
},
@@ -97,7 +97,7 @@
},
{
"tool": "Logrotate tuning",
"what": "Daily rotation with size cap 10M, 7 rotations, compression and copytruncate — Log2RAM-friendly.",
"what": "Sets daily and then size 10M globally, keeps 7 rotations, enables compression and copytruncate. The later size criterion takes precedence; per-log rules may override it. Suitable for Log2RAM, but rotation frequency depends on the rules and timer.",
"category": "System",
"categorySlug": "system"
},
@@ -31,7 +31,7 @@
],
"officialTitle": "The official Proxmox recommendation",
"officialBody": "Proxmox's own upgrade guidance is to run, in this exact order:",
"officialOutro": "That single line is all you need on any current Proxmox release. The challenge isn't the command itself — it's making sure the repositories on the host are sane <em>before</em> running it.",
"officialOutro": "For routine updates within the installed Proxmox VE major version, this is the usual command. Check the host’s repositories and review the proposed package changes <em>before</em> proceeding. Upgrading to another major version requires the official migration procedure.",
"doesTitle": "What the ProxMenux option does — verified against the script",
"doesIntro": "The ProxMenux update wraps the exact apt commands above and adds a number of pre/post steps you would otherwise have to remember by hand. Every item below is what the dispatcher script actually does (see <link>Proxmox System Update</link> for the full breakdown):",
"doesItems": [
@@ -59,7 +59,7 @@
},
"languages": {
"heading": "Skip downloading additional languages",
"intro": "On every <code>apt update</code>, Debian by default pulls translation files for every locale it ships — a lot of traffic and disk space for something you'll rarely see. This option tells APT to only fetch the main metadata. Before disabling, ProxMenux <strong>makes sure the host's own locale is actually generated</strong> (reads <code>/etc/default/locale</code> or <code>/etc/environment</code>, appends to <code>/etc/locale.gen</code> if missing, and runs <code>locale-gen</code>). So you don't lose your shell locale as a side effect.",
"intro": "During <code>apt update</code>, APT can download translated package descriptions as well as the main metadata. This option disables those translation downloads to save bandwidth and disk space. Before disabling them, ProxMenux <strong>makes sure the host’s own locale is actually generated</strong> (reads <code>/etc/default/locale</code> or <code>/etc/environment</code>, appends to <code>/etc/locale.gen</code> if missing, and runs <code>locale-gen</code>). So you do not lose your shell locale as a side effect.",
"writtenTitle": "What gets written",
"revertTitle": "Reversible from the Uninstall menu",
"revertBody": "Deleting <code>/etc/apt/apt.conf.d/99-disable-translations</code> (either manually or from the Uninstall menu) restores the default behaviour and APT downloads language files again."
@@ -7,12 +7,12 @@
},
"header": {
"title": "Customizable Post-Install Script",
"description": "Cherry-pick exactly which optimizations to apply to a Proxmox VE host. ProxMenux groups ~35 individual tools into 10 categories, each with its own checklist dialog. Same engine as Automated, but with full control over what gets applied.",
"description": "Cherry-pick exactly which optimizations to apply to a Proxmox VE host. ProxMenux groups ~35 individual tools into 10 categories in one checklist dialog. Same engine as Automated, but with full control over what gets applied.",
"section": "Post-Install · Customizable"
},
"intro": {
"title": "When to pick Customizable",
"body": "Choose this path when you already know which tweaks you want on the host — or which you definitely do not want. The script presents a checklist per category so you can pre-select, deselect or mix-and-match optimizations. Items can be applied again later, and reversible configuration changes are tracked for <link>Uninstall Optimizations</link>. Package upgrades are not presented as reversible."
"body": "Choose this path when you already know which tweaks you want on the host — or which you definitely do not want. The script presents a checklist grouped by category so you can pre-select, deselect or mix-and-match optimizations. Items can be applied again later, and reversible configuration changes are tracked for <link>Uninstall Optimizations</link>. Package upgrades are not presented as reversible."
},
"compare": {
"heading": "How it compares to Automated",
@@ -20,7 +20,7 @@
},
"categoriesSection": {
"heading": "The 10 categories",
"body": "The Customizable script groups optimizations into 10 categories. Each category has its own checklist dialog and its own documentation page — open one of the cards below for the per-option rationale, defaults and verification steps."
"body": "The Customizable script groups optimizations into 10 categories in one checklist dialog. Each category has its own documentation page — open one of the cards below for the per-option rationale, defaults and verification steps."
},
"categories": [
{
@@ -33,7 +33,7 @@
},
{
"name": "Virtualization",
"description": "Prepare the host for advanced virtualization. Auto-installs the guest agent on templates and enables IOMMU/VFIO for PCI passthrough of GPUs, controllers and TPUs."
"description": "Prepare the host for advanced virtualization. Installs a matching guest agent when Proxmox itself runs inside a supported VM, and enables IOMMU/VFIO for PCI passthrough of GPUs, controllers and TPUs."
},
{
"name": "Network",
@@ -53,7 +53,7 @@
},
{
"name": "Monitoring",
"description": "Integrate the host with OVH Real-Time Monitoring. Only appears when ProxMenux detects an OVH server."
"description": "Integrate the host with OVH Real-Time Monitoring. Installs RTM only when ProxMenux detects an OVH server; on other hosts the option makes no changes."
},
{
"name": "Performance",
@@ -38,7 +38,7 @@
"breakTitle": "Can break after Proxmox updates",
"breakBody": "Proxmox regularly ships UI updates that overwrite <code>proxmoxlib.js</code>. When that happens, the banner comes back. You can re-apply this option after every Proxmox update to re-patch the file, or just accept the nag until you reapply. ProxMenux also installs an APT hook that auto-re-patches on package upgrades — see <link>Uninstall Optimizations</link> for how that hook is reversed.",
"legalTitle": "Legal note (always worth repeating)",
"legalBody": "This option only hides the UI nag. It does <strong>not</strong> grant you enterprise support, access to the enterprise repository, or any rights to ship Proxmox commercially without a subscription. If you run Proxmox in a revenue-generating environment, buy a subscription — it supports the project and gets you the stable enterprise channel."
"legalBody": "This option only hides the UI notice. It does <strong>not</strong> grant enterprise support or access to the enterprise repository, and it does not change the software license. Proxmox VE is released under the GNU AGPLv3. A subscription supports the project and provides access to the enterprise update channel; choose the appropriate support level for your environment."
},
"verify": {
"heading": "Verification",
@@ -78,6 +78,17 @@
"heading": "Community scripts",
"body": "The menu also exposes two third-party scripts from the <a>community-scripts</a> project: <em>Proxmox VE Post Install</em> and <em>Proxmox VE Microcode</em>. These are wrappers that <code>wget | bash</code> the original authors' scripts. They are not maintained by ProxMenux. See <link>External Repositories</link> for details and trust considerations before running them."
},
"pbs": {
"heading": "On Proxmox Backup Server",
"body": "On a Proxmox Backup Server host, the same menu opens from <strong>Settings post-install Proxmox</strong> in the PBS menu. Automated, Customizable, Apply Available Updates and Uninstall Optimizations work as on Proxmox VE, with these differences:",
"items": [
"<strong>Repositories and upgrade:</strong> when the server has no subscription and only the Enterprise PBS repository is active, ProxMenux asks before switching to the PBS no-subscription repository. The switch goes through the server's own repository API, the same one behind <em>Administration > Repositories</em>. With an active subscription, or with a no-subscription or test repository already configured, the sources stay as they are.",
"<strong>Customizable list:</strong> the options that only apply to Proxmox VE are not listed: guest agent, VFIO IOMMU, Open vSwitch, vzdump backup speed, pigz, Appliance Manager, Ceph, High Availability and OVH Real Time Monitoring.",
"<strong>Subscription banner:</strong> the patch applies to the Proxmox Backup Server web interface, and Uninstall Optimizations restores it.",
"<strong>Figurine:</strong> installs without the Proxmox VE aliases.",
"<strong>Community scripts:</strong> the section offers <em>Proxmox Backup Server Post Install</em> and <em>Proxmox Backup Server Microcode</em> in place of the Proxmox VE scripts."
]
},
"related": {
"heading": "Related",
"items": [
@@ -30,8 +30,8 @@
"verifyTitle": "Verification",
"verifyBody": "On a real OVH host, after a reboot you should see the <a>RTM dashboard</a> in your OVH Manager populated with live data for the host. On the Proxmox side, the RTM collector is a systemd service — check it directly:",
"troubleTitle": "Troubleshooting",
"revertTitle": "Not reversible from the Uninstall menu",
"revertBody": "There is no dedicated uninstall entry for RTM. On a real OVH host, remove the packages manually with <code>apt purge ovh-*</code> and delete any puppet manifests under <code>/etc/puppet/</code> that RTM installed. On a non-OVH host, nothing was ever installed, so there's nothing to revert."
"revertTitle": "Remove RTM from the Uninstall menu",
"revertBody": "When RTM is registered as installed, Uninstall Optimizations offers an OVH Real-Time Monitoring entry. It runs <code>apt-get purge -y ''ovh-rtm*'' ''rtm'' ''rtm-*''</code>, removes /etc/rtm, and marks RTM as uninstalled. Puppet artefacts may still need manual cleanup; review any RTM-specific manifests under <code>/etc/puppet/</code>. On a non-OVH host, no RTM packages were installed, so there is nothing to remove."
},
"related": {
"heading": "Related",
@@ -74,7 +74,7 @@
"impactTitle": "Impact is workload-dependent",
"impactBody": "BBR shines on high-latency or lossy links (cross-continent replication, VPN tunnels, mobile clients). On a LAN between two machines on the same switch, the difference is often within noise. TFO helps short, repeated HTTP connections the most.",
"revertTitle": "Reversible from the Uninstall menu",
"revertBody": "BBR/TFO are tracked. <link>Uninstall Optimizations</link> removes the two sysctl files (<code>/etc/sysctl.d/99-tcp-bbr.conf</code> and <code>99-tcp-fastopen.conf</code>) and reloads sysctl so the kernel returns to <code>cubic</code> and <code>tcp_fastopen=1</code>. Manual equivalent:"
"revertBody": "BBR/TFO are tracked. <link>Uninstall Optimizations</link> removes the two sysctl files (<code>/etc/sysctl.d/99-kernel-bbr.conf</code> and <code>99-tcp-fastopen.conf</code>) and reloads the remaining sysctl configuration. Removing a file does not itself reset its live kernel values. Defaults such as <code>cubic</code> and <code>tcp_fastopen=1</code> return after a reboot unless another configuration overrides them. Manual equivalent:"
},
"names": {
"heading": "Interface Names (persistent)",
@@ -83,13 +83,13 @@
"whyItems": [
"Adding or removing PCIe devices can shift the bus numbering, turning <code>enp3s0</code> into <code>enp4s0</code>. If your <code>/etc/network/interfaces</code> references the old name, the bridge vanishes on reboot.",
"BIOS / firmware updates sometimes change how devices enumerate, with the same effect.",
"LXC containers with <code>hotplug</code> NICs and bonded links can race on boot and end up named inconsistently. Pinning fixes that."
"The helper writes <code>.link</code> files for physical NICs, including bond member ports, using the MAC currently reported in sysfs; for bond members this may be the bond’s shared MAC, so check their names after a reboot. It skips virtual interfaces such as LXC veth devices and the bond devices themselves; it does not fix naming races for virtual <code>hotplug</code> NICs."
],
"writtenTitle": "What gets written",
"writtenIntro": "One file per physical NIC, at <code>/etc/systemd/network/10-proxmenux-&lt;iface&gt;.link</code>. Each file starts with the header <code># Managed by ProxMenux — do not edit</code> so ProxMenux can distinguish its own files from user- or package-provided <code>.link</code> files.",
"writtenOutro": "<code>.link</code> files from other packages or hand-written by the user are left in place — only files carrying the ProxMenux header are managed. On re-run, the function reconciles its own set: it re-reads <code>MACAddress=</code> from every <code>10-proxmenux-*.link</code> and drops any whose MAC no longer appears under <code>/sys/class/net/</code>. Files written by an earlier ProxMenux release in the old <code>10-&lt;iface&gt;.link</code> format are migrated automatically the first time.",
"pveTitle": "PVE 9 vs PVE 8",
"pveBody": "On Proxmox VE 9 (<code>systemd-networkd</code> native), the script reloads udev rules after writing the <code>.link</code> files so new hotplug NICs pick up the correct name without a reboot. On PVE 8 (<code>ifupdown2</code>), interface naming is resolved at boot anyway — a reboot is required for the changes to take effect. The script sets the reboot flag either way so Customizable prompts you.",
"pveBody": "The script uses systemd <code>.link</code> files for persistent names. On Proxmox VE 9 it also reloads udev rules after writing them, so a pinned NIC that is re-added or re-probed can pick up its rule without rebooting. Existing interfaces should be checked after a reboot on both PVE 8 and PVE 9. This does not switch the host away from <code>ifupdown2</code>. The script sets the reboot flag in either case so Customizable prompts you.",
"reviewTitle": "Review existing /etc/network/interfaces first",
"reviewBody": "If your host has legacy configuration in <code>/etc/network/interfaces</code> that references NIC names generated by the kernel's default scheme, pinning <em>today's</em> names is exactly what you want. But if you've already manually customised the config around specific names, double-check the pinning matches what the interfaces file expects before rebooting.",
"revertTitle": "Reversible from the Uninstall menu",
@@ -133,7 +133,7 @@
},
"log2ram": {
"title": "Install Log2RAM (SSD/NVMe wear reduction)",
"intro": "Log2RAM mounts <code>/var/log</code> on a tmpfs ramdisk and periodically syncs the contents back to the underlying disk. On a hypervisor journald churn hits the root SSD every few seconds; moving the writes to RAM reduces wear and disk I/O without losing logs — the periodic sync flushes them to disk and a clean shutdown flushes them too.",
"intro": "Log2RAM mounts <code>/var/log</code> on a tmpfs ramdisk and periodically syncs it to the underlying disk. Buffering frequent journald writes in RAM can reduce root SSD wear and disk I/O. Periodic sync and a clean shutdown save logs to disk, but an abrupt power loss can lose entries written since the last sync.",
"upstreamLabel": "Upstream project:",
"upstreamUrl": "https://github.com/azlux/log2ram",
"upstreamLinkLabel": "azlux/log2ram on GitHub",
@@ -149,7 +149,7 @@
"Registers itself in <code>installed_tools.json</code> so it can be reverted from Uninstall Optimizations."
],
"howUseLabel": "How to use:",
"howUseBody": "On the Automated flow Log2RAM applies unattended when an SSD/NVMe root is detected. On the Customizable flow the script prompts for size, sync interval and whether to enable the 90% auto-sync guard.",
"howUseBody": "On the Automated flow Log2RAM installs unattended when an SSD/NVMe root is detected. The Customizable flow asks for size, sync interval and whether to enable the auto-sync guard. Its prompt currently says 90%, but the generated guard uses the 80% cleanup and 92% aggressive thresholds described above.",
"verifyLabel": "Verify and manage from the shell:",
"verifyCode": "# Service state and configured timers\nsystemctl status log2ram\nsystemctl list-timers log2ram*\n\n# Current ramdisk usage — /var/log IS the tmpfs itself\ndf -h /var/log\n\n# Force a sync now (tmpfs → disk); does NOT shrink the tmpfs\nlog2ram write\n\n# Trigger the auto-sync guard on demand (vacuum + sync when usage is high)\n/usr/local/bin/log2ram-check.sh\n\n# Current config: SIZE, mail settings, path\ncat /etc/log2ram.conf\n\n# ProxMenux-specific cron jobs\ncat /etc/cron.d/log2ram /etc/cron.d/log2ram-auto-sync\n\n# Recent Log2RAM activity\njournalctl -u log2ram -n 50 --no-pager"
},
@@ -172,6 +172,6 @@
},
"autoApplication": {
"title": "Automatic Application",
"body": "These optional features are applied only when specifically selected during the post-install process. Each feature can be individually chosen based on your specific needs and preferences."
"body": "In the Customizable flow, select each optional feature according to your needs. Log2RAM is also part of the Automated flow with hardware detection; ZRAM remains Customizable-only. Review the individual sections for prompts and prerequisites."
}
}
@@ -16,7 +16,7 @@
"heading": "Use pigz for faster gzip compression",
"intro": "Standard <code>gzip</code> compresses data using a <strong>single CPU core</strong>. On modern Proxmox hosts with 8, 16 or 32 cores, that is a huge bottleneck during <code>vzdump</code> VM/CT backups, log rotation, and anything else that pipes through <code>gzip</code>. <a>pigz</a> is a drop-in parallel replacement: same gzip-compatible output, but it spreads the work across every core.",
"doesTitle": "What ProxMenux does",
"doesIntro": "Four steps, all idempotent:",
"doesIntro": "Five steps, all idempotent:",
"doesItems": [
"Sets <code>pigz: 1</code> in <code>/etc/vzdump.conf</code> so Proxmox's backup tool uses pigz natively.",
"Installs the <code>pigz</code> apt package if not already present.",
+10 -10
View File
@@ -15,7 +15,7 @@
"trackedBody": "Each of these options registers a tool in <code>installed_tools.json</code>, so they appear in <link>Uninstall Optimizations</link>. Reverting <code>zfs_arc</code> removes <code>/etc/modprobe.d/99-zfsarc.conf</code> and rebuilds initramfs; <code>zfs_auto_snapshot</code> reverses the cron schedule and offers to purge the package; <code>zfs_autotrim</code> sets <code>autotrim=off</code> on the pools it enabled; <code>vzdump_speed</code> restores <code>/etc/vzdump.conf</code> from the <code>.bak</code> the install created.",
"arc": {
"heading": "Optimize ZFS ARC size",
"intro": "The <strong>Adaptive Replacement Cache (ARC)</strong> is ZFS's in-memory read cache. Without explicit tuning, ZFS happily grabs up to half the host RAM for itself, which is excessive on a Proxmox host that also needs memory for VMs and LXCs. This option caps ARC to a sane fraction of total RAM based on the size of the machine.",
"intro": "The <strong>Adaptive Replacement Cache (ARC)</strong> is ZFS’s in-memory read cache. Proxmox VE installations since 8.1 set a limit of 10% of RAM, capped at 16 GiB; older or upgraded hosts may differ. This option writes that same policy explicitly (RAM/10, capped at 16 GiB, with a 64 MiB floor) to its own file, which also covers older or upgraded hosts. Check existing settings and workload needs before changing it; a smaller cache is not always faster.",
"sizingTitle": "Sizing rules",
"headerRam": "Host RAM",
"headerMax": "ARC cap",
@@ -41,7 +41,7 @@
"max": "16 GiB (cap)"
}
],
"after": "The file <code>/etc/modprobe.d/99-zfsarc.conf</code> contains a single directive — <code>options zfs zfs_arc_max=…</code>. Every other ZFS module parameter (<code>zfs_arc_min</code>, L2ARC prefetch/write throttle, TXG timeout) is left at its OpenZFS default. Before writing, a reconcile step scans any other <code>*.conf</code> in <code>/etc/modprobe.d/</code> that sets <code>zfs_arc_min</code> / <code>zfs_arc_max</code>, backs them up to <code>/usr/local/share/proxmenux/backups/zfs_arc/</code> with a manifest, and strips the conflicting lines so only ProxMenux's file is active. After writing the file, ProxMenux runs <code>update-initramfs -u -k all</code> and, when applicable, <code>proxmox-boot-tool refresh</code>, so the cap also lands in the initramfs used by ZFS-on-root setups.",
"after": "The file <code>/etc/modprobe.d/99-zfsarc.conf</code> contains a single directive — <code>options zfs zfs_arc_max=…</code>. This option does not tune other ZFS module parameters (<code>zfs_arc_min</code>, L2ARC prefetch/write throttle, TXG timeout). Before writing, a reconcile step scans other <code>*.conf</code> in <code>/etc/modprobe.d/</code> that set <code>zfs_arc_min</code> / <code>zfs_arc_max</code>, backs them up to <code>/usr/local/share/proxmenux/backups/zfs_arc/</code> with a manifest, and removes the conflicting <code>zfs_arc_min</code> / <code>zfs_arc_max</code> values (other options on those lines are kept), so only ProxMenux’s ARC file sets the limit. After writing, it runs <code>update-initramfs -u -k all</code> and, when applicable, <code>proxmox-boot-tool refresh</code>, so the limit also reaches the initramfs used by ZFS-on-root setups.",
"rebootTitle": "Requires a reboot to take effect",
"rebootBody": "ARC settings are read when the <code>zfs</code> kernel module loads. To make the cap take effect on ZFS-on-root hosts, ProxMenux regenerates the initramfs with <code>update-initramfs -u -k all</code> and, when applicable, refreshes the boot loader with <code>proxmox-boot-tool refresh</code>. A reboot is still required to pick up the new module parameter; the \"reboot required\" flag is set automatically.",
"safeTitle": "Safe on non-ZFS hosts",
@@ -90,22 +90,22 @@
},
"autotrim": {
"heading": "Enable ZFS autotrim (SSD/NVMe pools)",
"intro": "Enables the <code>autotrim</code> property on every ZFS pool that is backed exclusively by SSDs or NVMe drives with TRIM support. Pools that include a single HDD vdev are skipped automatically — TRIM on rotational drives is meaningless and ZFS will refuse it. Pools that were already set to <code>autotrim=on</code> are left as they are.",
"intro": "Enables the <code>autotrim</code> property on ZFS pools backed exclusively by SSDs or NVMe drives with TRIM support. The script conservatively skips pools containing a rotational device. Pools already set to <code>autotrim=on</code> are left unchanged.",
"trimTitle": "What TRIM does, and why it matters on ZFS",
"trimBody1": "SSDs / NVMe drives manage internal storage in fixed-size erase blocks that are much larger than the logical sectors the filesystem talks to. When a filesystem deletes a file, the drive doesn't know — it still thinks those sectors are in use, so its internal garbage collector keeps shuffling stale data around to free new erase blocks. <strong>TRIM</strong> is the standard command the OS uses to tell the drive \"these sectors are now free, you can erase them ahead of time\".",
"trimBody2": "Without TRIM, SSD performance degrades over time as the drive runs out of pre-erased blocks and has to do erase-on-write, and write amplification increases — both shortening the drive's useful life. With TRIM enabled, the drive can keep a healthy pool of empty blocks ready to write into.",
"trimBody3": "On ZFS, <code>autotrim=on</code> is the modern equivalent of the periodic <code>zpool trim &lt;pool&gt;</code> command — instead of having to remember to run a trim manually (or schedule one), the pool issues TRIM commands automatically and continuously as blocks become free. It's low-overhead and the recommended setting for SSD-backed pools.",
"trimBody3": "With <code>autotrim=on</code>, ZFS trims recently freed space in the background, grouping ranges before issuing commands. A periodic <code>zpool trim &lt;pool&gt;</code> is another option. Autotrim avoids a separate schedule, but its performance impact depends on the devices; periodic manual trim may suit some lower-end drives better.",
"practicalTitle": "Why is this practical to enable?",
"practicalItems": [
"<strong>Sustained write performance.</strong> SSDs that never see TRIM commands slow down measurably over months. Autotrim keeps the controller's job easier.",
"<strong>Drive longevity.</strong> Lower write amplification means fewer total NAND writes for the same amount of data — measurable in years of useful life for the disk.",
"<strong>Sustained write performance.</strong> TRIM can help a drive reclaim unused blocks efficiently. The benefit and any latency impact depend on the device and workload.",
"<strong>Drive longevity.</strong> Avoiding unnecessary internal copying can reduce write amplification and NAND wear. It does not guarantee a specific increase in drive lifespan.",
"<strong>No scheduled cron job to remember.</strong> Unlike a periodic <code>zpool trim</code>, autotrim is fire-and-forget — the pool handles it on its own."
],
"whenTitle": "When is this necessary?",
"whenIntro1": "<strong>You should enable it</strong> on any ZFS pool whose vdevs are all SSD or NVMe with TRIM support — the typical Proxmox install on consumer or enterprise SSDs.",
"whenIntro1": "<strong>Consider enabling it</strong> on ZFS pools made entirely of TRIM-capable SSD or NVMe devices. Check device behaviour and workload latency; a scheduled manual trim can be preferable on some hardware.",
"whenIntro2": "<strong>The script skips it automatically</strong> on:",
"whenSkipItems": [
"Pools containing any HDD (rotational) vdev — TRIM is meaningless there.",
"Pools containing any HDD (rotational) vdev — a conservative hardware filter used by this script.",
"Drives without <code>discard_granularity</code> exposed in sysfs — usually old SSDs without TRIM, or pass-through paths where the OS can't see the underlying device.",
"Pools where ZFS itself reports the property unsupported (rare; very old pools).",
"Pools already set to <code>autotrim=on</code> (no-op)."
@@ -121,7 +121,7 @@
},
"vzdump": {
"heading": "Increase vzdump backup speed",
"intro": "By default, Proxmox vzdump throttles backups to protect running VMs/CTs from IO starvation. On many setups that throttle is more conservative than needed. This option removes the bandwidth cap and lowers the I/O priority so vzdump can saturate the storage path during backup windows.",
"intro": "Writes bwlimit: 0 and ionice: 5 in the global vzdump configuration. Proxmox already defaults to no bandwidth limit, so this removes a cap only if one was configured. Compared with the default ionice: 7, the value 5 gives a higher best-effort I/O priority where the scheduler honours it. This can affect running guests and does not guarantee faster backups.",
"changedTitle": "What gets changed in /etc/vzdump.conf",
"backupTitle": "First run creates a .bak of vzdump.conf",
"backupBody": "The first time this option runs, ProxMenux copies <code>/etc/vzdump.conf</code> to <code>/etc/vzdump.conf.bak</code> before touching it. Subsequent runs re-use that backup and won't overwrite it, so a hand-edited config from before the first apply stays recoverable. The changes to <code>bwlimit</code> and <code>ionice</code> are then made with <code>sed</code>, and any other options in the file (compress, pigz, tmpdir, exclude-path, etc.) are preserved.",
@@ -145,7 +145,7 @@
{
"label": "Backup and Restore commands",
"href": "/docs/help-info/backup-commands",
"tail": " — vzdump CLI reference (now without the legacy bwlimit / ionice caps)."
"tail": " — vzdump CLI reference for bandwidth and I/O-priority settings."
},
{
"label": "Uninstall Optimizations",
@@ -28,9 +28,9 @@
},
"logrotate": {
"heading": "Optimize logrotate",
"intro": "Rewrites <code>/etc/logrotate.conf</code> with a tighter policy suitable for a host that's also part of an SSD-protecting Log2RAM setup: daily rotation, 7-day retention, 10 MB size trigger, compression, and <code>copytruncate</code> so active services keep writing without reopening their log files. Original <code>logrotate.conf</code> is backed up to <code>.bak</code> on first apply.",
"intro": "Rewrites <code>/etc/logrotate.conf</code> with global daily and size settings, 7 retained rotations, compression and <code>copytruncate</code>, so active services can keep writing without reopening their log files. The size setting follows daily and takes precedence unless a per-log rule overrides it. Original <code>logrotate.conf</code> is backed up to <code>.bak</code> on first apply.",
"tipTitle": "Log2RAM-friendly",
"tipBody": "The <code>size 10M</code> trigger means logs rotate on size <em>or</em> daily, whichever comes first. Combined with Log2RAM's RAM-backed <code>/var/log</code>, this keeps the working set small so flushes to disk stay cheap."
"tipBody": "The <code>size 10M</code> directive takes precedence over the earlier daily setting; it does <em>not</em> mean whichever comes first. Logrotate checks the threshold when it runs, and per-log rules may override the global settings. With Log2RAM’s RAM-backed <code>/var/log</code>, review both the rules and the timer frequency to control disk usage."
},
"limits": {
"heading": "Increase various system limits",
@@ -72,7 +72,7 @@
}
],
"tipTitle": "Why inotify matters",
"tipBody": "Applications like Docker, Syncthing, Node.js watchers, Plex's library scanner and many more hit <code>max_user_watches</code> quickly. Default on Debian is 8192 — a single running Plex can exhaust it. 1M is generous and costs ~1 KB of RAM per watch, which is negligible."
"tipBody": "Docker, Syncthing, Node.js file watchers and Plex library scans can exhaust <code>max_user_watches</code>. The default depends on the kernel and host memory; it is not always 8192. Raising the limit to 1M allows more watches, but does not preallocate them. Active watches consume kernel memory, so monitor actual usage on memory-constrained hosts."
},
"memory": {
"heading": "Optimize memory settings",
@@ -102,8 +102,8 @@
"verify": {
"heading": "Verification",
"intro": "After applying the System optimizations:",
"tipTitle": "Fully reversible",
"tipBody": "All six options are tracked in <code>installed_tools.json</code>, so they appear in <link>Uninstall Optimizations</link> if you want to back any of them out. Reverts restore the sysctl files' defaults, drop the systemd unit and alias for kexec, and reset journald/logrotate to stock Debian configs."
"tipTitle": "Restoration and limitations",
"tipBody": "All six options are tracked in <code>installed_tools.json</code> and offered in <link>Uninstall Optimizations</link>. Restoration removes the configuration files and the kexec unit and alias, rewrites journald.conf with the stock systemd defaults and restores logrotate.conf from its backup. Some live limits need a new session or reboot."
},
"related": {
"heading": "Related",
@@ -58,7 +58,7 @@
},
{
"tool": "APT Language Skip",
"restores": "Removes /etc/apt/apt.conf.d/99-disable-translations. APT will download language packages again."
"restores": "Removes /etc/apt/apt.conf.d/99-disable-translations, allowing APT to download translated package-description indexes according to its remaining language settings."
},
{
"tool": "APT IPv4 Force",
@@ -83,7 +83,7 @@
},
{
"tool": "System Limits Increase",
"restores": "Removes /etc/sysctl.d/99-maxwatches.conf, 99-maxkeys.conf, 99-swap.conf, 99-fs.conf and /etc/security/limits.d/99-limits.conf. Reverts PAM limits and systemd DefaultLimitNOFILE, and strips the ulimit -n 256000 line from /root/.profile."
"restores": "Removes /etc/sysctl.d/99-maxwatches.conf, 99-maxkeys.conf, 99-swap.conf, 99-fs.conf and /etc/security/limits.d/99-limits.conf. Reverts PAM limits and systemd DefaultLimitNOFILE. Its /root/.profile cleanup targets the legacy ulimit -n 256000 line; a ulimit -n 1048576 line from the current installer can remain and may need manual removal."
},
{
"tool": "ZRAM swap profile",
@@ -104,7 +104,7 @@
},
{
"tool": "TCP BBR + TCP Fast Open",
"restores": "Removes /etc/sysctl.d/99-tcp-bbr.conf and 99-tcp-fastopen.conf and reloads sysctl so the kernel returns to the cubic congestion control and to tcp_fastopen=1."
"restores": "Removes /etc/sysctl.d/99-kernel-bbr.conf and 99-tcp-fastopen.conf and reloads remaining sysctl configuration. File removal does not itself reset live values; defaults such as cubic and tcp_fastopen=1 return after reboot unless another configuration overrides them."
},
{
"tool": "Persistent Interface Names",
@@ -166,8 +166,8 @@
"restores": "Removes idle=nomwait from kernel cmdline (ZFS) or GRUB, and the ignore_msrs / report_ignored_msrs options from /etc/modprobe.d/kvm.conf."
},
{
"tool": "QEMU Guest Agent (templates)",
"restores": "Reads /usr/local/share/proxmenux/guest_agent.pkg (recorded at install time) and apt purges whichever package was installed (qemu-guest-agent for standard hosts, spice-vdagent when Spice mode is used)."
"tool": "Guest agent for a virtualized Proxmox host",
"restores": "Reads /usr/local/share/proxmenux/guest_agent.pkg and purges the recorded host package. The installer selects qemu-guest-agent, open-vm-tools or virtualbox-guest-utils according to the detected hypervisor. If the record is missing, the uninstaller tries those three packages. This acts on the nested Proxmox host, not guest templates."
}
]
},
@@ -221,7 +221,7 @@
},
{
"tool": "OVH RTM (monitoring)",
"restores": "apt purges any ovh-* packages the RTM installer added and deletes the puppet manifests it dropped under /etc/puppet/. On non-OVH hosts nothing was ever installed, so nothing is removed."
"restores": "Purges packages matching ovh-rtm*, rtm and rtm-*, removes /etc/rtm and clears the RTM registry entry. Puppet artefacts under /etc/puppet/ may need manual inspection and cleanup; the uninstaller does not delete that directory. On non-OVH hosts the installer normally makes no changes."
}
]
}
@@ -230,7 +230,7 @@
"edge": {
"heading": "Edge cases and caveats",
"packageTitle": "Package reinstall touches live Proxmox packages",
"packageBody": "Reverting <strong>Subscription Banner Removal</strong> reinstalls <code>pve-manager</code>, <code>proxmox-widget-toolkit</code>, <code>libjs-extjs</code> and <code>libpve-http-server-perl</code> with <code>--force-confnew</code>. This is generally safe but does touch the running web UI — refresh your browser afterwards, and expect a few seconds of reconnection. Don't run this in the middle of a migration or clone operation.",
"packageBody": "If ProxMenux’s own backups cannot restore <strong>Subscription Banner Removal</strong>, the fallback reinstalls <code>pve-manager</code>, <code>proxmox-widget-toolkit</code>, <code>libjs-extjs</code> and <code>libpve-http-server-perl</code> with <code>--force-confnew</code>. This touches the running web UI: refresh the browser afterwards and allow a few seconds to reconnect. Avoid doing it during a migration or clone operation.",
"rebootTitle": "Persistent names and VFIO need a reboot",
"rebootBody": "Removing ProxMenux's <code>10-proxmenux-*.link</code> files (<em>Persistent Interface Names</em>) and reverting <em>IOMMU/VFIO</em> do not affect the running system — they only matter after a reboot. ProxMenux sets the reboot flag automatically for these.",
"perItemTitle": "You can revert one thing and keep the rest",
@@ -48,9 +48,9 @@
},
"pathB": {
"heading": "Path B — From the Monitor dashboard",
"intro": "The same updates appear in the Monitor under <link>Settings → ProxMenux Optimizations</link>. When pending updates are detected the card shows an \"Updates available\" banner with the count and an <strong>Apply</strong> action that opens the same per-optimization picker.",
"imageAlt": "ProxMenux Optimizations card on the Monitor dashboard with an Updates available banner at the top showing the count of pending updates and an Apply button that opens the per-optimization picker",
"imageCaption": "The Optimizations card in the Monitor — when at least one optimization has a newer version on disk, the banner surfaces it without requiring shell access."
"intro": "The same updates appear in the Monitor under <link>Settings → ProxMenux Optimizations</link>. When pending updates are detected, the card shows an <strong>updates count badge</strong> that opens the per-optimization picker. Individual tools also provide their own update action.",
"imageAlt": "ProxMenux Optimizations card in Monitor settings, with a pending-updates count badge that opens the per-optimization picker and update actions on individual tools",
"imageCaption": "The Optimizations card in the Monitor — when at least one optimization has a newer version on disk, its count badge makes the updates accessible without shell access."
},
"applying": {
"heading": "What happens when you apply",
@@ -69,7 +69,7 @@
},
{
"title": "Registry refresh sent to the Monitor",
"body": "Once the batch finishes, the menu POSTs to <code>http://127.0.0.1:8008/api/updates/post-install/scan</code> to rebuild <code>/usr/local/share/proxmenux/updates_available.json</code>. That is what makes the Monitor's Optimizations card and the shell menu entry disappear immediately after the update, without waiting for the next scheduled scan."
"body": "Once the batch finishes, the menu POSTs to <code>http://127.0.0.1:8008/api/updates/post-install/scan</code> to rebuild <code>/usr/local/share/proxmenux/updates_available.json</code>. After all pending updates are applied, the update badge in the Monitor’s Optimizations card and the conditional shell menu entry disappear without waiting for the next scheduled scan. The Optimizations card itself remains available."
}
],
"jqTitle": "jq is required",
+2 -2
View File
@@ -91,7 +91,7 @@
},
"hardening": {
"heading": "SSH hardening: MaxAuthTries",
"intro": "Lynis control <strong>SSH-7408</strong> recommends <code>MaxAuthTries 3</code> in <code>sshd_config</code>. With Fail2Ban's <code>maxretry = 2</code> on the SSH jail, a malicious client never reaches three attempts anyway — but the explicit setting satisfies the audit and adds defence in depth (e.g. if Fail2Ban is stopped for maintenance).",
"intro": "Lynis control <strong>SSH-7408</strong> recommends <code>MaxAuthTries 3</code> in <code>sshd_config</code>. The SSH jail uses <code>maxretry = 2</code> to trigger a ban after repeated failures, while MaxAuthTries limits attempts within a connection. The explicit SSH setting adds defence in depth, including when Fail2Ban is stopped for maintenance.",
"installerIntro": "The installer:",
"items": [
"Reads the current <code>MaxAuthTries</code> value (or defaults to 6 if absent).",
@@ -125,7 +125,7 @@
"troubleshoot": {
"heading": "Troubleshooting",
"neverBansTitle": "Fail2Ban runs but never bans anything",
"neverBansBody": "Check the auth log files actually receive entries: <code>tail -f /var/log/ssh-auth.log</code> and try a wrong-password SSH attempt from a different machine. If the log stays empty, the logger service is not running: <code>systemctl status ssh-auth-logger.service</code>. If it's active but the log is empty, check that the journald drop-in took effect: <code>journalctl --dump-catalog | head</code> — events at <em>info</em> level should be visible.",
"neverBansBody": "Check that <code>tail -f /var/log/ssh-auth.log</code> receives an entry after a wrong-password SSH attempt from another machine. If it stays empty, inspect <code>systemctl status ssh-auth-logger.service</code>. If the logger is active, inspect recent SSH events with <code>journalctl -u ssh.service -n 50 --no-pager -p info</code> and check whether the journald drop-in permits the <em>info</em> level.",
"monitorEmptyTitle": "ProxMenux Monitor jail has no entries even after failed logins",
"monitorEmptyBody": "The <code>[proxmenux]</code> jail reads <code>/var/log/proxmenux-auth.log</code>, which is written by the ProxMenux Monitor Flask app — not by a journald logger. If you don't run the Monitor, the file stays empty and the jail never fires. That's expected; the jail config is harmless. If you do run the Monitor and the log is empty, check the Flask logging config.",
"selfBanTitle": "I banned myself",
@@ -28,7 +28,7 @@
"items": [
"Auto-detected — no path to type",
"Picks the ACME-uploaded cert if present, falls back to the self-signed default",
"Renewal happens through Proxmox; the Monitor follows automatically on next start"
"Proxmox handles renewal; the Monitor validates and loads it on the next new TLS connection"
]
},
"custom": {
@@ -143,7 +143,7 @@
},
"trustCa": {
"heading": "Trust the Proxmox self-signed CA",
"intro1Rich": "If you prefer to keep the default Proxmox-generated certificate (no ACME), you can stop the browser warning by importing the cluster's root CA into the OS or browser trust store. The Monitor will keep auto-detecting the same <code>/etc/pve/local/pveproxy-ssl.pem</code>; the only thing that changes is that the device viewing the dashboard now recognises the issuer.",
"intro1Rich": "To keep the default Proxmox-generated certificate without ACME, import the cluster root CA into the OS or browser trust store. The Monitor detects <code>/etc/pve/local/pve-ssl.pem</code> as the fallback certificate. This makes the client trust its issuer; the URL must still match a name in the certificate.",
"intro2Rich": "The CA file lives at <code>/etc/pve/pve-root-ca.pem</code> on every node. In a cluster the path is on the shared <code>pmxcfs</code> filesystem so all nodes share the same root, and a single import covers the whole datacenter. Copy it to your machine:",
"code": "scp root@<your-host>:/etc/pve/pve-root-ca.pem ./pve-root-ca.pem",
"thenImport": "Then import it on the client side:",
@@ -98,7 +98,7 @@
"noLunTitle": "LUNs do not appear in /dev/disk/by-path after registering",
"noLunBody": "<code>pvesm add iscsi</code> succeeded but the kernel is not seeing block devices. Check <code>iscsiadm --mode session</code> — if no session is active, start one manually with <code>iscsiadm --mode node --login</code>. If a session exists but no LUNs appear, force a rescan: <code>iscsiadm --mode session --rescan</code>. If still nothing, the target probably has no LUNs assigned to this IQN yet (configure LUN mapping on the target).",
"chapTitle": "CHAP authentication",
"chapBody": "ProxMenux does not expose the CHAP username / password fields in the interactive flow. If your target requires CHAP, register with ProxMenux first (discovery with CHAP may fail, but <code>pvesm add</code> will still write the entry), then add credentials manually: <code>pvesm set &lt;id&gt; --username &lt;chap-user&gt;</code> and edit <code>/etc/iscsi/iscsid.conf</code> with the password, then <code>systemctl restart iscsid</code>. This is a gap in the current script — documented here so you know what to do."
"chapBody": "ProxMenux does not expose CHAP username/password fields in the interactive flow. If discovery fails, the script stops before <code>pvesm add</code>; it does not register the storage anyway. Configure the target’s CHAP requirements separately in <code>open-iscsi</code>, including discovery authentication when required, then retry. Use <code>iscsiadm</code> to check the configuration and verify active sessions with <code>iscsiadm --mode session</code>."
},
"related": {
"heading": "Related",
@@ -16,7 +16,7 @@
},
"destructive": {
"title": "Destructive tool for the 'format' path",
"body": "The <em>Format</em> path <strong>erases every partition and byte on the selected disk</strong>. ProxMenux shows the disk list with a warning banner, filters out disks already in use, and requires an explicit confirmation before any <code>wipefs</code> / <code>sgdisk</code> / <code>mkfs</code> runs. Still — read the device path aloud twice before typing \"yes\"."
"body": "The <em>Format</em> path <strong>destroys the existing partitions and data on the selected disk</strong>. ProxMenux shows the disk list with a warning banner, filters out disks already in use, and requires confirmation before any <code>wipefs</code> / <code>sgdisk</code> / <code>mkfs</code> runs. This is not a secure overwrite of every byte. Double-check the device path before confirming."
},
"compare": {
"heading": "Directory vs ZFS pool — which to pick",
@@ -78,7 +78,7 @@
"Pick the filesystem — see the table above for trade-offs.",
"Storage ID (default <code>disk-&lt;device&gt;</code>), mount path, content-type preset.",
"Confirm at the <strong>FINAL CONFIRMATION — DATA WILL BE ERASED</strong> dialog.",
"ProxMenux wipes the partition table, creates one partition spanning the whole disk, runs <code>mkfs.&lt;fs&gt;</code> (or <code>zpool create</code>), mounts it, adds it to <code>/etc/fstab</code> by UUID with <code>defaults,nofail</code>, and registers it in Proxmox via <code>pvesm add dir</code> or <code>pvesm add zfspool</code>."
"ProxMenux clears the partition table and creates one partition spanning the disk. It runs <code>mkfs.&lt;fs&gt;</code> or <code>zpool create</code> and mounts the result. Regular filesystems get an entry in <code>/etc/fstab</code> by UUID with <code>defaults,nofail</code>; ZFS uses its pool mount configuration instead. If <em>As Proxmox storage</em> is marked (always the case for ZFS), it is then registered with <code>pvesm add dir</code> or <code>pvesm add zfspool</code>; otherwise it stays a host mount only."
],
"tipTitle": "Why UUID + nofail in /etc/fstab",
"tipBody": "The script writes <code>UUID=… /mnt/&lt;id&gt; &lt;fs&gt; defaults,nofail 0 2</code>. UUID survives device reordering across reboots (so <code>/dev/sdc</code> becoming <code>/dev/sdd</code> does not break boot). <code>nofail</code> means that if the disk ever disappears (hardware failure, unplugged), Proxmox still boots — storage will just show as inactive until the disk comes back."
@@ -97,7 +97,7 @@
},
"presets": {
"heading": "Content-type presets",
"intro": "Instead of a long checklist, ProxMenux offers 4 presets plus a custom option. The preset string is passed to <code>pvesm add … --content &lt;csv&gt;</code>:",
"intro": "ProxMenux offers three presets plus a custom option. The selected content string is passed to <code>pvesm add … --content &lt;csv&gt;</code>:",
"headerPreset": "Preset",
"headerContent": "Content string",
"headerUse": "Use case",
@@ -115,7 +115,7 @@
{
"preset": "All types",
"content": "images,backup,iso,vztmpl,snippets",
"use": "\"Let the host decide\" — allows every content type Proxmox supports on this storage."
"use": "The broad preset shown here enables images, backups, ISOs, LXC templates and snippets. Add other supported types, such as rootdir, through Custom if needed."
},
{
"preset": "Custom",
@@ -124,7 +124,7 @@
}
],
"zfsTitle": "ZFS pool content is narrower",
"zfsBody": "If you picked <strong>zfs</strong> as the filesystem, Proxmox only accepts <code>images</code> and <code>rootdir</code> as content types on a <code>zfspool</code> (it is block storage, not a filesystem for ISOs). The presets above will still work but Proxmox silently drops the content types it cannot use — check <code>pvesm status</code> afterwards."
"zfsBody": "If you picked <strong>zfs</strong>, a <code>zfspool</code> supports only <code>images</code> and <code>rootdir</code>, not ISO or backup files. Use Custom to enter a supported content list. The script forwards your selection unchanged; unsupported types may make registration fail. Check <code>pvesm status</code> afterwards."
},
"manual": {
"heading": "Manual equivalent",
@@ -37,8 +37,8 @@
"rows": [
{
"bit": "1 (sticky)",
"effect": "Only the file owner can delete / rename a file.",
"why": "Prevents CT-A from deleting files created by CT-B in the same directory."
"effect": "Deleting or renaming a file is restricted to its owner, the directory owner or a suitably privileged process.",
"why": "Helps prevent users with different mapped UIDs from deleting each other’s files. It does not isolate CTs that share a mapped UID or privileged root access."
},
{
"bit": "777 (rwx-rwx-rwx)",
@@ -48,7 +48,7 @@
{
"bit": "ACLs (default)",
"effect": "Inherited on every newly created file / subdir.",
"why": "The permissions apply forever — new content keeps the open profile without re-running the script."
"why": "New content inherits the default ACLs without re-running the script. Applications can still request restrictive modes or change permissions later."
}
],
"privTitle": "Privileged CTs see the same thing, just without the shift",
@@ -46,13 +46,13 @@
},
{
"method": "<strong>Both</strong>",
"mount": "Both paths above (two independent NFS connections to the server)",
"mount": "Both paths above (two mount points for the same NFS export)",
"ui": "Yes",
"useCase": "You want UI integration AND a stable host-side path with open permissions for LXC bind-mounts. Server sees two connections from the host."
"useCase": "UI integration plus a separate stable host path with open permissions for LXC bind mounts."
}
],
"bothTitle": "\"Both\" creates two independent NFS mounts",
"bothBody": "Marking both options runs each method independently — pvesm mounts at <code>/mnt/pve/&lt;id&gt;</code>, fstab mounts at <code>/mnt/&lt;path&gt;</code>. The server sees two TCP connections from the Proxmox host. This is intentional: it lets you keep the pvesm storage clean (default Proxmox options) while having a separate host-side mount with open permissions for LXC bind-mounts."
"bothBody": "Both methods run separately: pvesm mounts at <code>/mnt/pve/&lt;id&gt;</code> and fstab at <code>/mnt/&lt;path&gt;</code>. This provides a Proxmox-managed mount with default options and a separate host mount for LXC bind mounts. Two mount points do not guarantee two separate TCP connections; the NFS client may reuse transport connections."
},
"pvesmBranch": {
"heading": "Method A — As Proxmox storage (pvesm)",
@@ -94,7 +94,7 @@
}
],
"warnTitle": "images and rootdir on NFS",
"warnBody": "Marking <code>images</code> on an NFS storage lets Proxmox place live VM disks there. It works, but NFS latency + its lock semantics make this noticeably slower than local storage (and a network outage pauses every VM backed by that store). Marking <code>rootdir</code> means you will host LXC root filesystems over NFS, which historically has trouble with unprivileged containers (overlay / chown interactions). Prefer keeping <code>images</code> / <code>rootdir</code> on local or iSCSI storage and using NFS for <code>backup</code> / <code>iso</code> / <code>vztmpl</code>.",
"warnBody": "Selecting <code>images</code> lets Proxmox store VM disks on NFS. Performance depends on the network, server and workload; an outage can block I/O that depends on the mount. Selecting <code>rootdir</code> stores LXC root filesystems on NFS, where unprivileged containers can encounter UID mapping, overlay and ownership limitations. Assess these requirements before placing <code>images</code> / <code>rootdir</code> there; NFS is also useful for <code>backup</code> / <code>iso</code> / <code>vztmpl</code>.",
"result": "The result is <code>pvesm add nfs &lt;id&gt; --server &lt;srv&gt; --export &lt;path&gt; --content &lt;csv&gt;</code> and Proxmox auto-mounts at <code>/mnt/pve/&lt;id&gt;</code>."
},
"fstabBranch": {
@@ -113,7 +113,7 @@
"Attempts <code>chmod 1777</code> + <code>setfacl o::rwx</code> on the mount point (silent best-effort — only succeeds if the NFS server allows it; many NAS exports already serve world-writable paths)."
],
"lxcTitle": "Using the fstab mount from an LXC",
"lxcBody": "The script prints the exact <code>pct set</code> command at the end to bind-mount the path into a container, e.g. <code>pct set &lt;ctid&gt; -mp0 /mnt/&lt;path&gt;,mp=/mnt/&lt;ct-path&gt;,shared=1,backup=0</code>. Or use the <mountLink>LXC Mount Manager</mountLink>, which detects the fstab mount automatically. <strong>No changes are made inside the container</strong> — for unprivileged LXCs to read/write the NFS data, the script relies on server-side world-writable export semantics (typical for Synology, TrueNAS, OMV defaults). If your NFS server squashes root or restricts perms, see Troubleshooting.",
"lxcBody": "The script prints a <code>pct set</code> command for a bind mount, e.g. <code>pct set &lt;ctid&gt; -mp0 /mnt/&lt;path&gt;,mp=/mnt/&lt;ct-path&gt;,shared=1,backup=0</code>. Or use the <mountLink>LXC Mount Manager</mountLink>, which detects fstab mounts. <strong>No changes are made inside the container</strong>. Access from an unprivileged CT depends on server-side permissions for its mapped UIDs/GIDs. The script only attempts host-side permission changes; it cannot guarantee writable exports or manufacturer defaults. See Troubleshooting if access fails.",
"noUiTitle": "The fstab mount is NOT a Proxmox storage",
"noUiBody": "The host fstab mount is invisible to the Proxmox web UI — by design. It does not appear in <em>Datacenter > Storage</em>, you cannot select it in the VM / CT creator, and Proxmox backup jobs cannot target it. It is purely a host filesystem path. If you also want UI integration, mark <em>both</em> methods in the picker."
},
@@ -145,9 +145,9 @@
"showmountTitle": "\"Failed to connect\" from showmount",
"showmountBody": "The server refused the export-list query. Common causes: the server enforces strict client ACLs (your Proxmox IP is not on the allowed list), <code>rpcbind</code> is not running on the server, or the server exposes only NFSv4 with <code>showmount</code> disabled. The export path can still be added manually if you know it.",
"inactiveTitle": "Storage shows as inactive in the Proxmox UI",
"inactiveBody": "Run <em>Test NFS Connectivity</em> first — 99 % of the time this flags the exact hop that breaks (DNS, port, mount permissions). If connectivity is fine but Proxmox still flags inactive, check <code>journalctl -u pvestatd</code> on the Proxmox node; intermittent DNS or slow NFS mounts on boot can leave storages in an inactive state until <code>pvesm set &lt;id&gt; --disable 0</code> is re-applied.",
"inactiveBody": "Run <em>Test NFS Connectivity</em> first: it checks that the server answers ping, that port 2049 is open and that the export list (<code>showmount -e</code>) is available. If connectivity works but storage remains inactive, inspect <code>journalctl -u pvestatd</code> and the server/export configuration. Restore the underlying connectivity or permissions first. <code>pvesm set &lt;id&gt; --disable 0</code> enables a disabled storage; it is not a general repair for NFS failures.",
"lxcNoWriteTitle": "fstab mount works but unprivileged LXC can't write through a bind-mount",
"lxcNoWriteBody": "NFS permissions are enforced server-side, not by the host. The fstab options (<code>uid=</code>, <code>file_mode=</code>) that work for CIFS do <strong>not</strong> apply to NFS. If your unprivileged LXC (root inside CT = UID 100000 on host, mapped to NFS as \"others\") cannot write, the NFS server is either squashing root or has restrictive perms. Fix on the server: change the export to <code>no_root_squash</code> if you trust the host, or set the export world-writable (Synology, TrueNAS, OMV all expose this in the UI). The script tries <code>chmod 1777</code> on the mount but that only succeeds when the server permits it.",
"lxcNoWriteBody": "NFS access depends on server-side ownership, ACLs and squash settings. CIFS-style options such as <code>uid=</code> and <code>file_mode=</code> do <strong>not</strong> apply to NFS. With default LXC mapping, CT root becomes host UID 100000, not remote UID 0. Configure access for the actual mapped UID/GID or the server’s anonymous identity. <code>no_root_squash</code> only affects remote UID 0, so it is not a universal fix for UID 100000. The script attempts <code>chmod 1777</code> but cannot override the server’s permissions.",
"fstabBootTitle": "fstab mount does not come up after reboot",
"fstabBootBody": "The script adds <code>nofail,_netdev</code> so a missing share at boot does not block startup. If the mount never comes up: confirm the network is reachable (<code>ping &lt;server&gt;</code>), check <code>journalctl -u remote-fs.target</code>, and verify the entry is valid with <code>mount -fav | grep nfs</code>. <code>mount -a</code> after boot mounts everything that is currently missing."
},
@@ -12,7 +12,7 @@
},
"intro": {
"title": "What this does",
"body": "ProxMenux offers two mount methods for any Samba share: <strong>(1) Proxmox storage</strong> via <code>pvesm add cifs</code> — visible in Datacenter > Storage, mounted at <code>/mnt/pve/&lt;id&gt;</code>, credentials stored encrypted in <code>/etc/pve/priv/storage/&lt;id&gt;.pw</code>; <strong>(2) Host fstab mount</strong> at a path you pick — persistent via <code>/etc/fstab</code>, mounted with <code>uid=0,gid=0,file_mode=0777,dir_mode=0777</code> so unprivileged LXC bind-mounts can write, credentials stored in a root-only <code>/etc/samba/credentials/</code> file (password never in <code>fstab</code>). You can run one method, the other, or both."
"body": "ProxMenux offers two mount methods for a Samba share: <strong>(1) Proxmox storage</strong> via <code>pvesm add cifs</code>, visible in Datacenter > Storage and mounted at <code>/mnt/pve/&lt;id&gt;</code>. The password is stored as plaintext in <code>/etc/pve/priv/storage/&lt;id&gt;.pw</code>, protected by the root-only private directory; this is access control, not encryption. <strong>(2) Host fstab mount</strong> at a chosen path, persistent through <code>/etc/fstab</code> and mounted with <code>uid=0,gid=0,file_mode=0777,dir_mode=0777</code> so unprivileged LXC bind mounts can write. Its credentials are in a root-only file under <code>/etc/samba/credentials/</code>, never directly in <code>fstab</code>. Either or both methods can be used."
},
"opening": {
"heading": "Opening the tool",
@@ -41,24 +41,24 @@
"method": "<strong>Host fstab only</strong>",
"mountRich": "<code>/mnt/&lt;path&gt;</code> with open uid/gid/file_mode",
"ui": "No",
"useCaseRich": "Bind-mounting the share into one or more LXCs <em>without</em> exposing it as a Proxmox storage. Open perms guarantee unprivileged CTs can write through the bind-mount."
"useCaseRich": "Bind-mount the share into one or more LXCs <em>without</em> registering Proxmox storage. Open local permissions help unprivileged CTs access it, but writing still requires permission from the SMB server for the authenticated user."
},
{
"method": "<strong>Both</strong>",
"mount": "Both paths above (two independent CIFS connections to the server)",
"mount": "Both paths above (two mount points for the same CIFS export)",
"ui": "Yes",
"useCase": "You want UI integration AND a stable host-side path with open permissions for LXC bind-mounts. The pvesm mount uses Proxmox defaults; the fstab mount applies the open uid/gid/file_mode separately."
}
],
"bothTitle": "\"Both\" creates two independent CIFS connections",
"bothBody": "pvesm and fstab mount the same share twice with different options. The pvesm mount at <code>/mnt/pve/&lt;id&gt;</code> uses Proxmox defaults (no open uid/gid) — Proxmox UI is happy. The fstab mount at <code>/mnt/&lt;path&gt;</code> uses <code>uid=0,gid=0,file_mode=0777,dir_mode=0777</code> — unprivileged LXC bind-mounts to it can write. Two TCP connections to the SMB server from the same Proxmox host."
"bothTitle": "“Both” creates two CIFS mount points",
"bothBody": "pvesm mounts at <code>/mnt/pve/&lt;id&gt;</code> with Proxmox defaults. fstab mounts at <code>/mnt/&lt;path&gt;</code> with <code>uid=0,gid=0,file_mode=0777,dir_mode=0777</code> to permit local access through LXC bind mounts. The SMB server still enforces the mounted user’s permissions. These are two mount points; they do not guarantee two separate TCP connections."
},
"pvesmBranch": {
"heading": "Method A — As Proxmox storage (pvesm)",
"intro": "If <em>As Proxmox storage</em> is marked, ProxMenux runs the original pvesm flow:",
"items": [
"<strong>Storage ID</strong> — default <code>cifs-&lt;server-ip-with-dashes&gt;</code>. Only letters, digits, <code>-</code> and <code>_</code>.",
"<strong>Content types</strong> — CIFS exposes <strong>6</strong> options (same as NFS minus <code>rootdir</code>, which Proxmox does not allow on CIFS because the locking semantics cannot hold a live LXC rootfs):"
"<strong>Content types</strong> — this ProxMenux checklist offers <strong>6</strong> choices. It omits <code>rootdir</code> compared with the NFS checklist; this is a script-level choice, not a claim that every Proxmox CIFS backend version forbids LXC root filesystems:"
],
"headerType": "Content type",
"headerAllows": "What it allows",
@@ -89,16 +89,16 @@
}
],
"warnTitle": "Live VM disks on CIFS",
"warnBody": "If you tick <code>images</code>, ProxMenux pops up a warning before continuing. CIFS uses an advisory locking model that interacts badly with KVM disk operations (snapshots, live migration, I/O under contention). A backup or ISO store on CIFS is perfectly fine; a live-disk store is asking for trouble. Use NFS (or local storage) for <code>images</code> when you can.",
"warnBody": "Selecting <code>images</code> triggers a ProxMenux warning that CIFS/SMB can cause locking issues with VM disk operations. The script recommends NFS for VM disk images but lets you continue. Backups and ISOs are common CIFS uses. Assess locking, snapshots, migration and workload requirements before using CIFS for <code>images</code>.",
"credsTitle": "Where pvesm stores credentials",
"credsBody": "When you use User authentication, the password is NOT written in <code>/etc/pve/storage.cfg</code>. Proxmox keeps it in <code>/etc/pve/priv/storage/&lt;storage-id&gt;.pw</code> — mode <code>0600</code>, owner <code>root</code>, member of the cluster sync. If you later change the password on the Samba server, use <code>pvesm set &lt;id&gt; --password &lt;new&gt;</code> or remove and re-add through ProxMenux."
"credsBody": "The password is not written in <code>/etc/pve/storage.cfg</code>. Proxmox writes plaintext credentials in <code>/etc/pve/priv/storage/&lt;storage-id&gt;.pw</code>, inside the private area of the cluster filesystem, readable only by <code>root</code> (mode <code>0600</code>); it is protected by access control, not by encryption. After a server password change, use <code>pvesm set &lt;id&gt; --password &lt;new&gt;</code> or remove and re-add through ProxMenux."
},
"fstabBranch": {
"heading": "Method B — Host fstab mount only",
"intro": "If <em>As host fstab mount only</em> is marked, ProxMenux asks for a host mount path and persistent options, writes a root-only credentials file (User mode), then mounts and writes the entry to <code>/etc/fstab</code>:",
"items": [
"<strong>Host mount path</strong> — default <code>/mnt/&lt;share-name&gt;</code>. Must be an absolute path. If something is already mounted there, or a fstab entry exists, ProxMenux offers to replace it.",
"<strong>Mount options</strong> — pick <em>Read/Write</em> (default with <code>rw,uid=0,gid=0,file_mode=0777,dir_mode=0777,iocharset=utf8,nofail,_netdev</code>), <em>Read-only</em> (read-only variant with <code>file_mode=0555,dir_mode=0555</code>), or <em>Custom</em> (type your own option string). Open uid/gid/file_mode are always recommended for LXC bind-mount writes."
"<strong>Mount options</strong> — choose <em>Read/Write</em> (default: <code>rw,uid=0,gid=0,file_mode=0777,dir_mode=0777,iocharset=utf8,nofail,_netdev</code>), <em>Read-only</em> (<code>file_mode=0555,dir_mode=0555</code>), or <em>Custom</em> (enter your own options). The script recommends open uid/gid/file_mode for writes through LXC bind mounts."
],
"credsTitle": "Credentials file (User mode)",
"credsBody": "If you authenticated with a username + password, ProxMenux writes a root-only credentials file at <code>/etc/samba/credentials/&lt;server&gt;_&lt;share&gt;.cred</code> (mode <code>0600</code>) with <code>username=</code> and <code>password=</code> lines, and references it in the fstab entry via the <code>credentials=</code> mount option. Plain text never lands in <code>/etc/fstab</code>. For Guest mode, the option <code>guest</code> is appended instead and no credentials file is created.",
@@ -110,7 +110,7 @@
"Runs <code>systemctl daemon-reload</code> so systemd picks up the new fstab entry."
],
"lxcTitle": "Using the fstab mount from an unprivileged LXC",
"lxcBody": "Because the CIFS client applies <code>uid=0,gid=0,file_mode=0777,dir_mode=0777</code>, every file in the mount is owned by host UID 0 with mode 0777. An unprivileged LXC bind-mounting the path sees \"others\" perms on every entry (CT root = host UID 100000 = others on the host filesystem), so reads and writes succeed. <strong>No changes are made inside the container</strong> — no <code>chown</code> in the CT, no group setup, nothing modified in <code>/etc/lxc</code> beyond the single <code>mp</code> bind-mount line. The script prints the exact <code>pct set</code> command at the end, or use the <mountLink>LXC Mount Manager</mountLink> to attach the path.",
"lxcBody": "The CIFS options <code>uid=0,gid=0,file_mode=0777,dir_mode=0777</code> open local permission checks for LXC bind mounts. Server ACLs and negotiated Unix/POSIX semantics can still affect access. <strong>No changes are made inside the container</strong>: no CT <code>chown</code>, groups or edits to <code>/etc/lxc</code>. The <code>mp</code> line is managed in Proxmox CT configuration. Use the printed <code>pct set</code> command or the <mountLink>LXC Mount Manager</mountLink>.",
"noUiTitle": "The fstab mount is NOT a Proxmox storage",
"noUiBody": "The host fstab mount is invisible to the Proxmox web UI — by design. It does not appear in <em>Datacenter > Storage</em>, you cannot select it in the VM / CT creator, and Proxmox backup jobs cannot target it. It is purely a host filesystem path. If you also want UI integration, mark <em>both</em> methods in the picker."
},
@@ -121,12 +121,12 @@
"fstabGuestIntro": "Method B (fstab, guest auth) — no credentials file:"
},
"view": {
"heading": "View configured CIFS storages",
"body": "Lists every CIFS entry in Proxmox (<code>pvesm status | awk ''$2 == \"cifs\"''</code>) with server, share, content types, username (or <em>Guest</em>), mount path and live status. Password is <strong>never printed</strong>. <strong>Note:</strong> this view only shows pvesm-registered storages — fstab-only mounts are not listed. Check those with <code>findmnt -t cifs</code> or <code>grep cifs /etc/fstab</code>."
"heading": "View configured CIFS storages and host mounts",
"body": "Shows registered CIFS storages (<code>pvesm status | awk ''$2 == \"cifs\"''</code>) with server, share, content, username or <em>Guest</em>, path and status. Passwords are <strong>never printed</strong>. <strong>Note:</strong> a second section lists host fstab mounts, their options, authentication-file path and active/inactive state. For manual checks use <code>findmnt -t cifs</code> or <code>grep cifs /etc/fstab</code>."
},
"remove": {
"heading": "Remove CIFS storage",
"body": "Runs <code>pvesm remove &lt;storage-id&gt;</code> after a confirmation that shows the server / share / content. Only the Proxmox-side registration is removed — <strong>the remote Samba server is not touched</strong>. Proxmox also deletes the paired credentials file. To remove a fstab-only mount, edit <code>/etc/fstab</code> by hand, run <code>umount &lt;path&gt;</code>, and remove the file from <code>/etc/samba/credentials/</code>.",
"heading": "Remove CIFS storage or a host mount",
"body": "The removal picker offers pvesm storages and host fstab mounts. For pvesm, it runs <code>pvesm remove &lt;storage-id&gt;</code> and Proxmox removes the paired credentials. For fstab, it attempts <code>umount &lt;path&gt;</code>, removes the matching <code>/etc/fstab</code> entry and credentials under <code>/etc/samba/credentials/</code>, and removes an empty mount directory if possible. A failed unmount requires a manual unmount or reboot. <strong>The remote Samba server and its files are not modified</strong>.",
"warnTitle": "Back up dependencies first",
"warnBody": "VMs, CTs or backup jobs that reference this storage by ID will fail after removal. Move / drop those references before removing the storage."
},
@@ -145,7 +145,7 @@
"sleepTitle": "\"host is down\" or storage status goes inactive at night",
"sleepBody": "Some NAS appliances put SMB to sleep aggressively. <code>pvestatd</code> then sees the storage as inactive until the first access wakes the NAS. If this is cosmetic, nothing is broken. If it causes backup failures, disable SMB idle disconnect on the NAS or schedule backups outside the sleep window.",
"lxcNoWriteTitle": "Unprivileged LXC bind-mount can't write to the fstab path",
"lxcNoWriteBody": "Confirm the mount uses the open options: <code>findmnt /mnt/&lt;path&gt;</code> should show <code>uid=0,gid=0,file_mode=0777,dir_mode=0777</code>. If a Custom option string omits those, an unprivileged LXC will get permission denied (CT UIDs map to host \"others\", which without open file_mode have no write bit). Re-mount with the default RW preset or add the four options back to your custom string.",
"lxcNoWriteBody": "Check <code>findmnt /mnt/&lt;path&gt;</code> for the intended options, such as <code>uid=0,gid=0,file_mode=0777,dir_mode=0777</code>. Missing local write permissions can block mapped LXC UIDs. Reapply the default RW preset or adjust Custom options as needed. Also verify the mounted SMB user can write on the server and whether Unix/POSIX extensions supply server modes; open local modes cannot override server ACLs.",
"fstabBootTitle": "fstab mount does not come up after reboot",
"fstabBootBody": "The script adds <code>nofail,_netdev</code> so a missing share at boot does not block startup. If the mount never comes up: confirm the network is reachable (<code>ping &lt;server&gt;</code>), check <code>journalctl -u remote-fs.target</code>, verify the credentials file still exists (<code>ls -l /etc/samba/credentials/</code>) and is readable, and try <code>mount -a</code> manually."
},
@@ -121,7 +121,7 @@
},
"privReq": {
"title": "Privileged container requirement",
"body": "The four <strong>LXC network sharing</strong> flows above require a <strong>privileged</strong> container — the kernel modules (<code>nfs-kernel-server</code>, <code>smbd</code>, <code>mount.nfs</code>, <code>mount.cifs</code>) need capabilities that unprivileged CTs do not expose. If you only need to share files between the host and an unprivileged CT, use the <mountLink>LXC Mount Manager</mountLink> instead — it bind-mounts from the host and adjusts permissions on the host side so the container can read and write without any kernel-module magic."
"body": "The four <strong>LXC network sharing</strong> flows above require a <strong>privileged</strong> container. The scripts enforce this supported mode before configuring the services and mount tools (<code>nfs-kernel-server</code>, <code>smbd</code>, <code>mount.nfs</code>, <code>mount.cifs</code>). If you only need to share files between the host and an unprivileged CT, use the <mountLink>LXC Mount Manager</mountLink> instead — it bind-mounts from the host and adjusts permissions on the host side so the container can read and write without configuring these services or mount tools inside it."
},
"unprivExplain": {
"title": "How ProxMenux handles unprivileged CT permissions",
@@ -24,7 +24,7 @@
"arrowLabel": "bind mount",
"outro": "The host path on the left and the container path on the right can be different — for example, <code>/mnt/nas-films</code> on the host can become <code>/media/films</code> inside the container. The script adds the entry to the container config (<code>/etc/pve/lxc/&lt;ctid&gt;.conf</code>) using the next free <code>mpN</code> index, with two safe-defaults baked in:",
"items": [
"<code>shared=1</code> — tells Proxmox the data is shared storage, so it does not try to migrate the disk when you migrate the container.",
"<code>shared=1</code> — tells Proxmox the mount uses shared data, so it does not migrate the data with the CT. This flag does not replicate data: the same path and data must already be available on the destination node.",
"<code>backup=0</code> — excludes the bind mount from <code>vzdump</code> backups (the data lives on the host, you back it up separately)."
]
},
@@ -64,7 +64,7 @@
"idmapChangesRich": "Custom UID/GID mapping in <code>/etc/pve/lxc/&lt;CTID&gt;.conf</code> plus matching <code>/etc/subuid</code> + <code>/etc/subgid</code> entries on the host. CT must be restarted on every change.",
"idmapWhenRich": "You need a <em>specific</em> UID inside the CT to map to a <em>specific</em> UID on the host (e.g. <code>media:1000</code> in the CT must own files as <code>media:1000</code> on the host so NFS doesn't squash). A more rigid setup, normally only worth it for that exact scenario.",
"idmapTipTitle": "When you actually need lxc.idmap",
"idmapTipBody": "If the LXC Mount Manager doesn't cover your case — typically because you need files written from inside the CT to land on the host as a specific UID/GID (NFS exports with <code>all_squash</code>, Samba shares with strict ownership, sharing a directory with a host user that has the same name as someone inside the CT) — that is the moment to add <code>lxc.idmap</code> entries by hand. The Mount Manager won't fight you: the bind-mount line it wrote is independent of any idmap configuration you add later."
"idmapTipBody": "Use manual ID mapping when files written in the CT must appear on the host under a specific numeric UID/GID, for example to share ownership with a host user. It cannot override NFS <code>all_squash</code>; the server still decides the anonymous identity and permissions. Add <code>lxc.idmap</code> entries by hand if needed. The Mount Manager’s bind-mount line is independent of those entries."
},
"opening": {
"heading": "Opening the tool",
@@ -56,7 +56,7 @@
}
],
"netEffectTitle": "Net effect",
"netEffectBody": "Your container always boots, even if the NFS server is offline. The first time something touches the mount path, systemd quietly mounts it. If the server is still down, that one access fails with <em>resource temporarily unavailable</em> — but nothing else in the CT is affected."
"netEffectBody": "The NFS filesystem is mounted on <em>first access</em> rather than eagerly during boot. If the server is offline, that access may wait or fail, and services that depend on the path can be affected. Automount reduces the boot-time dependency; it does not guarantee that every CT service is unaffected by an unavailable NFS server."
},
"manual": {
"heading": "Manual equivalent",
@@ -27,9 +27,9 @@
"heading": "The \"sharedfiles\" group convention",
"body": "Before exporting the folder, ProxMenux creates a group called <code>sharedfiles</code> with <strong>GID 101000</strong> inside the container, adds every regular user to it, then sets the export directory to <code>root:sharedfiles</code> with mode <code>2775</code>. The <code>2</code> at the front is the SGID bit — every file or folder created inside automatically inherits the <code>sharedfiles</code> group.",
"gidTitle": "Why GID 101000 specifically",
"gidBody": "It maps to host GID <strong>1000</strong> when an unprivileged container reads the same file (LXC default idmap shifts everything by +100000). In the current ProxMenux flow the NFS server itself runs in a <strong>privileged</strong> CT (no shift on its side), but the convention keeps the group ID numerically consistent with unprivileged client CTs that may mount this share later. Two privileged CTs both using <code>sharedfiles</code> at GID 101000 can read / write each other's files cleanly because the GID numbers match end-to-end.",
"remapTitle": "The script also creates 'remap_*' users — they are vestigial here",
"remapBody": "For every regular user in the CT (and for common UIDs like 33 = www-data, 1000, 1001, 1002), the script creates a parallel <code>remap_&lt;uid&gt;</code> user with UID = <code>&lt;uid&gt; + 100000</code>, all members of <code>sharedfiles</code>. The <code>+100000</code> shift mimics the LXC unprivileged idmap, but since this script enforces a <strong>privileged</strong> CT (no shift), those <code>remap_*</code> users are shadow accounts with no real-world counterpart on the host. They are harmless leftovers from a more ambitious design intent. If you don't see them in <code>getent passwd</code>, nothing breaks."
"gidBody": "With the default LXC ID map (+100000), host GID 101000 appears as GID <strong>1000</strong> inside an unprivileged CT. This server flow uses a <strong>privileged</strong> CT, where there is no shift. Two privileged CTs using <code>sharedfiles</code> at GID 101000 therefore share the same numeric group ID.",
"remapTitle": "Legacy 'remap_*' user creation is skipped for privileged CTs",
"remapBody": "The legacy unprivileged branch would create <code>remap_&lt;uid&gt;</code> users with UID <code>&lt;uid&gt; + 100000</code>, all in <code>sharedfiles</code>, for regular users and common UIDs (33, 1000, 1001, 1002). This <code>+100000</code> shift is irrelevant in the <strong>privileged</strong> CTs required by this flow, so the script skips that branch. Missing <code>remap_*</code> accounts in <code>getent passwd</code> are expected and do not indicate a failure."
},
"defaults": {
"heading": "Default export options — read this first",
@@ -38,7 +38,7 @@
},
"creds": {
"heading": "Where the password lives",
"body": "ProxMenux <strong>never stores the password in plain text</strong> in the mount command or in <code>/etc/fstab</code>. Instead it writes a credentials file inside the CT:",
"body": "ProxMenux <strong>does not put the password in the mount command</strong> or in <code>/etc/fstab</code>. It writes a credentials file inside the CT instead. That file contains plaintext credentials protected by root-only permissions; it is not encrypted:",
"whyTitle": "Why a separate file rather than fstab",
"whyBody": "The CIFS module accepts <code>username=</code> / <code>password=</code> options inline in fstab, but anyone who can read <code>/etc/fstab</code> sees the password in clear. Putting credentials in a root-only file and pointing fstab at it (<code>credentials=</code>) keeps the password out of world-readable config."
},
@@ -85,7 +85,7 @@
}
],
"netEffectTitle": "Net effect of the fstab flags",
"netEffectBody": "Your container always boots, even if the Samba server is offline. The first time something touches the mount path, systemd quietly mounts it. If the server is still down, that one access fails with <em>resource temporarily unavailable</em> — but nothing else in the CT is affected."
"netEffectBody": "The CIFS filesystem is mounted on <em>first access</em> rather than eagerly during boot. If the server is offline, that access may wait or fail, and services that depend on the path can be affected. Automount reduces the boot-time dependency; it does not guarantee that every CT service is unaffected by an unavailable CIFS server."
},
"manual": {
"heading": "Manual equivalent",
@@ -103,7 +103,7 @@
"noShareTitle": "Client connects but cannot see the share",
"noShareBody": "Check that <code>browseable = yes</code> is set in the share block (default for rw / ro modes; may be missing in custom). Also check the CT firewall and the Proxmox host firewall allow TCP 445 (SMB) and 139 (NetBIOS). Some Windows clients also require name resolution — try the IP directly first.",
"authTitle": "Authentication fails (NT_STATUS_LOGON_FAILURE)",
"authBody": "Either the wrong password (Samba passwords are <em>separate</em> from system passwords — see them with <code>pdbedit -L</code>) or the user is not in <code>valid users</code> for that share. Reset the password with <code>smbpasswd &lt;user&gt;</code> inside the CT.",
"authBody": "Either the password is wrong (Samba passwords are <em>separate</em> from system passwords; list Samba users with <code>pdbedit -L</code>) or the user is not in <code>valid users</code> for that share. Reset the password with <code>smbpasswd &lt;user&gt;</code> inside the CT.",
"groupTitle": "Files written by the client appear with wrong group on the server",
"groupBody": "The script sets <code>force group = sharedfiles</code> in the share block, so new files should be group <code>sharedfiles</code>. If they aren't, the SGID bit on the parent directory may have been lost (someone ran <code>chmod</code> by hand). Reapply: <code>chmod 2775 /mnt/&lt;share&gt;</code>.",
"bothTitle": "Sharing the same folder over both NFS and Samba",