This release adds two in-dashboard improvements — a one-click Proxmox update trigger from the Health Monitor and a mobile PWA install prompt — extends the Backups restore flow with atomic pmxcfs (`config.db`) snapshots and automatic ZFS data-pool import, sharpens Log2RAM behaviour on hosts running Proxmox Backup Server as a service, hardens firewall bridge sysctl tuning across VM lifecycle events, narrows the ZFS ARC optimization to its own scope, makes persistent NIC naming idempotent across reruns, rebuilds DKMS drivers automatically when a new kernel is staged, keeps the Monitor terminal session intact when a ProxMenux update is available, and reinforces five notification templates plus three Health panel checks.
This commit is contained in:
MacRimi
2026-07-22 17:04:55 +02:00
committed by GitHub
42 changed files with 2636 additions and 494 deletions
+2
View File
@@ -4,6 +4,7 @@ import { GeistSans } from "geist/font/sans"
import { GeistMono } from "geist/font/mono"
import { ThemeProvider } from "../components/theme-provider"
import { PwaRegister } from "../components/pwa-register"
import { PwaInstallPrompt } from "../components/pwa-install-prompt"
import { Suspense } from "react"
import "./globals.css"
@@ -48,6 +49,7 @@ export default function RootLayout({
</ThemeProvider>
</Suspense>
<PwaRegister />
<PwaInstallPrompt />
</body>
</html>
)
+41 -4
View File
@@ -32,6 +32,7 @@ import {
FileText,
RefreshCw,
Shield,
Download,
X,
Clock,
BellOff,
@@ -39,6 +40,7 @@ import {
Settings2,
HelpCircle,
} from "lucide-react"
import { ScriptTerminalModal } from "./script-terminal-modal"
interface CategoryCheck {
status: string
@@ -122,14 +124,15 @@ export function HealthStatusModal({ open, onOpenChange, getApiUrl }: HealthStatu
const [error, setError] = useState<string | null>(null)
const [dismissingKey, setDismissingKey] = useState<string | null>(null)
const [expandedCategories, setExpandedCategories] = useState<Set<string>>(new Set())
const [showUpdateTerminal, setShowUpdateTerminal] = useState(false)
const fetchHealthDetails = useCallback(async () => {
const fetchHealthDetails = useCallback(async (force = false) => {
setLoading(true)
setError(null)
try {
let newOverallStatus = "OK"
// Use the new combined endpoint for fewer round-trips
const token = getAuthToken()
const authHeaders: Record<string, string> = {}
@@ -137,7 +140,7 @@ export function HealthStatusModal({ open, onOpenChange, getApiUrl }: HealthStatu
authHeaders["Authorization"] = `Bearer ${token}`
}
const response = await fetch(getApiUrl("/api/health/full"), { headers: authHeaders })
const response = await fetch(getApiUrl(force ? "/api/health/full?refresh=1" : "/api/health/full"), { headers: authHeaders })
let infoCount = 0
if (!response.ok) {
@@ -219,7 +222,7 @@ export function HealthStatusModal({ open, onOpenChange, getApiUrl }: HealthStatu
if (open) {
fetchHealthDetails()
// Auto-refresh every 5 minutes while modal is open
const refreshInterval = setInterval(fetchHealthDetails, 300000)
const refreshInterval = setInterval(() => fetchHealthDetails(), 300000)
return () => clearInterval(refreshInterval)
}
}, [open, fetchHealthDetails])
@@ -722,6 +725,23 @@ export function HealthStatusModal({ open, onOpenChange, getApiUrl }: HealthStatu
No issues detected
</div>
)}
{/* Only offer "Update Now" when the category is not
already OK — hiding it when there's nothing
pending prevents the operator from spawning a
terminal that would only report "System is
already up to date". */}
{key === "updates" && status?.toUpperCase() !== "OK" && (
<div className="flex justify-end px-3 py-2 pt-1">
<Button
size="sm"
onClick={() => setShowUpdateTerminal(true)}
className="bg-purple-600/15 hover:bg-purple-600/25 border border-purple-500/40 text-purple-300 hover:text-purple-200"
>
<Download className="h-4 w-4 mr-1.5" />
Update Now
</Button>
</div>
)}
</div>
)}
</div>
@@ -848,6 +868,23 @@ export function HealthStatusModal({ open, onOpenChange, getApiUrl }: HealthStatu
</div>
)}
</DialogContent>
<ScriptTerminalModal
open={showUpdateTerminal}
onClose={() => {
setShowUpdateTerminal(false)
// Force a fresh read (cache-busting via ?refresh=1) so the
// "System Updates" row reflects the state right after the
// update finished, instead of the pre-update cached value.
fetchHealthDetails(true).catch(() => {})
}}
scriptPath="/usr/local/share/proxmenux/scripts/utilities/proxmox_update.sh"
scriptName="proxmox_update"
params={{
EXECUTION_MODE: "web",
}}
title="Proxmox System Update"
description="Runs apt-get update + dist-upgrade and post-update cleanup on the host."
/>
</Dialog>
)
}
+1 -1
View File
@@ -271,7 +271,7 @@ export function Login({ onLogin }: LoginProps) {
</form>
</div>
<p className="text-center text-sm text-muted-foreground">ProxMenux Monitor v1.2.3</p>
<p className="text-center text-sm text-muted-foreground">ProxMenux Monitor v1.2.4</p>
</div>
</div>
)
+1 -1
View File
@@ -836,7 +836,7 @@ export function ProxmoxDashboard() {
</Tabs>
<footer className="mt-8 md:mt-12 pt-4 md:pt-6 border-t border-border text-center text-xs md:text-sm text-muted-foreground">
<p className="font-medium mb-2">ProxMenux Monitor v1.2.3</p>
<p className="font-medium mb-2">ProxMenux Monitor v1.2.4</p>
<p>
<a
href="https://ko-fi.com/macrimi"
+220
View File
@@ -0,0 +1,220 @@
"use client"
import { useCallback, useEffect, useState } from "react"
import { Plus, Share, X } from "lucide-react"
// ==========================================================
// PwaInstallPrompt
// ==========================================================
// Bottom-sheet shown on mobile when the Monitor is opened in
// a browser (not launched as an installed PWA). Two variants:
// iOS Safari → manual 3-step instructions
// Android → generic "browser menu → Add to Home Screen"
//
// No `beforeinstallprompt` handling: capturing the event to
// drive a custom Install button interacts badly with Chrome's
// own "Add to Home Screen" menu — Chromium degrades the manual
// path to a plain shortcut when a page has intercepted the
// event but hasn't yet called `prompt()`. Installation goes
// through the browser's own menu entry and produces a real PWA.
//
// Never shown on desktop, or when already running standalone.
// Dismissal options:
// "Not now" → temporary, hidden for 30 days
// "Don't show again" → permanent (no expiry)
// Backdrop / X → session-only dismiss (reappears on
// the next page load)
// ==========================================================
const DISMISSED_FOREVER_KEY = "proxmenux-install-dismissed"
const DISMISSED_UNTIL_KEY = "proxmenux-install-dismissed-until"
const NOT_NOW_DAYS = 30
function isMobileDevice(): boolean {
if (typeof window === "undefined") return false
// Prefer feature detection (coarse pointer + touch) over UA sniffing,
// and fall back to UA for the corner case where a mobile browser
// reports fine pointer under a desktop-mode toggle.
const coarse = window.matchMedia("(pointer: coarse)").matches
const ua = navigator.userAgent
const uaMobile = /Android|iPhone|iPad|iPod|Mobile|Opera Mini|BlackBerry|IEMobile/i.test(ua)
return coarse || uaMobile
}
function isStandalone(): boolean {
if (typeof window === "undefined") return false
const displayModeStandalone = window.matchMedia("(display-mode: standalone)").matches
const iosStandalone = (window.navigator as Navigator & { standalone?: boolean }).standalone === true
return displayModeStandalone || iosStandalone
}
function isIOS(): boolean {
if (typeof window === "undefined") return false
const ua = navigator.userAgent
// iPadOS 13+ reports as MacIntel — detect that too when maxTouchPoints > 1.
const iPadMasqueradingAsMac =
ua.includes("Macintosh") && (navigator as Navigator & { maxTouchPoints?: number }).maxTouchPoints! > 1
return /iPhone|iPad|iPod/i.test(ua) || iPadMasqueradingAsMac
}
export function PwaInstallPrompt() {
const [open, setOpen] = useState(false)
const [platform, setPlatform] = useState<"ios" | "android" | null>(null)
useEffect(() => {
if (typeof window === "undefined") return
if (!isMobileDevice() || isStandalone()) return
try {
if (localStorage.getItem(DISMISSED_FOREVER_KEY) === "1") return
const untilRaw = localStorage.getItem(DISMISSED_UNTIL_KEY)
if (untilRaw) {
const until = Number.parseInt(untilRaw, 10)
// Corrupt / non-numeric values fall through and the prompt shows,
// which is the safe default.
if (Number.isFinite(until) && until > Date.now()) return
}
} catch {
// localStorage unavailable (private mode etc.) — treat as not dismissed.
}
setPlatform(isIOS() ? "ios" : "android")
setOpen(true)
}, [])
const handleNotNow = useCallback(() => {
try {
const until = Date.now() + NOT_NOW_DAYS * 24 * 60 * 60 * 1000
localStorage.setItem(DISMISSED_UNTIL_KEY, String(until))
} catch {
// Best-effort; if localStorage fails the user will see the prompt
// again next visit, which is the safe default.
}
setOpen(false)
}, [])
const handleNeverAgain = useCallback(() => {
try {
localStorage.setItem(DISMISSED_FOREVER_KEY, "1")
} catch {
// Best-effort; if localStorage fails the user will see the prompt
// again next visit, which is the safe default.
}
setOpen(false)
}, [])
const handleClose = useCallback(() => {
// Session-only dismiss: closing via X or backdrop does NOT persist,
// so the prompt reappears on the next page load. Users who want to
// silence it for longer must use "Not now" (30 d) or "Don't show again".
setOpen(false)
}, [])
if (!open || !platform) return null
return (
<div
role="dialog"
aria-modal="true"
aria-labelledby="pwa-install-title"
className="fixed inset-0 z-[100] flex items-end justify-center bg-black/60 backdrop-blur-sm animate-in fade-in duration-200"
onClick={(e) => {
if (e.target === e.currentTarget) handleClose()
}}
>
<div
className="w-full max-w-md rounded-t-2xl bg-background text-foreground shadow-2xl border-t border-border animate-in slide-in-from-bottom duration-300"
style={{ paddingBottom: "max(1.25rem, env(safe-area-inset-bottom))" }}
>
<div className="relative px-5 pt-5">
<div className="mx-auto mb-3 h-1 w-10 rounded-full bg-border" aria-hidden="true" />
<button
type="button"
onClick={handleClose}
aria-label="Close"
className="absolute right-3 top-3 flex h-8 w-8 items-center justify-center rounded-full text-muted-foreground hover:bg-muted transition-colors"
>
<X className="h-4 w-4" />
</button>
<div className="mb-4 flex items-start gap-3.5">
<div className="flex h-[52px] w-[52px] shrink-0 items-center justify-center rounded-xl bg-muted p-1 shadow-md">
<img src="/icon.svg" alt="ProxMenux Monitor" className="h-full w-full object-contain" />
</div>
<div className="flex-1 min-w-0">
<h3 id="pwa-install-title" className="text-[17px] font-bold leading-tight tracking-tight text-foreground">
Install ProxMenux Monitor
</h3>
<p className="mt-1 text-[13px] leading-snug text-muted-foreground">
{platform === "ios"
? "Add the Monitor to your home screen for quick access."
: "Add the Monitor as an app to launch it like a native application."}
</p>
</div>
</div>
{platform === "ios" ? (
<ol className="mb-4 flex flex-col gap-2" role="list">
<li className="flex items-center gap-3 rounded-xl bg-primary/10 px-3.5 py-3 text-[13.5px] leading-tight">
<span className="flex h-[22px] w-[22px] shrink-0 items-center justify-center rounded-full bg-primary text-[11px] font-bold text-primary-foreground">
1
</span>
<span>
Tap the{" "}
<span className="inline-flex items-center gap-1 font-semibold text-primary">
<Share className="h-4 w-4" aria-hidden="true" />
Share
</span>{" "}
button in the bottom bar
</span>
</li>
<li className="flex items-center gap-3 rounded-xl bg-primary/10 px-3.5 py-3 text-[13.5px] leading-tight">
<span className="flex h-[22px] w-[22px] shrink-0 items-center justify-center rounded-full bg-primary text-[11px] font-bold text-primary-foreground">
2
</span>
<span>
Choose{" "}
<span className="inline-flex items-center gap-1 font-semibold text-primary">
<Plus className="h-4 w-4" aria-hidden="true" />
Add to Home Screen
</span>
</span>
</li>
<li className="flex items-center gap-3 rounded-xl bg-primary/10 px-3.5 py-3 text-[13.5px] leading-tight">
<span className="flex h-[22px] w-[22px] shrink-0 items-center justify-center rounded-full bg-primary text-[11px] font-bold text-primary-foreground">
3
</span>
<span>
Confirm by tapping <b>Add</b> in the top-right
</span>
</li>
</ol>
) : (
<div className="mb-4 rounded-lg border border-border bg-muted/50 px-3.5 py-3 text-[13px] leading-relaxed text-muted-foreground">
Open the browser menu <b className="text-foreground"></b> {" "}
<b className="text-foreground">Add to Home Screen</b> confirm by tapping{" "}
<b className="text-foreground">Install</b>.
</div>
)}
<div className="mt-1 flex flex-col gap-1 border-t border-border pt-3">
<button
type="button"
onClick={handleNotNow}
className="rounded-lg py-2.5 text-center text-[13.5px] font-semibold text-muted-foreground hover:bg-muted transition-colors"
>
Not now
</button>
<button
type="button"
onClick={handleNeverAgain}
className="rounded-lg py-2.5 text-center text-[13.5px] font-semibold text-amber-700 dark:text-amber-500 hover:bg-muted transition-colors"
>
Don&apos;t show again
</button>
</div>
</div>
</div>
</div>
)
}
+10 -28
View File
@@ -2,38 +2,20 @@
import { useEffect } from "react"
// ===========================================================
// PwaRegister
// ===========================================================
// Registers /sw.js once on mount. Chrome (Android) only
// surfaces the "Install app" prompt when a service worker
// is active, so even though the SW itself does nothing
// (network-only, no caching), its mere presence flips the
// PWA-installability check from no-to-yes.
//
// Mounted from app/layout.tsx so it runs on every route.
// ===========================================================
// Unregister any Service Worker on this origin at mount. A SW here
// interacts badly with mobile battery throttling behind reverse
// proxies. `sw.js` is kept for a future PWA-offline revisit.
export function PwaRegister() {
useEffect(() => {
if (typeof window === "undefined") return
if (!("serviceWorker" in navigator)) return
// Wait for the load event to avoid competing with the
// initial render — the SW registration is not on the
// critical render path.
const register = () => {
navigator.serviceWorker
.register("/sw.js", { scope: "/" })
.catch((err) => {
// Surface the failure only in DevTools — silent in prod.
console.warn("[pwa] service worker registration failed:", err)
})
}
if (document.readyState === "complete") {
register()
} else {
window.addEventListener("load", register, { once: true })
return () => window.removeEventListener("load", register)
}
navigator.serviceWorker
.getRegistrations()
.then((regs) => {
if (regs.length === 0) return
return Promise.all(regs.map((r) => r.unregister()))
})
.catch(() => {})
}, [])
return null
}
+19 -20
View File
@@ -6,7 +6,7 @@ import { Dialog, DialogContent, DialogTitle } from "./ui/dialog"
import { X, Sparkles, Thermometer, Activity, HardDrive, Shield, Globe, Cpu, Zap, Sliders, Wrench, RefreshCw, Server, BellOff, Bell, Calendar, DatabaseBackup } from "lucide-react"
import { Checkbox } from "./ui/checkbox"
const APP_VERSION = "1.2.3" // Sync with AppImage/package.json
const APP_VERSION = "1.2.4" // Sync with AppImage/package.json
interface ReleaseNote {
date: string
@@ -18,6 +18,21 @@ interface ReleaseNote {
}
export const CHANGELOG: Record<string, ReleaseNote> = {
"1.2.3": {
date: "July 15, 2026",
changes: {
added: [
"Backups integrated in the Monitor — a new first-class section to create, schedule and restore host backups against Local, PBS or Borg destinations from the Web dashboard. Jobs run on a proper systemd timer or attach to an existing PVE vzdump job with retention live-inherited from the parent. Encrypted PBS backups store a paired recovery blob next to each snapshot so a fresh install can always get the key back. After a reboot the tab shows a real-time restore progress card with milestones, per-component status (NVIDIA, Intel GPU tools, Coral, AMD tools), boot sanity warnings and a rollback delta listing anything on the host that wasn't in the backup.",
"Network Flow diagram — a new live topology view on the Network tab showing NICs → host → bridges → LXCs / VMs with animated rx / tx pulses on every internal link, so the operator can see in real time how traffic distributes inside the host and which guests are pulling or pushing data.",
"Physical Disks and Physical Interfaces cards redesigned — clearer per-item presentation on the Storage and Network tabs. USB-NVMe / USB-SATA enclosures reporting removable=0 (ASMedia, JMicron, Realtek, ASM105x) now walk sysfs to detect USB attachment, so the -d snt* pass-through is tried and the drive's real model, serial, temperature, power-on hours and health surface — instead of the bridge's chatter.",
"Richer notifications out of the box — for users not running an AI agent, the templated body now identifies the affected object (which storage, which interface, which container), surfaces the top offenders with an \"…and N more\" tail when the list is long, and preserves the same identity in the recovery message. Users with AI enrichment enabled continue to get their tailored rewrite on top of this improved base.",
],
changed: [
"Redesigned cards across Overview, VM / LXC, Storage and Network — layouts reworked for faster reading and denser, more practical information: key numbers surface at a glance, grouped by relevance, and the responsive grid now behaves cleanly from a phone up to an ultrawide.",
"Health Monitor Thresholds — the Settings panel that controls per-category Warning and Critical levels (CPU, memory, temperature, storage, disks, ...) was reworked with clearer visual grouping and inline hints, so tuning a threshold now takes a couple of clicks instead of scrolling through a wall of numbers.",
],
},
},
"1.2.2": {
date: "May 31, 2026",
changes: {
@@ -217,28 +232,12 @@ export const CHANGELOG: Record<string, ReleaseNote> = {
const CURRENT_VERSION_FEATURES = [
{
icon: <DatabaseBackup className="h-5 w-5" />,
text: "Backups integrated in the Monitor — a new first-class section to create, schedule and restore host backups against Local, PBS or Borg destinations from the Web dashboard. Jobs run on a proper systemd timer or attach to an existing PVE vzdump job with retention live-inherited from the parent. Encrypted PBS backups store a paired recovery blob next to each snapshot so a fresh install can always get the key back. After a reboot the tab shows a real-time restore progress card with milestones, per-component status (NVIDIA, Intel GPU tools, Coral, AMD tools), boot sanity warnings and a rollback delta listing anything on the host that wasn't in the backup.",
},
{
icon: <Activity className="h-5 w-5" />,
text: "Network Flow diagram — a new live topology view on the Network tab showing NICs → host → bridges → LXCs / VMs with animated rx / tx pulses on every internal link, so the operator can see in real time how traffic distributes inside the host and which guests are pulling or pushing data.",
icon: <RefreshCw className="h-5 w-5" />,
text: "One-click host update from the Health Monitor — new Update Now button in the System Updates section runs the Proxmox update flow in an in-dashboard terminal, without leaving the browser.",
},
{
icon: <Sparkles className="h-5 w-5" />,
text: "Redesigned cards across Overview, VM / LXC, Storage and Network — layouts reworked for faster reading and denser, more practical information: key numbers surface at a glance, grouped by relevance, and the responsive grid now behaves cleanly from a phone up to an ultrawide.",
},
{
icon: <HardDrive className="h-5 w-5" />,
text: "Physical Disks and Physical Interfaces cards redesigned — clearer per-item presentation on the Storage and Network tabs. USB-NVMe / USB-SATA enclosures reporting removable=0 (ASMedia, JMicron, Realtek, ASM105x) now walk sysfs to detect USB attachment, so the -d snt* pass-through is tried and the drive's real model, serial, temperature, power-on hours and health surface — instead of the bridge's chatter.",
},
{
icon: <Sliders className="h-5 w-5" />,
text: "Health Monitor Thresholds — the Settings panel that controls per-category Warning and Critical levels (CPU, memory, temperature, storage, disks, ...) was reworked with clearer visual grouping and inline hints, so tuning a threshold now takes a couple of clicks instead of scrolling through a wall of numbers.",
},
{
icon: <Bell className="h-5 w-5" />,
text: "Richer notifications out of the box — for users not running an AI agent, the templated body now identifies the affected object (which storage, which interface, which container), surfaces the top offenders with an \"…and N more\" tail when the list is long, and preserves the same identity in the recovery message. Users with AI enrichment enabled continue to get their tailored rewrite on top of this improved base.",
text: "In-app Install prompt for mobile — first-time visitors on Android and iOS Safari now see a bottom-sheet with clear steps for adding the Monitor to their home screen as a PWA.",
},
]
@@ -65,6 +65,16 @@ interface RestoreRollback {
components_to_uninstall?: string[]
}
interface DataPoolsImport {
ok: string[]
forced: string[]
partial: string[]
missing: string[]
failed: string[]
finished_at?: string
log_path?: string
}
interface RestoreState {
status: "running" | "complete" | "failed"
started_at: string
@@ -79,6 +89,7 @@ interface RestoreState {
summary: RestoreSummary | null
acknowledged: boolean
duration?: string
data_pools_import?: DataPoolsImport
}
interface HistoryEntry {
@@ -371,6 +382,8 @@ const RestoreDetailModal: React.FC<{
</div>
)}
{state.data_pools_import && <DataPoolsBlock section={state.data_pools_import} />}
<div className="space-y-2">
<div className="text-sm font-medium">Rollback delta</div>
<RollbackDelta delta={state.rollback_delta} />
@@ -392,6 +405,88 @@ const RestoreDetailModal: React.FC<{
)
}
// Rendered inside RestoreDetailModal — one row per outcome category
// (imported / forced / partial skip / missing skip / failed).
const DataPoolsBlock: React.FC<{ section: DataPoolsImport }> = ({ section }) => {
const total =
section.ok.length +
section.forced.length +
section.partial.length +
section.missing.length +
section.failed.length
if (total === 0) return null
const Row: React.FC<{
label: string
tone: "ok" | "warn" | "info" | "error"
items: string[]
help?: string
}> = ({ label, tone, items, help }) => {
if (items.length === 0) return null
const toneClass =
tone === "ok"
? "text-emerald-400"
: tone === "warn"
? "text-amber-400"
: tone === "error"
? "text-red-400"
: "text-blue-400"
return (
<div className="rounded-md border border-border bg-muted/30 px-3 py-2 text-xs">
<div className={`font-medium ${toneClass} flex items-center gap-2`}>
{tone === "ok" && <CheckCircle2 className="h-3.5 w-3.5" />}
{tone === "warn" && <AlertTriangle className="h-3.5 w-3.5" />}
{tone === "error" && <XCircle className="h-3.5 w-3.5" />}
{tone === "info" && <CheckCircle2 className="h-3.5 w-3.5" />}
<span>{label}</span>
<span className="text-muted-foreground">({items.length})</span>
</div>
<div className="mt-1 font-mono text-muted-foreground break-all">{items.join(", ")}</div>
{help && <div className="mt-1 text-muted-foreground">{help}</div>}
</div>
)
}
return (
<div className="space-y-2">
<div className="text-sm font-medium flex items-center gap-2">
<Cpu className="h-4 w-4" />
ZFS data pools — auto-import
</div>
<div className="space-y-1.5">
<Row label="Imported" tone="ok" items={section.ok} />
<Row
label="Imported (forced, foreign hostid)"
tone="info"
items={section.forced}
help="New hostid grabbed onto the pool label — next boot imports clean."
/>
<Row
label="Skipped (some disks missing)"
tone="warn"
items={section.partial}
help="Some vdev disks weren't found by /dev/disk/by-id. Pool NOT imported to avoid a degraded auto-import. Fix the disks or import manually with zpool import."
/>
<Row
label="Skipped (no disks present)"
tone="warn"
items={section.missing}
help="None of the pool's disks are on this host. Move the disks over or import from a different host."
/>
<Row
label="Import failed"
tone="error"
items={section.failed}
help="ZFS rejected the import even with -f. Inspect with `zpool import` and the log below."
/>
</div>
{section.log_path && (
<div className="text-xs text-muted-foreground font-mono">Log: {section.log_path}</div>
)}
</div>
)
}
// ── History browser modal ─────────────────────────────────────
const RestoreHistoryModal: React.FC<{ open: boolean; onClose: () => void }> = ({ open, onClose }) => {
@@ -510,6 +605,14 @@ export const RestoreProgressCard: React.FC = () => {
}
const hasWarnings = state.sanity_warnings.length > 0
const pools = state.data_pools_import
const poolCount =
(pools?.ok.length ?? 0) +
(pools?.forced.length ?? 0) +
(pools?.partial.length ?? 0) +
(pools?.missing.length ?? 0) +
(pools?.failed.length ?? 0)
const poolWarnings = (pools?.partial.length ?? 0) + (pools?.missing.length ?? 0) + (pools?.failed.length ?? 0)
const barColor =
state.status === "failed" ? "bg-red-500" : state.status === "complete" ? "bg-emerald-500" : "bg-blue-500"
@@ -530,6 +633,20 @@ export const RestoreProgressCard: React.FC = () => {
{state.sanity_warnings.length} boot warning{state.sanity_warnings.length === 1 ? "" : "s"}
</Badge>
)}
{poolCount > 0 && (
<Badge
variant="outline"
className={
poolWarnings > 0
? "text-amber-400 border-amber-500/40 bg-amber-500/10 gap-1"
: "text-emerald-400 border-emerald-500/40 bg-emerald-500/10 gap-1"
}
>
<Cpu className="h-3 w-3" />
{poolCount} ZFS pool{poolCount === 1 ? "" : "s"}
{poolWarnings > 0 && ` · ${poolWarnings} need attention`}
</Badge>
)}
</CardTitle>
<div className="flex items-center gap-2">
<Button size="sm" variant="outline" onClick={() => setDetailOpen(true)}>
+1 -1
View File
@@ -3717,7 +3717,7 @@ ${observationsHtml}
<!-- Footer -->
<div class="rpt-footer">
<div>Report generated by ProxMenux Monitor</div>
<div>ProxMenux Monitor v1.2.3</div>
<div>ProxMenux Monitor v1.2.4</div>
</div>
</body>
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "ProxMenux-Monitor",
"version": "1.2.3",
"version": "1.2.4",
"description": "Proxmox System Monitoring Dashboard",
"private": true,
"scripts": {
+19 -1
View File
@@ -101,7 +101,14 @@ def acknowledge_error():
'security': 'security_check',
'temperature': 'cpu_check',
'network': 'network_check',
# Both 'disks' (kept for compat) and 'storage' land on the
# same cache — the two categories share `storage_check` in
# health_monitor. Without the 'storage' entry, dismissing
# a storage_unavailable / mount_stale / lxc_mount_low
# error persisted but never invalidated the cache, so the
# error stayed visible in the next fetch.
'disks': 'storage_check',
'storage': 'storage_check',
'vms': 'vms_check',
}
cache_key = cache_key_map.get(category)
@@ -223,14 +230,25 @@ def get_full_health():
Get complete health data in a single request: detailed status + active errors + dismissed.
Uses background-cached results if fresh (< 6 min) for instant response,
otherwise runs a fresh check.
?refresh=1 busts the background + per-check caches for updates/services/
security before returning, so an event that just changed underlying state
(Update Now finished, dismiss action) sees the new value immediately
instead of waiting for the next polling tick.
"""
import time as _time
try:
if request.args.get('refresh') == '1':
for ck in ('updates_check', 'pve_services', 'security_check',
'_bg_detailed', '_bg_overall', 'overall_health'):
health_monitor.last_check_times.pop(ck, None)
health_monitor.cached_results.pop(ck, None)
# Try to use the background-cached detailed result for instant response
bg_key = '_bg_detailed'
bg_last = health_monitor.last_check_times.get(bg_key, 0)
bg_age = _time.time() - bg_last
if bg_age < 360 and bg_key in health_monitor.cached_results:
# Use cached result (at most ~5 min old)
details = health_monitor.cached_results[bg_key]
+51 -1
View File
@@ -214,6 +214,53 @@ def _is_loopback_addr(value: str) -> bool:
return value == 'localhost'
def _is_own_host_ip(value: str) -> bool:
"""Return True when ``value`` is loopback OR an IP bound to any local iface.
``_pve_webhook_url()`` may register a URL that resolves to the host's
LAN/VPN IP when SSL is on and a hostname cert is loaded (issue #239).
In that case PVE POSTs to e.g. ``https://<fqdn>:8008`` and — on Linux —
the connection is routed to the local interface holding that IP; the
Flask socket sees the peer as the interface IP, NOT ``127.0.0.1``. The
request is still coming from THIS host, so the loopback trust path
should extend to any of this host's own interface IPs (Tailscale/Zerotier
CGNAT, WireGuard, LAN, IPv6 GUA…). Without this, PVE hits the layer 3
``X-ProxMenux-Timestamp`` check — a header PVE cannot inject dynamically —
and every notification target test returns ``401 missing_timestamp``.
IMPORTANT: Flask bound to ``*:8008`` (dual-stack) reports IPv4 peers
in v4-mapped IPv6 form (``::ffff:192.168.0.55``). psutil reports the
same interface as plain IPv4 (``192.168.0.55``). Without unmapping,
literal comparison fails and the fix effectively does nothing in
production — reproduced end-to-end on 192.168.0.55: passing the raw
literal returned True, but ``::ffff:192.168.0.55`` returned False,
which is what Flask actually hands us at runtime.
"""
if _is_loopback_addr(value):
return True
try:
import ipaddress
import socket
import psutil
addr = ipaddress.ip_address(value)
mapped = getattr(addr, 'ipv4_mapped', None)
if mapped is not None:
addr = mapped
client = addr.compressed
for _iface, addrs in psutil.net_if_addrs().items():
for a in addrs:
if a.family in (socket.AF_INET, socket.AF_INET6):
ip_str = a.address.split('%')[0] # strip IPv6 zone id
try:
if ipaddress.ip_address(ip_str).compressed == client:
return True
except ValueError:
continue
except Exception:
pass
return False
def _validate_event_type(value: str) -> bool:
return isinstance(value, str) and bool(_EVENT_TYPE_RE.match(value))
@@ -1407,7 +1454,10 @@ def proxmox_webhook():
_reject = lambda code, error, status: (jsonify({'accepted': False, 'error': error}), status)
client_ip = request.remote_addr or ''
is_localhost = _is_loopback_addr(client_ip)
# Trust loopback AND any IP bound to a local interface — see
# `_is_own_host_ip` for the FQDN/CGNAT rationale. Layer 1 rate
# limiting still applies to every request.
is_localhost = _is_own_host_ip(client_ip)
# CSRF defence-in-depth: reject `application/x-www-form-urlencoded`
# bodies. PVE always sends `application/json`; form-encoded bodies
@@ -262,6 +262,10 @@ def terminal_websocket(ws):
_term_env.setdefault('COLORTERM', 'truecolor')
_term_env.setdefault('LANG', 'C.UTF-8')
_term_env.setdefault('LC_ALL', 'C.UTF-8')
# Inherited by every child of this shell (including `menu`), so the
# update path can tell it's running inside a WebSocket-backed session
# that would be cut mid-install if the Monitor service restarted.
_term_env['PROXMENUX_TERMINAL'] = 'monitor'
_term_env.pop('PS1', None)
_home = _term_env.get('HOME') or os.path.expanduser('~') or '/root'
+13 -7
View File
@@ -3614,26 +3614,32 @@ class HealthMonitor:
if health_persistence.check_vm_running(vm_id):
continue # Error auto-resolved if VM is now running
# Still active, add to details
# Still active, add to details. `details` may be persisted
# as SQL NULL / JSON null → deserializes to Python None, and
# `dict.get('details', {})` returns None (not `{}`) in that
# case. Coalesce explicitly to avoid `NoneType has no
# attribute 'get'` (issue #255 in 1.2.3).
details = error.get('details') or {}
vm_details[error_key] = {
'status': error['severity'],
'reason': error['reason'],
'id': error.get('details', {}).get('id', 'unknown'),
'type': error.get('details', {}).get('type', 'VM/CT'),
'id': details.get('id', 'unknown'),
'type': details.get('type', 'VM/CT'),
'first_seen': error['first_seen'],
'dismissed': False,
}
issues.append(f"{error.get('details', {}).get('type', 'VM')} {error.get('details', {}).get('id', '')}: {error['reason']}")
issues.append(f"{details.get('type', 'VM')} {details.get('id', '')}: {error['reason']}")
# Process dismissed errors (show as INFO)
for error in dismissed_vm_errors:
error_key = error['error_key']
if error_key not in vm_details: # Don't overwrite active errors
details = error.get('details') or {}
vm_details[error_key] = {
'status': 'INFO',
'reason': error['reason'],
'id': error.get('details', {}).get('id', 'unknown'),
'type': error.get('details', {}).get('type', 'VM/CT'),
'id': details.get('id', 'unknown'),
'type': details.get('type', 'VM/CT'),
'first_seen': error['first_seen'],
'dismissed': True,
}
+12 -1
View File
@@ -918,11 +918,22 @@ class HealthPersistence:
# Try to infer category from the error_key prefix.
category = ''
# Order matters: more specific prefixes MUST come before shorter ones
# e.g. 'security_updates' (updates) before 'security_' (security)
# e.g. 'security_updates' (updates) before 'security_' (security),
# and 'lxc_disk_low_' / 'zfs_pool_full_' (storage) before the shorter
# 'disk_' / 'zfs_pool_' fallbacks that map to 'disks'.
for cat, prefix in [('updates', 'security_updates'), ('updates', 'system_age'),
('updates', 'pending_updates'), ('updates', 'kernel_pve'),
('security', 'security_'),
('pve_services', 'pve_service_'), ('vms', 'vmct_'), ('vms', 'vm_'), ('vms', 'ct_'),
# ── Storage keys — HealthMonitor emits these under `storage` category
# but they used to fall through to 'general' here because no prefix
# matched, breaking the Dismiss flow (the acknowledge would persist
# but the storage cache wouldn't be invalidated because the ack was
# tagged with the wrong category).
('storage', 'storage_unavailable_'), ('storage', 'mount_stale'),
('storage', 'mount_readonly'), ('storage', 'lxc_disk_low_'),
('storage', 'lxc_mount_low_'), ('storage', 'pve_storage_full_'),
('storage', 'zfs_pool_full_'),
('disks', 'disk_smart_'), ('disks', 'disk_'), ('disks', 'smart_'), ('disks', 'zfs_pool_'),
('logs', 'log_'), ('network', 'net_'),
('temperature', 'temp_')]:
+9 -1
View File
@@ -983,6 +983,10 @@ class EmailChannel(NotificationChannel):
elif group == 'backup':
_add('VM/CT ID', data.get('vmid'), 'code')
_add('Name', data.get('vmname'), 'bold')
# Storage / destination — the piece a multi-PBS operator needs to
# tell which target the backup ran against. Reported gap: emails
# showed no way to distinguish which PBS failed with 2+ configured.
_add('Storage', data.get('storage') or data.get('storage_name'), 'code')
_add('Status', 'Failed' if 'fail' in event_type else 'Completed' if 'complete' in event_type else 'Started',
'severity' if 'fail' in event_type else '')
_add('Size', data.get('size'))
@@ -1082,7 +1086,11 @@ class EmailChannel(NotificationChannel):
)
rows.append((esc('Important Packages'), pkg_html))
_add('Current Version', data.get('current_version'), 'code')
_add('New Version', data.get('new_version'), 'code')
# `new_version` is the field used by generic package-update events;
# driver-update templates (nvidia, coral) populate `latest_version`.
# Read both so the tabular row is never empty when the template's
# title/body already printed the new version.
_add('New Version', data.get('new_version') or data.get('latest_version'), 'code')
# ── Other / unknown ──
else:
+155 -8
View File
@@ -396,6 +396,74 @@ def is_vzdump_active_on_host() -> bool:
return found
# ─── APT / dpkg activity gate ────────────────────────────────────
# PVE services (pve-cluster, pveproxy, pvedaemon, corosync…) are
# routinely killed and restarted as part of a package upgrade, so
# every full-upgrade produces a burst of `service_fail` events that
# are entirely expected. We gate `_check_service_failure` on this
# helper so those events are silently dropped while apt is running
# (plus a short grace window after it exits).
_APT_ACTIVE_MARKER = '/var/run/proxmenux-update-in-progress'
_APT_FINISHED_MARKER = '/var/run/proxmenux-update-just-finished'
_APT_GRACE_SECONDS = 60
_DPKG_LOCK_FILE = '/var/lib/dpkg/lock-frontend'
_APT_ACTIVE_CACHE_TTL = 3.0
_apt_active_cache_ts = 0.0
_apt_active_cache_value = False
def is_apt_active_on_host() -> bool:
"""Return True while apt/dpkg is running on this host.
Sources checked, in order:
1. `/var/run/proxmenux-update-in-progress` created by
`scripts/utilities/proxmox_update.sh` around its full-upgrade
call so ProxMenux-driven updates are always covered.
2. `fuser` on `/var/lib/dpkg/lock-frontend` covers a manual
`apt`/`dpkg`/`apt-get` invocation by the operator, or any
other tool holding the lock.
3. Grace window (60 s) after `/var/run/proxmenux-update-just-finished`
was touched catches the tail of service_fail events that
only reach the journal shortly after apt itself exits.
Cached 3 s so a burst of journal events doesn't spawn a fuser
subprocess per line. Caller-safe: returns False on any error.
"""
global _apt_active_cache_ts, _apt_active_cache_value
now = time.time()
if now - _apt_active_cache_ts < _APT_ACTIVE_CACHE_TTL:
return _apt_active_cache_value
active = False
try:
if os.path.exists(_APT_ACTIVE_MARKER):
active = True
elif os.path.exists(_APT_FINISHED_MARKER):
try:
if now - os.path.getmtime(_APT_FINISHED_MARKER) < _APT_GRACE_SECONDS:
active = True
except OSError:
pass
if not active:
try:
result = subprocess.run(
['fuser', _DPKG_LOCK_FILE],
capture_output=True, timeout=2,
)
if result.returncode == 0 and result.stdout.strip():
active = True
except (subprocess.TimeoutExpired, FileNotFoundError, OSError):
pass
except Exception:
active = False
_apt_active_cache_ts = now
_apt_active_cache_value = active
return active
# ─── Journal Watcher (Real-time) ─────────────────────────────────
class JournalWatcher:
@@ -801,7 +869,7 @@ class JournalWatcher:
if re.search(pattern, msg, re.IGNORECASE):
entity = 'node'
entity_id = ''
# Build a context-rich reason from the journal message.
enriched = reason
@@ -959,7 +1027,7 @@ class JournalWatcher:
enriched = f"{reason}\n{msg[:300]}"
data = {'reason': enriched, 'hostname': self._hostname}
self._emit(event_type, severity, data, entity=entity, entity_id=entity_id)
return
@@ -1051,6 +1119,14 @@ class JournalWatcher:
def _check_service_failure(self, msg: str, unit: str):
"""Detect critical service failures with enriched context."""
# Skip while apt/dpkg is running. PVE services (pve-cluster,
# pveproxy, pvedaemon, corosync…) get killed and restarted as a
# normal part of every package upgrade, so their `service_fail`
# events during that window are expected noise, not real
# failures. See `is_apt_active_on_host()` for detection details.
if is_apt_active_on_host():
return
# Filter out noise -- these are normal systemd transient units,
# not real service failures worth alerting about.
_NOISE_PATTERNS = [
@@ -1800,7 +1876,51 @@ class TaskWatcher:
except Exception as e:
# Log error for debugging but return status as fallback
return status
def _get_task_context(self, upid: str, task_type: str) -> dict:
"""Extract task-type-specific fields from a task log.
For snapshots and migrations the interesting metadata (snapshot
name, target node) lives inside the task log body not in the
UPID itself. Without it, `snapshot_complete` bodies render as
`Snapshot "" created` and `migration_complete` as `... migrated
to node .` both real rendering bugs.
"""
wanted = None
if task_type in ('qmsnapshot', 'vzsnapshot'):
wanted = 'snapshot'
elif task_type in ('qmigrate', 'vzmigrate'):
wanted = 'migration'
if wanted is None:
return {}
try:
parts = upid.split(':')
if len(parts) < 5:
return {}
starttime_hex = parts[4]
if not starttime_hex:
return {}
subdir = starttime_hex[-1].upper()
log_path = os.path.join(self.TASK_DIR, subdir, upid)
if not os.path.exists(log_path):
return {}
with open(log_path, 'r', errors='replace') as f:
head = ''.join(f.readline() for _ in range(30))
ctx: dict = {}
if wanted == 'snapshot':
m = (re.search(r"snapshot\s+['\"]([^'\"\n]{1,80})['\"]", head, re.IGNORECASE)
or re.search(r"snapshot(?:\s+name)?\s+([A-Za-z0-9._\-]{1,80})", head, re.IGNORECASE))
if m:
ctx['snapshot_name'] = m.group(1).strip()
elif wanted == 'migration':
m = (re.search(r"to\s+node\s+([A-Za-z0-9._\-]{1,60})", head, re.IGNORECASE)
or re.search(r"migration to\s+(?:node\s+)?([A-Za-z0-9._\-]{1,60})", head, re.IGNORECASE))
if m:
ctx['target_node'] = m.group(1).strip()
return ctx
except Exception:
return {}
# Map PVE task types to our event types
TASK_MAP = {
'qmstart': ('vm_start', 'INFO'),
@@ -2111,16 +2231,21 @@ class TaskWatcher:
reason = self._get_task_log_reason(upid, status)
else:
reason = ''
# Populate task-type-specific fields (target_node for migrations,
# snapshot_name for snapshots) so the corresponding template bodies
# don't render "to node ." or `Snapshot ""`.
ctx = self._get_task_context(upid, task_type)
data = {
'vmid': vmid,
'vmname': vmname or f'ID {vmid}',
'hostname': self._hostname,
'user': user,
'reason': reason,
'target_node': '',
'target_node': ctx.get('target_node', ''),
'size': '',
'snapshot_name': '',
'snapshot_name': ctx.get('snapshot_name', ''),
}
# Determine entity type from task type
@@ -3017,7 +3142,8 @@ class PollingCollector:
'system_startup', severity, data, source='polling',
entity='node', entity_id='',
))
startup_grace.mark_startup_aggregated()
# ── Update check (enriched) ────────────────────────────────
# Proxmox-related package prefixes used for categorisation
@@ -3892,6 +4018,20 @@ class ProxmoxHookWatcher:
'job_id': pve_job_id,
}
# `system_problem` is the generic fallback of `_classify_pve` for
# unknown/empty pve_type. Without a populated `reason`, the template
# renders "Reason: " (empty) and `_summarize_event` falls back to
# printing the raw event_type ("problema_del_sistema") in every
# `burst_system` aggregate, producing the useless
# "🔵 constructor: Problema del sistema detectado" / "+1 problema
# más del sistema (Problemas adicionales: - problema_del_sistema)"
# messages the operator sees on Telegram. Preserve the PVE payload
# as `reason` so both surfaces have concrete text to render.
if event_type == 'system_problem':
reason_text = (message or title or '').strip()
if reason_text:
data['reason'] = reason_text[:500]
# ProxMenux Host Backup: pull the extra fields that the runner
# packs into payload.fields so the host_backup_* templates can
# render backend, destination, sizes, etc. without falling back
@@ -3963,7 +4103,14 @@ class ProxmoxHookWatcher:
dur_m = re.search(r'Total running time:\s*(.+?)(?:\n|$)', message)
if dur_m:
data['duration'] = dur_m.group(1).strip()
# Extract storage / destination from the "starting new backup job" line
# so the notification (email HTML, Telegram title) can show which
# PBS / storage the backup landed on. Operators with multiple PBS
# targets need this to diagnose which destination failed.
storage_m = re.search(r'--storage\s+(\S+)', message)
if storage_m:
data['storage'] = storage_m.group(1).strip()
# Capture journal context for critical/warning events (helps AI provide better context)
if severity in ('CRITICAL', 'WARNING') and event_type not in ('backup_complete', 'update_available'):
# Build keywords from available data for journal search
+6 -6
View File
@@ -646,22 +646,22 @@ TEMPLATES = {
'default_enabled': False,
},
'backup_complete': {
'title': '{hostname}: Backup complete — {vmname} ({vmid})',
'body': 'Backup of {vmname} (ID: {vmid}) completed successfully.\nSize: {size}',
'title': '{hostname}{storage}: Backup complete — {vmname} ({vmid})',
'body': 'Backup of {vmname} (ID: {vmid}) completed successfully on {storage}.\nSize: {size}',
'label': 'Backup complete',
'group': 'backup',
'default_enabled': True,
},
'backup_warning': {
'title': '{hostname}: Backup complete with warnings — {vmname} ({vmid})',
'body': 'Backup of {vmname} (ID: {vmid}) completed but encountered warnings.\nWarnings: {reason}',
'title': '{hostname}{storage}: Backup complete with warnings — {vmname} ({vmid})',
'body': 'Backup of {vmname} (ID: {vmid}) on {storage} completed but encountered warnings.\nWarnings: {reason}',
'label': 'Backup (warnings)',
'group': 'backup',
'default_enabled': True,
},
'backup_fail': {
'title': '{hostname}: Backup FAILED — {vmname} ({vmid})',
'body': 'Backup of {vmname} (ID: {vmid}) failed.\nReason: {reason}',
'title': '{hostname}{storage}: Backup FAILED — {vmname} ({vmid})',
'body': 'Backup of {vmname} (ID: {vmid}) failed on {storage}.\nReason: {reason}',
'label': 'Backup FAILED',
'group': 'backup',
'default_enabled': True,
+142 -11
View File
@@ -1,4 +1,135 @@
## 2026-07-17
### New version ProxMenux v1.2.4
This release adds two in-dashboard improvements — a one-click Proxmox update trigger from the Health Monitor and a mobile PWA install prompt — extends the Backups restore flow with atomic pmxcfs (`config.db`) snapshots and automatic ZFS data-pool import, sharpens Log2RAM behaviour on hosts running Proxmox Backup Server as a service, hardens firewall bridge sysctl tuning across VM lifecycle events, narrows the ZFS ARC optimization to its own scope, makes persistent NIC naming idempotent across reruns, rebuilds DKMS drivers automatically when a new kernel is staged, keeps the Monitor terminal session intact when a ProxMenux update is available, and reinforces five notification templates plus three Health panel checks.
---
## 🩺 Update Now button in Health Monitor
- New **Update Now** button inside the Health Monitor modal, under the **System Updates** section.
- Runs the standard Proxmox update flow (`apt update` + `dist-upgrade` + post-update cleanup) in an in-dashboard terminal — no need to open a shell.
- Only appears when updates are pending; when the system is up to date the button stays hidden.
- On close, the Health Monitor forces a cache-busting refresh (`/api/health/full?refresh=1`) so the pending-update count and kernel row reflect the post-update state right away, instead of the pre-update value that the background cache had stored moments before the run.
- The underlying script is context-aware: on an already-configured production host it respects the user's custom repositories (never disables enterprise/ceph, never deletes legacy sources, never purges alternate NTP, never force-installs zfsutils/chrony); on a bare host it lays down only the missing base repos. It also detects a newly installed kernel that isn't the running one and prompts for reboot at the end.
- During the upgrade, `service_fail` notifications for PVE services (pve-cluster, pveproxy, corosync…) are suppressed — their restart is a normal part of the upgrade cycle. Suppression extends 60 s past apt exit so the trailing restart events don't leak through.
---
## 📱 In-app Install prompt for mobile
- First-time visitors on **Android** (Chrome / Brave) and **iOS Safari** now see a bottom-sheet with clear instructions for adding the Monitor as a PWA to their home screen.
- Installation goes through the browser's own menu entry ("Add to Home Screen"), which produces a real installed PWA that launches in standalone mode. The sheet doesn't intercept the browser's `beforeinstallprompt` event — intercepting it and not calling `prompt()` degrades the manual menu path to a plain shortcut, which is what showed up in field testing.
- Two dismissal levels: **Not now** (temporary, reappears in 30 days) and **Don't show again** (permanent, stored in `localStorage`).
- Never shown on desktop, or once the Monitor is already running standalone.
---
## 🔔 Notification content — five rendering refinements
- **Backup destination in title and body** — VM/CT backup emails and Telegram messages carry the storage / PBS target, so users with several backup destinations can tell at a glance which one produced the event.
- **Migration bodies carry the real target node** — pulled from the PVE task log for `qmigrate` / `vzmigrate` events.
- **Snapshot bodies carry the real snapshot name** — pulled from the PVE task log for `qmsnapshot` / `vzsnapshot` events.
- **Generic `system_problem` notifications include the real reason** — PVE payload messages are surfaced as the notification body.
- **NVIDIA / Coral driver update emails render the *New Version* row correctly** — the template placeholder is now aligned with the field the renderer reads.
---
## 🩹 Health panel — three checks reinforced
- **Dismiss now silences storage alerts.** The acknowledge flow includes `storage_unavailable`, `mount_stale`, `mount_readonly`, `lxc_disk_low`, `lxc_mount_low`, `pve_storage_full` and `zfs_pool_full` under the `storage` category, and the storage cache is invalidated on dismiss so the panel refreshes immediately.
- **VMs & Containers check tolerates persisted errors with a NULL `details` column** (#255). `_check_vms_cts_with_persistence` coalesces missing `details` to an empty dict before reading nested keys, so a single sparse row no longer takes the whole VM/CT check offline.
- **`system_startup` notification fires once per boot.** `_check_startup_aggregation` marks aggregation as done right after queuing the event, so the boot summary lands one time regardless of how many polling ticks fit inside the session.
---
## 🛠 Mobile & webhook
- **Mobile dashboard polling stays live on HTTPS + reverse proxy setups.** `pwa-register.tsx` auto-unregisters any Service Worker on load so mobile-browser background throttling stops interfering with the polling fetches, and PWA installability is now driven by the new in-app install prompt above.
- **Webhook auth trusts every host-local IP.** The internal webhook (`/api/notifications/webhook`) accepts requests from any interface IP the host owns (Tailscale, WireGuard, LAN, IPv6, plus IPv4-mapped-in-IPv6 form `::ffff:x.x.x.x` that Flask emits on dual-stack binds), so PVE Test buttons work through any of them.
---
## 🛡 Update flow — Monitor-terminal-aware for update and channel switch
- **The Monitor's WebSocket terminal now exposes `PROXMENUX_TERMINAL=monitor`** in the environment of every shell it opens, and every child inherits it. This gives `menu` (and any other flow that cares) a reliable, deterministic way to tell that the current session lives inside the Monitor process — a session that would be cut mid-install if the Monitor service was restarted.
- **`menu` update prompt** — when a new ProxMenux version is available and the session is running inside the Monitor terminal, the classic yes/no update prompt is replaced by an informational msgbox. The msgbox names the new version and shows the canonical one-liner (`bash -c "$(wget -qLO - …)"`) to run the update from SSH or the Proxmox host console. Because the flow has already decided the in-terminal update path is unsafe (see the [msgbox-ack rule](memory/feedback_whiptail_msgbox_ack.md)), there's a single OK button — no yes/no that could trigger the destructive update by accident.
- **Settings → Release Channel** — the same guard is applied in `config_menu.sh`'s `apply_release_channel()`. Selecting Stable ↔ Beta from the Monitor terminal shows an informational msgbox with the exact `wget` one-liner for the target channel (using the same URL the flow would have downloaded itself) and returns to the menu instead of running the installer in place.
- **After OK, both flows continue normally**. The user keeps using ProxMenux from the same terminal without restrictions; only the destructive step is routed elsewhere. There is no lockdown and no forced action.
- **SSH sessions, the Proxmox host console, and any environment where `PROXMENUX_TERMINAL` isn't `monitor` keep the previous behaviour** and can update or switch channels as always. The change only affects the case where doing it in place would break the running session.
- **Bootstrap note**: because `PROXMENUX_TERMINAL=monitor` is added by the AppImage this release ships, the guard only starts protecting sessions once the host is on 1.2.4 or newer. The very first update to 1.2.4, if triggered from the Monitor terminal, can still hit the old behaviour — from 1.2.4 forward the guard is in place.
---
## 🔧 Update flow — DKMS drivers rebuilt when a new kernel lands
- **After `apt full-upgrade` stages a kernel newer than the one currently running, `update-pve-safe.sh` now rebuilds ProxMenux-installed DKMS drivers against the new kernel.** The Update Now button in the Health Monitor and the `utilities/proxmox_update.sh` CLI both delegate to `update-pve-safe.sh`, so both routes gain the behaviour. The step reads `components_status.json`, cross-references the DKMS-managed components ProxMenux tracks (`nvidia_driver`, `coral_driver`), installs the matching kernel headers (`proxmox-headers-<newkver>` or `pve-headers-<newkver>`) if they aren't already present, and runs `dkms autoinstall -k <newkver>`. Then it verifies via `dkms status` that each expected module (`gasket` for Coral, `nvidia` for the NVIDIA driver) actually reached `installed` state for the new kernel — if any module didn't, it falls back to each installer's `--auto-reinstall` path.
- **A whiptail msgbox announces the rebuild before it runs.** Single OK button — no yes/no. Names the incoming kernel version and lists the DKMS components that are going to be rebuilt, so the user sees exactly what's about to happen. Because leaving DKMS drivers unbuilt would leave the system with a working kernel but non-functional TPU / GPU at boot, this is transparency, not a decision — pressing OK acknowledges the follow-up work and the flow proceeds. Non-interactive invocations (cron, headless batch, missing whiptail) skip the msgbox and log the same information.
- **Only components already registered as `installed` in `components_status.json` are considered.** A host with no ProxMenux-managed DKMS drivers sees no msgbox and no rebuild step. Hosts that never ran the Coral or NVIDIA installer are unaffected.
- **Failure to rebuild does not abort the update.** If a DKMS module can't be rebuilt against the new kernel (upstream API break, missing dependency), the update flow completes normally, the specific components that failed are named in the summary, and the user can re-run their installer manually after reboot. The step is best-effort by design — a kernel/driver mismatch is an upstream problem, not something the update flow should fail on.
- Shared helper `pmx_rebuild_dkms_after_kernel` lives in `scripts/global/utils-install-functions.sh`, so future updaters or CLI utilities can pick it up with a one-line call.
---
## 🔌 Post-install — Persistent NIC naming becomes idempotent
- **ProxMenux-owned `.link` files now carry a distinctive filename prefix and internal marker.** Files are written as `10-proxmenux-<iface>.link` and the first line of every file is `# Managed by ProxMenux — do not edit`. Both are checked by the reconciliation and uninstall paths before touching a file, so anything the user wrote by hand or that came from another package is safe.
- **Reruns of `setup_persistent_network` reconcile ProxMenux entries.** Every invocation walks the existing `10-proxmenux-*.link` files, extracts the `MACAddress=` value, compares it against the MACs currently present under `/sys/class/net/`, and removes only the ProxMenux-owned entries whose MAC is no longer there. Hardware replacements, NIC swaps and hardware migrations stop leaving orphan mappings behind on every rerun.
- **Legacy 1.0-format files (`10-<iface>.link` written by the previous revision) are migrated on the first run of the new function.** If the file matches the exact template the 1.0 code used to write (two sections, `MACAddress=` + `Name=`, nothing else), it's removed and replaced with the new `10-proxmenux-<iface>.link` in one step. Any file that doesn't match the template exactly is left alone.
- **The uninstall path (`uninstall_persistent_network`) now only removes files that carry both the `10-proxmenux-` filename prefix and the marker on the first line.** The previous `rm -f /etc/systemd/network/*.link` blanket sweep is gone — user-authored `.link` files stay in place regardless of their filename.
- **Single shared implementation.** The three duplicated `setup_persistent_network` bodies (`auto_post_install.sh`, `customizable_post_install.sh`, `network_menu.sh`) plus the uninstall path now all delegate to `pmx_setup_persistent_network` / `pmx_uninstall_persistent_network` in `scripts/global/utils-install-functions.sh`. Future fixes can't miss a copy.
- `FUNC_VERSION` bumped 1.0 → 1.1 on all three call sites so the ProxMenux update detector re-runs the function on hosts that already had the 1.0 build. That first re-run performs the legacy migration + reconciliation in one shot.
---
## 🧮 Post-install — ZFS ARC optimization narrowed to its scope
- **`optimize_zfs_arc` now sets only `zfs_arc_max`.** The function writes a single line to `/etc/modprobe.d/99-zfsarc.conf`: `options zfs zfs_arc_max=<cap>`. `zfs_arc_min` stays at the OpenZFS default (auto-calculated as the larger of 32 MiB and ~1/32 of RAM), and L2ARC (`l2arc_noprefetch`, `l2arc_write_max`) and TXG (`zfs_txg_timeout`) tunables — which are outside the scope of an ARC optimization — are left at their OpenZFS defaults unless the user configures them elsewhere.
- **The initramfs is now regenerated after writing the config.** On ZFS-on-root systems the ZFS module loads from the initramfs before the running system reads `/etc/modprobe.d/`, so a plain reboot wasn't enough for the new cap to take effect. `update-initramfs -u -k all` runs right after the file is written, plus `proxmox-boot-tool refresh` on systemd-boot hosts, so the value is picked up at the next boot instead of being shadowed by the initramfs's stale copy.
- **The function is guarded on the presence of a live ZFS pool** (`zpool list` check) so it becomes a no-op on hosts that don't use ZFS.
- **Cap values use clean binary sizes**: 512 MiB up to 16 GB RAM, 1 GiB up to 32 GB, RAM/8 above that — with a floor of 512 MiB so a bad memory reading never leaves an unusably small ARC.
- `FUNC_VERSION` bumped 1.0 → 1.1 so the ProxMenux update detector re-runs the function on hosts that already had the 1.0 build. Because the write is a full rewrite of `99-zfsarc.conf`, running the updated function once replaces the whole file cleanly. The uninstall path now also runs `update-initramfs` + `proxmox-boot-tool refresh` after restoring or removing the config, so the revert propagates to the initramfs the same way.
---
## 🔥 Post-install — Firewall bridge sysctl tuning hardened
- **The `rp_filter=0` and `log_martians=0` tuning for `fwbr*`, `fwln*`, `fwpr*` and `tap*` interfaces now also applies to interfaces Proxmox spins up when a VM starts, stops, reboots or migrates.** A new `/etc/udev/rules.d/99-proxmenux-fwbr-tune.rules` fires a helper on every `net`/`add` event matching those prefixes, so each fresh interface picks up the correct value immediately — no reboot and no rerun of the post-install needed.
- **The tuning logic is now in a standalone helper** at `/usr/local/sbin/proxmenux-fwbr-tune`, shared by the initial sweep (`proxmenux-fwbr-tune.service`, oneshot) and by the udev rule. An explicit invocation at install time ensures the current session sees the change without waiting for the next VM cycle.
- **The customizable post-install flow (`customizable_post_install.sh`) now installs the same helper + oneshot service + udev rule + initial sweep as the automatic flow**, so both variants leave the system in the same end state.
- Both `apply_network_optimizations` functions bumped `FUNC_VERSION` 1.0 → 1.1, so the ProxMenux update detector re-runs the function on hosts that already had the 1.0 build. The uninstall path (`uninstall_network_optimization`) is extended to remove the new helper and udev rule, and reload udev.
---
## 🧰 Post-install — Log2RAM + PBS
- **PBS API log rotation applied automatically when `proxmox-backup-server` runs as a service on the host.** Both Log2RAM installers (`install_log2ram_auto` and the customizable `configure_log2ram`) detect PBS via `dpkg-query` and drop `/etc/logrotate.d/proxmox-backup-api` with a 20MB × 3 rotation rule plus `/etc/cron.hourly/proxmox-backup-logrotate`. On a PVE host that also runs PBS as a service, `pvestatd`'s local-datastore poll writes to `/var/log/proxmox-backup/api/access.log` and `auth.log` every few seconds — the upstream PBS package ships no logrotate rule for those files, and this rule keeps them bounded so a tmpfs-backed `/var/log` stays comfortably under budget. No-op on hosts without PBS as a service.
- **Upstream `log2ram` script patched to `rsync -aXv --no-acls` right after `install.sh`.** Both installers rewrite the call in place with a `sed` guarded by `grep -q` (backup at `.proxmenux.bak`, no-op if a future upstream release already dropped `-A`). Extended attributes (`-X`) are preserved. Result: `log2ram write` finishes cleanly on `/var/log.hdd` filesystems that don't accept POSIX ACLs (ZFS with `acltype=off`, ext4 mounted without the `acl` option) — no more `set_acl: Operation not supported` / exit 23 messages.
- **Emergency block of `log2ram-check.sh` rotates PBS logs before truncating.** When `/var/log` crosses the 92% threshold, the auto-sync script now runs `logrotate -f /etc/logrotate.d/proxmox-backup-api` (only if the rule file exists) *before* truncating `pveproxy/access.log`, `pveproxy/error.log` and `pveam.log`. Recent PBS access/auth history is preserved in the rotated `.gz` files instead of being lost. Both `install_log2ram_auto` and `configure_log2ram` bumped `FUNC_VERSION` 1.2 → 1.3, and the embedded `log2ram-check.sh` header comment bumped v1.2 → v1.3.
## 🗄 Backup restore — pmxcfs snapshot + ZFS data pools
- **`/var/lib/pve-cluster/config.db` is now captured with `sqlite3 .backup`.** pmxcfs (`/etc/pve`) is served by `pve-cluster` from that SQLite store, so a plain rsync of the raw file with the service running can catch it mid-WAL checkpoint and land in the archive as an inconsistent copy. `hb_prepare_staging` now runs `sqlite3 /var/lib/pve-cluster/config.db ".backup '$staging/…/config.db'"` before the general rsync — the canonical way (documented by Proxmox) to snapshot the store consistently while `pve-cluster` keeps serving traffic, with zero downtime for the cluster. The general rsync of `/var/lib/pve-cluster` now excludes `config.db`, `config.db-wal` and `config.db-shm` so nothing overwrites the atomic dump. Hosts without `sqlite3` fall back to a raw copy named `config.db.raw-fallback`, which the recovery helper promotes to `config.db` before starting `pve-cluster`. Metadata records which path was used via `pmxcfs_config_db=sqlite_backup|raw_fallback` in `metadata/run_info.env` for trace. The restore path continues to use the canonical `systemctl stop pve-cluster → cp → systemctl start pve-cluster` pattern (`apply_pending_restore.sh` and the standalone recovery helper written next to every extracted cluster dir), so the DB the user brings back is now guaranteed consistent instead of a raw file copy of state in flight.
- **Separate ZFS data pools listed in the backup are now imported automatically at restore time.** The new `_rs_import_data_pools` step runs after config apply, walks `storage_inventory.zfs_pools[]`, skips the root pool (already mounted by the system), and issues `zpool import <name>` for every non-root pool whose disks are all present on this host. When ZFS rejects the import as *foreign* — the typical case after a fresh install regrabs the pool label with a new `hostid` — the step retries with `-f` and reports the pool as forced so the user has trace. Pools missing any disk are skipped with a clear warning rather than imported degraded. Together this closes the common case where `zfs-import-scan.service` failed at boot after a fresh install and left the separate data pool unavailable until `zpool import -f` was run manually.
- **The auto-import result persists to the post-restore progress card.** The step writes a `data_pools_import` section into `/var/lib/proxmenux/restore-state.json` (the same JSON the Backups-tab card polls) and a raw log at `/var/log/proxmenux/restore-datapools-<timestamp>.log`. The Backups tab card renders a dedicated block inside Details with five color-coded rows (Imported / Forced / Skipped partial / Skipped missing / Failed) so the summary stays consultable after the restore terminal is closed, and the entry is preserved in the run's history for later review.
- **ZFS pools created with `by-partuuid` or raw `/dev/sdX` are recognised by the disk-presence check.** The auto-import step and `validate_storage.sh` treat `devices_by_id` entries that start with `/` as absolute paths and only prepend `/dev/disk/by-id/` to bare basenames, so pools built against partition UUIDs or a raw block device are detected as present when their disks are on the host.
- **`/etc/systemd/network` added to the default backup paths.** That directory holds systemd `.link` files that pin NIC names to their MAC across kernel updates and reinstalls — `setup_persistent_network` in the post_install writes them for every physical interface, and users can drop their own to rename a NIC to something meaningful. Preserving them across a fresh-install restore keeps the source host's NIC naming policy intact on the target, so `/etc/network/interfaces` entries that reference custom NIC names continue to resolve after the restore.
---
## 🙏 Acknowledgments
- **@pepenai** — mobile dashboard on HTTPS + reverse proxy.
- **Pepo** — webhook auth from a Tailscale FQDN.
- **@ash34** (#255) — VM/CT check with a NULL `details` row.
- **@f3rs3n** (#256, #257, #258) — firewall bridge sysctl tuning, ZFS ARC optimization scope, and persistent NIC naming reconciliation.
- **Juan C.** — ZFS data pool auto-import after a fresh install.
- **David Barbero (@sikete)** — DKMS driver rebuild on kernel upgrade.
## 2026-07-14
### New version ProxMenux v1.2.3
@@ -17,7 +148,7 @@ Stable consolidation of the **v1.2.2.x beta cycle** (v1.2.2.1 → v1.2.2.2 → v
- **PBS encryption with recovery blob**: encrypted backups store a passphrase-wrapped copy of the keyfile as a `-keyrecovery` group next to each backup, so a new Proxmox install can always get the key back with the user's passphrase.
- **Direction-aware restore**: reapplies IOMMU / VFIO / GRUB tunings on cross-kernel jumps, protects critical packages from cascade-remove, auto-remaps NICs after a motherboard swap.
- **Live post-restore progress card**: after the reboot, the Backups tab shows a real-time card with step-by-step milestones, per-component status (NVIDIA, Intel GPU tools, Coral, AMD tools) and a log tail with an Issues-only filter. Past restores are archived and browsable.
- **PBS keyfile management inline in the Monitor**: each PBS destination row exposes Download / Upload / Delete for the keyfile plus a Yes/No + passphrase + contextual Apply toggle for the escrow. When the installed keyfile does not match the backup's manifest, View contents / Download / Restore now show a structured amber panel with the required fingerprint so the operator knows which keyfile to import.
- **PBS keyfile management inline in the Monitor**: each PBS destination row exposes Download / Upload / Delete for the keyfile plus a Yes/No + passphrase + contextual Apply toggle for the escrow. When the installed keyfile does not match the backup's manifest, View contents / Download / Restore now show a structured amber panel with the required fingerprint so the user knows which keyfile to import.
---
@@ -61,7 +192,7 @@ For users who do **not** use an AI enhancement agent, the templated body now add
- **USB-NVMe / USB-SATA SMART on `removable=0` enclosures** — enclosures reporting `removable=0` (ASMedia, JMicron, Realtek, ASM105x) now walk sysfs to detect USB attachment, so `-d snt*` pass-through is tried and the drive's real model, serial, temperature, power-on hours and health surface. Temperature history sampler picks up the same fix.
- **PBS encryption prompt reworked** to a single explicit *Encrypt this backup?* Yes/No — nothing is uploaded to PBS unless the answer is Yes. Only when a keyfile is not yet installed does a second dialog ask whether to generate a new one or import an existing one. Cancelling never leaves a phantom keyfile behind.
- **Attached scheduled backups now inherit retention on every run** — jobs attached to a PVE vzdump parent re-read the parent's `prune-backups` config at each run and rewrite `KEEP_*` accordingly. Previously frozen to the value at job creation time.
- **Installer no longer auto-relaunches `menu` after an update** — the `exec MENU_SCRIPT` at the tail of the update path triggered *"line: syntax"* errors when bash tried to read the just-rewritten `/usr/local/bin/menu` under its feet. Flow now exits cleanly; operator types `menu` when ready. `change_release_channel` in Settings unaffected.
- **Installer no longer auto-relaunches `menu` after an update** — the `exec MENU_SCRIPT` at the tail of the update path triggered *"line: syntax"* errors when bash tried to read the just-rewritten `/usr/local/bin/menu` under its feet. Flow now exits cleanly; user types `menu` when ready. `change_release_channel` in Settings unaffected.
- **PBS restore listing broken on Proxmox 9 / jq 1.7**`hb_pbs_list_snapshots` switched from the prefix form `and not (...)` (rejected by jq 1.7) to the postfix form `and ((...) | not)` (accepted by both jq 1.6 and 1.7). Silent stderr redirect removed so future parse errors surface.
- **`run_scheduled_backup.sh` no longer crashes when `LANGUAGE` is unset** — cron / systemd invocations now load language + initialize the translation cache before sourcing utility functions that require it.
- **Local archive restore prompts no longer freeze silently**`hb_prompt_restore_source_dir` and `hb_prompt_local_archive` use the fd-9 TTY handoff already applied elsewhere.
@@ -108,7 +239,7 @@ Special thanks to the community members who shaped this release with concrete de
- **[@JF_Car](https://github.com/JF_Car)** — proposed the tree layout for the new Network Flow diagram so it reads correctly on mobile devices.
- **[@ghosthvj](https://github.com/ghosthvj)** — contributed the design for the new **Physical Disks** and **Physical Interfaces** cards.
- **[@riglesias](https://github.com/riglesias)**, **[@princo56](https://github.com/princo56)** and **[@jonatanc](https://github.com/jonatanc)** — tested the beta cycle end-to-end and provided the suggestions that closed most of the operator-visible gaps.
- **[@riglesias](https://github.com/riglesias)**, **[@princo56](https://github.com/princo56)** and **[@jonatanc](https://github.com/jonatanc)** — tested the beta cycle end-to-end and provided the suggestions that closed most of the user-visible gaps.
And to every user who opened an issue, commented in [GitHub Discussions](https://github.com/MacRimi/ProxMenux/discussions), reported a bug on the community channel, or told us what worked and what didn't on their hardware — most internal fixes in this release started as one of those reports. Keep them coming.
@@ -118,13 +249,13 @@ And to every user who opened an issue, commented in [GitHub Discussions](https:/
### New version ProxMenux v1.2.2 — *Stable consolidation of the v1.2.1.x cycle*
Stable release that brings the four prereleases of the **v1.2.1.x** cycle to the main channel in one move. The work over those four betas centred on three themes: making the Health Monitor genuinely configurable instead of just observable (per-category thresholds, per-event dismiss durations, an audit log of active suppressions), expanding the notification stack to cover roughly 80 services through Apprise while persisting events across Quiet Hours, and turning the Monitor process itself into a quieter, more predictable system citizen on idle hosts. On top of those, this release lands automatic upgrade detection for LXC containers, an end-to-end rewrite of the Coral TPU installer with the latest upstream drivers, and a long list of operator-visible fixes — HTTPS terminal handshake, kernel-update detection on PVE 9.x, NVIDIA installer flow on Alpine LXC, mixed-GPU passthrough audio companion handling, and several runtime optimizations on the Monitor scanning loops. Five direct code contributions from the community ship alongside ([@jcastro](https://github.com/jcastro) ×5, [@pespinel](https://github.com/pespinel) ×1) and the GPU passthrough work was driven by [@ghosthvj](https://github.com/ghosthvj)'s detailed field reports — see the Acknowledgments at the end.
Stable release that brings the four prereleases of the **v1.2.1.x** cycle to the main channel in one move. The work over those four betas centred on three themes: making the Health Monitor genuinely configurable instead of just observable (per-category thresholds, per-event dismiss durations, an audit log of active suppressions), expanding the notification stack to cover roughly 80 services through Apprise while persisting events across Quiet Hours, and turning the Monitor process itself into a quieter, more predictable system citizen on idle hosts. On top of those, this release lands automatic upgrade detection for LXC containers, an end-to-end rewrite of the Coral TPU installer with the latest upstream drivers, and a long list of user-visible fixes — HTTPS terminal handshake, kernel-update detection on PVE 9.x, NVIDIA installer flow on Alpine LXC, mixed-GPU passthrough audio companion handling, and several runtime optimizations on the Monitor scanning loops. Five direct code contributions from the community ship alongside ([@jcastro](https://github.com/jcastro) ×5, [@pespinel](https://github.com/pespinel) ×1) and the GPU passthrough work was driven by [@ghosthvj](https://github.com/ghosthvj)'s detailed field reports — see the Acknowledgments at the end.
---
## 🩺 Health Monitor — Configurable, Granular, Auditable
Three coupled pieces that together let the operator tune the Health Monitor to the actual envelope of their host instead of working around its defaults, and to manage dismisses with the same fine-grained control they already have over the rest of the dashboard.
Three coupled pieces that together let the user tune the Health Monitor to the actual envelope of their host instead of working around its defaults, and to manage dismisses with the same fine-grained control they already have over the rest of the dashboard.
### Per-category Warning / Critical thresholds
@@ -144,7 +275,7 @@ The *Dismiss* button on each Health Monitor alert now opens a small dropdown wit
- **7 days** — handy for a temporary condition you don't want to hear about during a week-long migration
- **Permanently** — silences this specific `error_key` indefinitely
Permanent dismisses persist with `suppression_hours = -1` in the persistence DB, never re-emit, never re-notify and are marked with a distinct amber **Permanent** badge in the Health Monitor so the operator always knows which alerts are intentionally silenced. The backend infrastructure for the permanent sentinel already existed — the UI just lacked a way to set it. The API contract is small and backwards-compatible: `POST /api/health/acknowledge` accepts an optional `suppression_hours` body field (positive integer for hours, `-1` for permanent); omitting it preserves the previous behaviour and uses the category's configured suppression. A second new endpoint `POST /api/health/un-acknowledge {error_key}` clears a previously-recorded acknowledgment so the alert becomes eligible to fire again — used by the Active Suppressions panel below.
Permanent dismisses persist with `suppression_hours = -1` in the persistence DB, never re-emit, never re-notify and are marked with a distinct amber **Permanent** badge in the Health Monitor so the user always knows which alerts are intentionally silenced. The backend infrastructure for the permanent sentinel already existed — the UI just lacked a way to set it. The API contract is small and backwards-compatible: `POST /api/health/acknowledge` accepts an optional `suppression_hours` body field (positive integer for hours, `-1` for permanent); omitting it preserves the previous behaviour and uses the category's configured suppression. A second new endpoint `POST /api/health/un-acknowledge {error_key}` clears a previously-recorded acknowledgment so the alert becomes eligible to fire again — used by the Active Suppressions panel below.
### Active Suppressions panel in Settings
@@ -172,7 +303,7 @@ Three reliability fixes ship alongside, all surfaced after the initial beta roll
2. **Backend whitelist regression** that rejected Apprise with HTTP 400. The notifications-test validator's hard-coded channel set (`{telegram, gotify, discord, email, all}`) was missing `apprise`, so every Apprise test or send returned `400 Invalid channel` before the library was even invoked. The whitelist is now derived live from `notification_channels.CHANNEL_TYPES`, so adding a new channel implementation in the future cannot silently regress this validator again.
3. **Opaque error reporting** when the destination returned a non-2xx response. When a destination (`jsons://`, `ntfy://`, `slack://`, …) rejected the payload, the operator only saw a generic *"Apprise rejected the notification (transport failure)"* message. The channel now captures Apprise's internal logger during `notify()` and surfaces the real HTTP status code plus the destination's response body (capped at 300 chars) — so a beta tester debugging a custom webhook can immediately see whether the upstream server is rejecting their payload schema.
3. **Opaque error reporting** when the destination returned a non-2xx response. When a destination (`jsons://`, `ntfy://`, `slack://`, …) rejected the payload, the user only saw a generic *"Apprise rejected the notification (transport failure)"* message. The channel now captures Apprise's internal logger during `notify()` and surfaces the real HTTP status code plus the destination's response body (capped at 300 chars) — so a beta tester debugging a custom webhook can immediately see whether the upstream server is rejecting their payload schema.
---
@@ -209,7 +340,7 @@ The mount monitor used to call `lxc-info -n <vmid> -p` for every running CT just
## 🔌 HTTPS Terminal Handshake
Every terminal modal in the Monitor (dashboard terminal, LXC terminal, script terminal) used to fail with *WebSocket connection error* on hosts where HTTPS was enabled. The root cause was specific to the `gevent + SSL` path: the gevent-websocket `WebSocketHandler` was stacked on top of flask-sock's protocol implementation, so the server emitted **two** consecutive `HTTP/1.1 101 Switching Protocols` headers and the browser closed the connection as a corrupt frame. Dropping the explicit `handler_class=WebSocketHandler` argument restores a single 101 response and the handshake completes normally. The fix is invisible to operators running on plain HTTP — they were unaffected — but unblocks every HTTPS-fronted install (reverse proxies, certificate-managed deployments, anything behind nginx/Traefik).
Every terminal modal in the Monitor (dashboard terminal, LXC terminal, script terminal) used to fail with *WebSocket connection error* on hosts where HTTPS was enabled. The root cause was specific to the `gevent + SSL` path: the gevent-websocket `WebSocketHandler` was stacked on top of flask-sock's protocol implementation, so the server emitted **two** consecutive `HTTP/1.1 101 Switching Protocols` headers and the browser closed the connection as a corrupt frame. Dropping the explicit `handler_class=WebSocketHandler` argument restores a single 101 response and the handshake completes normally. The fix is invisible to users running on plain HTTP — they were unaffected — but unblocks every HTTPS-fronted install (reverse proxies, certificate-managed deployments, anything behind nginx/Traefik).
Additionally, the terminal panel used to lose its WebSocket connection when the user enabled the browser's auto-translate feature (Chrome / Edge / Safari "translate this page" prompts). The translator moves DOM nodes that React still holds refs to, and the WebSocket React component breaks because its container ref points to a moved node. Added `translate="no"` on the terminal container divs so the translator skips the embedded tty entirely — translations on the rest of the page still work.
@@ -223,7 +354,7 @@ On Proxmox VE 9.x hosts, the *System Updates → Kernel / PVE* row used to repor
2. **Dry-run switched from `apt-get upgrade --dry-run` to `apt-get dist-upgrade --dry-run`**. PVE 9 ships kernel updates packaged as new installs (not as straight upgrades of an existing package), and the plain `upgrade --dry-run` does not consider new installs at all. `dist-upgrade --dry-run` does.
3. **Running-kernel detection** now reads `uname -r` and flags an update as a *running-kernel update* when the package matches the running release exactly or its branch meta-package (e.g. `proxmox-kernel-6.14` for a host on `6.14.11-4-pve`). The row text distinguishes *"Running kernel update available (reboot required)"* from *"N kernel update(s) available (none for running kernel)"* so the operator knows whether they need to reboot or just install.
3. **Running-kernel detection** now reads `uname -r` and flags an update as a *running-kernel update* when the package matches the running release exactly or its branch meta-package (e.g. `proxmox-kernel-6.14` for a host on `6.14.11-4-pve`). The row text distinguishes *"Running kernel update available (reboot required)"* from *"N kernel update(s) available (none for running kernel)"* so the user knows whether they need to reboot or just install.
---
@@ -253,9 +384,9 @@ New documentation pages cover the **Active Suppressions** section in the Setting
- **Post-install function update detection** — the Monitor tracks installed ProxMenux optimizations (Log2Ram, Memory Settings, System Limits, Logrotate, …) and notifies when a newer version is available, with one-click apply from Settings.
- **Secure Gateway (Tailscale) update flow** — one-click Tailscale update from Settings with Last-checked / Installed / Latest indicators and notification when a new version is published.
- **Helper-Scripts menu** — richer context and useful information for each entry, making it easier to know what every script does before running it.
- **Burst aggregation wording** — burst summaries now report only the *additional* events that arrived after the initial individual alert, so the operator no longer sees the first event counted twice.
- **Burst aggregation wording** — burst summaries now report only the *additional* events that arrived after the initial individual alert, so the user no longer sees the first event counted twice.
- **Known-error classifier** — word-boundary regex on ATA / UNC patterns so kernel messages like `nvidia_uvm:FatalError` are no longer misclassified as ATA cable issues.
- **VM / CT control errors** — failed start / stop / restart now surfaces the real `pvesh` stderr (e.g. *"no space left on device"*) in the UI toast and fires a `vm_fail` / `ct_fail` notification, instead of the bare 500 INTERNAL SERVER ERROR the operator used to see.
- **VM / CT control errors** — failed start / stop / restart now surfaces the real `pvesh` stderr (e.g. *"no space left on device"*) in the UI toast and fires a `vm_fail` / `ct_fail` notification, instead of the bare 500 INTERNAL SERVER ERROR the user used to see.
- **log2ram apply path** — the auto / update flow now restarts log2ram after writing the new size, so a configured `512M` actually takes effect on the running tmpfs without a manual restart.
- **PVE webhook URL** — the notification webhook now follows the active SSL state automatically, switching between `http://` and `https://` when you toggle HTTPS in the panel.
- **Frontend 401 cascade** — the login screen no longer swallows a 401 forever after a brief stale-token state; the dedup flag is cleared on mount and on successful login.
+1 -1
View File
@@ -485,7 +485,7 @@ extract_appimage_to_runtime_dir() {
rm -f "$appimage_path"
msg_ok "AppImage runtime extracted (no FUSE mount; bypasses Wazuh rule 521)."
msg_ok "AppImage runtime extracted."
return 0
}
+1 -1
View File
@@ -372,7 +372,7 @@ extract_appimage_to_runtime_dir() {
rm -f "$appimage_path"
msg_ok "AppImage runtime extracted (no FUSE mount; bypasses Wazuh rule 521)."
msg_ok "AppImage runtime extracted."
return 0
}
+45 -12
View File
@@ -43,6 +43,8 @@
"A host reboot is required before starting the VM. Reboot now?": "Vor dem Starten der VM ist ein Host-Neustart erforderlich. Jetzt neu starten?",
"A job with this ID already exists.": "Ein Job mit dieser ID existiert bereits.",
"A keyfile is installed at:": "Eine Schlüsseldatei ist installiert unter:",
"A new ProxMenux version is available:": "Eine neue ProxMenux-Version ist verfügbar:",
"A new kernel is staged for the next boot:": "Ein neuer Kernel wird für den nächsten Start bereitgestellt:",
"A newer version is available:": "Eine neuere Version ist verfügbar:",
"A reboot is required after installation to load the new kernel modules.": "Nach der Installation ist ein Neustart erforderlich, um die neuen Kernelmodule zu laden.",
"A reboot is required for VFIO binding to take effect. Do you want to restart now?": "Damit die VFIO-Bindung wirksam wird, ist ein Neustart erforderlich. Möchten Sie jetzt neu starten?",
@@ -287,6 +289,7 @@
"Authorized": "Autorisiert",
"Auto-detected firewall backend (nftables/iptables)": "Automatisch erkanntes Firewall-Backend (nftables/iptables)",
"Auto-discover servers on network": "Automatische Erkennung von Servern im Netzwerk",
"Auto-importing ZFS data pools from backup...": "ZFS-Datenpools automatisch aus Backup importieren ...",
"Auto-negotiate:": "Automatische Aushandlung:",
"Auto-start was skipped because GPU passthrough setup was requested.": "Der automatische Start wurde übersprungen, da die GPU-Passthrough-Einrichtung angefordert wurde.",
"Auto-sync enabled when /var/log exceeds 80% of": "Automatische Synchronisierung aktiviert, wenn /var/log 80 % überschreitet",
@@ -480,7 +483,6 @@
"Change Language": "Sprache ändern",
"Change Release Channel": "Veröffentlichungskanal ändern",
"Changes applied. A system reboot is recommended for them to take full effect.": "Änderungen übernommen. Damit sie ihre volle Wirkung entfalten, wird ein Neustart des Systems empfohlen.",
"Changes detected. Updating ZFS ARC configuration...": "Änderungen erkannt. ZFS ARC-Konfiguration wird aktualisiert...",
"Changes have been applied to the configuration file.": "Es wurden Änderungen an der Konfigurationsdatei vorgenommen.",
"Changes will apply after reboot.": "Änderungen werden nach dem Neustart wirksam.",
"Changing Release Channel": "Veröffentlichungskanal ändern",
@@ -515,7 +517,6 @@
"Checking ZFS autotrim configuration...": "ZFS-Autotrim-Konfiguration wird überprüft...",
"Checking and repairing old LVM PV headers (if needed)...": "Überprüfung und Reparatur alter LVM-PV-Header (falls erforderlich) ...",
"Checking conflicting drivers blacklist...": "Blacklist mit in Konflikt stehenden Treibern wird überprüft...",
"Checking existing ZFS ARC configuration...": "Überprüfen der vorhandenen ZFS ARC-Konfiguration ...",
"Checking for updates...": "Suche nach Updates...",
"Checking free space in /var/cache/apt/archives...": "Überprüfen Sie den freien Speicherplatz in /var/cache/apt/archives...",
"Checking if system disk is SSD or M.2...": "Überprüfen, ob es sich bei der Systemfestplatte um eine SSD oder M.2 handelt ...",
@@ -972,7 +973,6 @@
"Creating export archive...": "Exportarchiv wird erstellt...",
"Creating local archive...": "Lokales Archiv erstellen...",
"Creating mount point...": "Mountpunkt wird erstellt...",
"Creating new ZFS ARC configuration...": "Neue ZFS ARC-Konfiguration erstellen...",
"Creating partition table and partition...": "Partitionstabelle und Partition erstellen...",
"Creating partition...": "Partition erstellen...",
"Creating pigz wrapper script...": "Pigz-Wrapper-Skript wird erstellt...",
@@ -1050,6 +1050,9 @@
"DKMS add failed. Check": "DKMS-Hinzufügen fehlgeschlagen. Überprüfen",
"DKMS build failed.": "DKMS-Build ist fehlgeschlagen.",
"DKMS build failed. Last lines of make.log:": "DKMS-Build ist fehlgeschlagen. Letzte Zeilen von make.log:",
"DKMS driver rebuild": "Neuerstellung des DKMS-Treibers",
"DKMS drivers rebuilt for kernel": "DKMS-Treiber für Kernel neu erstellt",
"DKMS drivers reinstalled for kernel": "DKMS-Treiber für Kernel neu installiert",
"DKMS install failed.": "Die DKMS-Installation ist fehlgeschlagen.",
"DKMS module registered.": "DKMS-Modul registriert.",
"DNS Resolution": "DNS-Auflösung",
@@ -1603,10 +1606,8 @@
"Failed to create partition table on disk": "Die Partitionstabelle auf der Festplatte konnte nicht erstellt werden",
"Failed to create partition.": "Partition konnte nicht erstellt werden.",
"Failed to create temporary directory:": "Temporäres Verzeichnis konnte nicht erstellt werden:",
"Failed to create/update ZFS ARC configuration file": "Die ZFS ARC-Konfigurationsdatei konnte nicht erstellt/aktualisiert werden",
"Failed to destroy LXC:": "LXC konnte nicht zerstört werden:",
"Failed to destroy VM:": "VM konnte nicht zerstört werden:",
"Failed to detect RAM size. Using default value of 16GB for ZFS ARC optimization.": "Die RAM-Größe konnte nicht erkannt werden. Verwendung des Standardwerts von 16 GB für die ZFS ARC-Optimierung.",
"Failed to detect installed NVIDIA driver version.": "Die installierte NVIDIA-Treiberversion konnte nicht erkannt werden.",
"Failed to detect partition on disk": "Die Partition auf der Festplatte konnte nicht erkannt werden",
"Failed to determine timezone from IP address - keeping current timezone settings": "Die Zeitzone konnte nicht anhand der IP-Adresse ermittelt werden. Die aktuellen Zeitzoneneinstellungen werden beibehalten",
@@ -1697,12 +1698,14 @@
"Failed to update amdgpu_top": "Aktualisierung von amdgpu_top fehlgeschlagen",
"Failed to update auth.json — restoring backup.": "Aktualisierung von auth.json fehlgeschlagen Sicherung wird wiederhergestellt.",
"Failed to update cluster certificates (might not be in a cluster)": "Cluster-Zertifikate konnten nicht aktualisiert werden (möglicherweise nicht in einem Cluster)",
"Failed to update initramfs.": "Initramfs konnte nicht aktualisiert werden.",
"Failed to update intel-gpu-tools": "Aktualisierung der Intel-GPU-Tools fehlgeschlagen",
"Failed to update package list.": "Paketliste konnte nicht aktualisiert werden.",
"Failed to update package lists": "Paketlisten konnten nicht aktualisiert werden",
"Failed to write": "Schreiben fehlgeschlagen",
"Failed transitions from D3cold to D0": "Fehlgeschlagene Übergänge von D3cold zu D0",
"Failed. See log:": "Fehlgeschlagen. Siehe Protokoll:",
"Falling back to each installer with --auto-reinstall...": "Zurückgreifen auf jedes Installationsprogramm mit --auto-reinstall...",
"Falling back to manual paste mode.": "Zurück zum manuellen Einfügemodus.",
"Fastfetch Logo Selection": "Auswahl des Fastfetch-Logos",
"Fastfetch configuration updated": "Fastfetch-Konfiguration aktualisiert",
@@ -2081,6 +2084,8 @@
"Import failed for:": "Der Import ist fehlgeschlagen für:",
"Important: both VMs cannot be running at the same time with the same GPU.": "Wichtig: Beide VMs können nicht gleichzeitig mit derselben GPU ausgeführt werden.",
"Important: some GPUs may still fail in passthrough and can affect host stability or overall performance depending on hardware/firmware quality.": "Wichtig: Einige GPUs können beim Passthrough immer noch ausfallen und je nach Hardware-/Firmware-Qualität die Hoststabilität oder die Gesamtleistung beeinträchtigen.",
"Imported (foreign hostid, forced):": "Importiert (ausländische Host-ID, erzwungen):",
"Imported:": "Importiert:",
"Importing": "Importieren",
"Importing disk": "Datenträger importieren",
"Importing:": "Importieren:",
@@ -2216,6 +2221,7 @@
"Installing host drivers while the GPU is assigned to a VM could break passthrough and destabilize the system.": "Die Installation von Hosttreibern, während die GPU einer VM zugewiesen ist, könnte den Passthrough unterbrechen und das System destabilisieren.",
"Installing iSCSI initiator tools...": "iSCSI-Initiator-Tools werden installiert...",
"Installing intel-gpu-tools...": "Intel-GPU-Tools werden installiert...",
"Installing kernel headers:": "Kernel-Header installieren:",
"Installing kexec-tools...": "Kexec-Tools werden installiert...",
"Installing latest Lynis security scan tool...": "Das neueste Lynis-Sicherheitsscantool wird installiert ...",
"Installing packages...": "Pakete werden installiert...",
@@ -2307,6 +2313,8 @@
"Kept sharedfiles group (has regular users assigned).": "Sharedfiles-Gruppe beibehalten (mit regulären Benutzern).",
"Kernel and architecture info": "Kernel- und Architekturinformationen",
"Kernel headers and build tools verified.": "Kernel-Header und Build-Tools überprüft.",
"Kernel headers install failed — DKMS rebuild will likely fail:": "Installation der Kernel-Header fehlgeschlagen DKMS-Neuaufbau wird wahrscheinlich fehlschlagen:",
"Kernel headers installed": "Kernel-Header installiert",
"Kernel max Key limit configured": "Maximales Kernel-Schlüssellimit konfiguriert",
"Kernel panic behavior configuration completed": "Die Konfiguration des Kernel-Panic-Verhaltens ist abgeschlossen",
"Kernel panic configuration removed": "Kernel-Panic-Konfiguration entfernt",
@@ -2484,6 +2492,7 @@
"Method:": "Verfahren:",
"Migrate VMs away from node being upgraded": "Migrieren Sie VMs vom zu aktualisierenden Knoten weg",
"Migrate away any guests that must keep running": "Migrieren Sie alle Gäste weg, die weiter ausgeführt werden müssen",
"Migrated": "Migriert",
"Migrated legacy ProxMenux NVIDIA blacklist state — module will reload after reboot": "Migrierter Legacy-ProxMenux-NVIDIA-Blacklist-Status Modul wird nach dem Neustart neu geladen",
"Mirror URL not available for this script.": "Die Spiegel-URL ist für dieses Skript nicht verfügbar.",
"Missing": "Fehlen",
@@ -2730,6 +2739,7 @@
"New Virtual Machine": "Neue virtuelle Maschine",
"New backup job": "Neuer Sicherungsauftrag",
"New backups on this host will be unencrypted until a new keyfile is set up.": "Neue Backups auf diesem Host werden unverschlüsselt sein, bis eine neue Schlüsseldatei eingerichtet wird.",
"New kernel staged; rebuilding DKMS drivers:": "Neuer Kernel bereitgestellt;DKMS-Treiber neu erstellen:",
"New mount options to apply:": "Neue Mount-Optionen zur Anwendung:",
"New scheduled job (own timer + retention)": "Neuer geplanter Job (eigener Timer + Aufbewahrung)",
"New version available": "Neue Version verfügbar",
@@ -2738,7 +2748,6 @@
"Next Steps:": "Nächste Schritte:",
"Next step: stop that VM first, then run": "Nächster Schritt: Stoppen Sie zuerst die VM und führen Sie sie dann aus",
"No": "NEIN",
"No .link files found in": "Es wurden keine .link-Dateien gefunden",
"No .ova or .ovf files found in:": "Keine .ova- oder .ovf-Dateien gefunden in:",
"No .ovf descriptor found inside OVA.": "In OVA wurde kein .ovf-Deskriptor gefunden.",
"No .pxar archives were found in this backup:": "In diesem Backup wurden keine .pxar-Archive gefunden:",
@@ -2802,6 +2811,7 @@
"No PVs with old headers found.": "Keine PVs mit alten Headern gefunden.",
"No ProxMenux ZFS autotrim state file found.": "Keine ProxMenux ZFS-Autotrim-Statusdatei gefunden.",
"No ProxMenux host-backup archives were found in:": "Es wurden keine ProxMenux-Host-Backup-Archive gefunden in:",
"No ProxMenux-managed .link files found — nothing to remove.": "Keine von ProxMenux verwalteten .link-Dateien gefunden nichts zum Entfernen.",
"No Recent Servers": "Keine aktuellen Server",
"No Samba mounts found.": "Keine Samba-Mounts gefunden.",
"No Samba ports found": "Keine Samba-Ports gefunden",
@@ -2828,6 +2838,7 @@
"No VirtIO ISO found. Please download one.": "Keine VirtIO-ISO gefunden. Bitte laden Sie eines herunter.",
"No VirtIO ISO selected. Please choose again.": "Kein VirtIO ISO ausgewählt. Bitte wählen Sie erneut.",
"No Virtual Machines found on this system.": "Auf diesem System wurden keine virtuellen Maschinen gefunden.",
"No ZFS pools detected. Skipping ZFS ARC optimization.": "Keine ZFS-Pools erkannt.Überspringen der ZFS ARC-Optimierung.",
"No ZFS pools detected. Skipping ZFS autotrim.": "Keine ZFS-Pools erkannt. ZFS-Autotrim wird übersprungen.",
"No accessible": "Nicht zugänglich",
"No accessible NFS servers found.": "Es wurden keine zugänglichen NFS-Server gefunden.",
@@ -3056,7 +3067,7 @@
"Optimize ZFS ARC size": "ZFS ARC-Größe optimieren",
"Optimize journald": "Journal optimieren",
"Optimize logrotate": "Logrotate optimieren",
"Optimizing ZFS ARC size according to available memory...": "Optimieren der ZFS ARC-Größe entsprechend dem verfügbaren Speicher ...",
"Optimizing ZFS ARC maximum size...": "Maximale ZFS ARC-Größe optimieren ...",
"Optimizing logrotate configuration...": "Optimierung der Logrotate-Konfiguration...",
"Optimizing memory settings...": "Speichereinstellungen optimieren...",
"Optimizing network settings...": "Netzwerkeinstellungen optimieren...",
@@ -3082,6 +3093,7 @@
"Owner:": "Eigentümer:",
"Ownership set to root:sharedfiles with 2775 on:": "Eigentümerschaft auf root:sharedfiles mit 2775 gesetzt auf:",
"PAM limits configured": "PAM-Grenzwerte konfiguriert",
"PBS API log rotation configured (hourly, size-based)": "PBS-API-Protokollrotation konfiguriert (stündlich, größenbasiert)",
"PBS backup error log": "PBS-Backup-Fehlerprotokoll",
"PBS backup failed.": "PBS-Sicherung fehlgeschlagen.",
"PBS encryption keyfile (show / replace / remove)": "PBS-Verschlüsselungsschlüsseldatei (anzeigen/ersetzen/entfernen)",
@@ -3299,6 +3311,7 @@
"ProxMenux logo applied": "ProxMenux-Logo angewendet",
"ProxMenux only acts as a launcher — once the script starts, control leaves ProxMenux.": "ProxMenux fungiert nur als Startprogramm sobald das Skript gestartet wird, verlässt ProxMenux die Steuerung.",
"ProxMenux saved it locally at:": "ProxMenux hat es lokal gespeichert unter:",
"ProxMenux-managed .link file(s). User-authored .link files were left in place.": "Von ProxMenux verwaltete .link-Datei(en).Vom Benutzer erstellte .link-Dateien wurden an Ort und Stelle belassen.",
"Proxmology logo applied": "Proxmology-Logo angewendet",
"Proxmox 9 system update allready": "Proxmox 9 Systemupdate bereits",
"Proxmox APT repositories configured": "Proxmox APT-Repositorys konfiguriert",
@@ -3313,6 +3326,7 @@
"Proxmox VE 9.x no-subscription repository created": "Proxmox VE 9.x No-Subscription-Repository erstellt",
"Proxmox VE Helper Scripts": "Proxmox VE-Hilfsskripte",
"Proxmox VE configuration completed.": "Proxmox VE-Konfiguration abgeschlossen.",
"Proxmox VE safe update completed": "Sicheres Proxmox VE-Update abgeschlossen",
"Proxmox auth logger service created and started": "Proxmox-Auth-Logger-Dienst erstellt und gestartet",
"Proxmox enterprise repo might be missing or inaccessible. Trying to switch to no-subscription...": "Das Proxmox-Enterprise-Repository fehlt möglicherweise oder ist nicht zugänglich. Ich versuche, auf kein Abonnement umzustellen...",
"Proxmox filter configured": "Proxmox-Filter konfiguriert",
@@ -3326,7 +3340,6 @@
"Proxmox storages:": "Proxmox-Speicher:",
"Proxmox system repair completed successfully!": "Proxmox-Systemreparatur erfolgreich abgeschlossen!",
"Proxmox system repair completed with some issues.": "Die Reparatur des Proxmox-Systems wurde mit einigen Problemen abgeschlossen.",
"Proxmox system update": "Proxmox-Systemupdate",
"Proxmox web interface protection": "Schutz der Proxmox-Weboberfläche",
"Proxmox web interface: Datacenter > Storage > Add > Directory": "Proxmox-Weboberfläche: Datencenter > Speicher > Hinzufügen > Verzeichnis",
"Proxmox web interface: Datacenter > Storage > Add > NFS": "Proxmox-Weboberfläche: Datencenter > Speicher > Hinzufügen > NFS",
@@ -3396,6 +3409,7 @@
"Recommended: schedule these paths for next boot to avoid immediate SSH disconnection.": "Empfohlen: Planen Sie diese Pfade für den nächsten Start ein, um eine sofortige SSH-Trennung zu vermeiden.",
"Recommended: use GPU -> LXC mode for these devices.": "Empfohlen: Verwenden Sie für diese Geräte den GPU->LXC-Modus.",
"Recommended: use GPU with LXC workloads instead of VM passthrough on this hardware.": "Empfohlen: Verwenden Sie auf dieser Hardware eine GPU mit LXC-Workloads anstelle von VM-Passthrough.",
"Reconciled": "Versöhnt",
"Recover the keyfile using your recovery passphrase?": "Die Schlüsseldatei mit Ihrer Wiederherstellungspassphrase wiederherstellen?",
"Recoverable:": "Wiederherstellbar:",
"Recovery blob upload failed — main backup is OK, but keyfile recovery from PBS will not be available for this backup.": "Hochladen des Wiederherstellungs-Blobs fehlgeschlagen Hauptsicherung ist in Ordnung, aber die Wiederherstellung der Schlüsseldatei von PBS ist für diese Sicherung nicht verfügbar.",
@@ -3420,6 +3434,7 @@
"Reinstall/Update NVIDIA drivers": "NVIDIA-Treiber neu installieren/aktualisieren",
"Reinstalled": "Neu installiert",
"Reinstalled Proxmox packages successfully": "Proxmox-Pakete erfolgreich neu installiert",
"Reinstalling": "Neuinstallation",
"Reinstalling core Proxmox packages...": "Kernpakete von Proxmox werden neu installiert...",
"Release Channel": "Release-Kanal",
"Release channel set to Beta.": "Veröffentlichungskanal auf Beta eingestellt.",
@@ -3480,7 +3495,6 @@
"Removed": "ENTFERNT",
"Removed KVM MSR options from configuration": "KVM-MSR-Optionen aus der Konfiguration entfernt",
"Removed Mount:": "Entfernte Halterung:",
"Removed all .link files from": "Alle .link-Dateien entfernt von",
"Removed bwlimit/ionice tuning (no .bak found)": "bwlimit/ionice-Tuning entfernt (keine .bak gefunden)",
"Removed configurations for": "Konfigurationen für entfernt",
"Removed credentials file:": "Anmeldeinformationsdatei entfernt:",
@@ -3503,8 +3517,8 @@
"Removing NVIDIA packages...": "NVIDIA-Pakete werden entfernt...",
"Removing OVH RTM...": "OVH RTM wird entfernt...",
"Removing OpenVSwitch...": "OpenVSwitch wird entfernt...",
"Removing ProxMenux persistent NIC .link files...": "Permanente ProxMenux-NIC-.Link-Dateien werden entfernt...",
"Removing VFIO ownership for selected GPU(s)...": "VFIO-Besitz für ausgewählte GPU(s) wird entfernt...",
"Removing all .link files from": "Entfernen aller .link-Dateien von",
"Removing any pre-existing gasket-dkms package...": "Entfernen aller bereits vorhandenen Gasket-DKMS-Pakete ...",
"Removing conflicting utilities...": "In Konflikt stehende Dienstprogramme werden entfernt...",
"Removing entropy generation optimization...": "Optimierung der Entropieerzeugung wird entfernt...",
@@ -3640,6 +3654,8 @@
"Run mode: Unattended": "Betriebsmodus: Unbeaufsichtigt",
"Run security audit now": "Führen Sie jetzt ein Sicherheitsaudit durch",
"Run the following inside the VM:": "Führen Sie Folgendes in der VM aus:",
"Run the release-channel switch from an SSH session or the Proxmox host console with:": "Führen Sie den Release-Channel-Schalter von einer SSH-Sitzung oder der Proxmox-Hostkonsole aus mit:",
"Run the update from an SSH session or the Proxmox host console with:": "Führen Sie das Update über eine SSH-Sitzung oder die Proxmox-Hostkonsole aus mit:",
"Run upgrade checklist script:": "Führen Sie das Upgrade-Checklisten-Skript aus:",
"Running": "Läuft",
"Running Lynis security audit...": "Lynis-Sicherheitsaudit wird ausgeführt...",
@@ -3648,6 +3664,7 @@
"Running VM detected": "Laufende VM erkannt",
"Running backup job:": "Sicherungsjob ausführen:",
"Running containers detected": "Laufende Container erkannt",
"Running dkms autoinstall for kernel": "Ausführen der dkms-Autoinstallation für den Kernel",
"Running pre-upgrade simulation to verify 'proxmox-ve' will remain installed...": "Führen Sie eine Simulation vor dem Upgrade durch, um zu überprüfen, ob „proxmox-ve“ installiert bleibt ...",
"SATA (standard - high compatibility)": "SATA (Standard hohe Kompatibilität)",
"SCSI (recommended for Linux and Windows)": "SCSI (empfohlen für Linux und Windows)",
@@ -4049,6 +4066,8 @@
"Skip this step if using no-subscription repository": "Überspringen Sie diesen Schritt, wenn Sie ein Repository ohne Abonnement verwenden",
"Skip — I will add it as PCIe device": "Überspringen Ich werde es als PCIe-Gerät hinzufügen",
"Skip — leave as-is": "Überspringen unverändert lassen",
"Skipped (no disks of the pool are present on this host):": "Übersprungen (auf diesem Host sind keine Festplatten des Pools vorhanden):",
"Skipped (some disks missing):": "Übersprungen (einige Festplatten fehlen):",
"Skipped device": "Übersprungenes Gerät",
"Skipped to protect target system (would cascade-remove packages)": "Übersprungen, um das Zielsystem zu schützen (würde Pakete kaskadierend entfernen)",
"Skipped, not in apt cache:": "Übersprungen, nicht im Apt-Cache:",
@@ -4183,6 +4202,7 @@
"Switch to GPU -> LXC (native driver mode)": "Wechseln Sie zu GPU -> LXC (nativer Treibermodus)",
"Switch to GPU -> VM (VFIO passthrough mode)": "Wechseln Sie zu GPU -> VM (VFIO-Passthrough-Modus)",
"Switches to the free no-subscription repository": "Wechselt zum kostenlosen Repository ohne Abonnement",
"Switching to": "Wechseln zu",
"Switching to GPU -> LXC mode removes VFIO exclusivity.": "Durch den Wechsel in den GPU->LXC-Modus wird die VFIO-Exklusivität aufgehoben.",
"Switching to GPU -> VM mode requires exclusive VFIO binding.": "Der Wechsel in den GPU -> VM-Modus erfordert eine exklusive VFIO-Bindung.",
"Synchronize time automatically": "Zeit automatisch synchronisieren",
@@ -4265,11 +4285,13 @@
"The disk": "Die Festplatte",
"The file does not exist, is empty or is not readable.": "Die Datei existiert nicht, ist leer oder nicht lesbar.",
"The filesystem": "Das Dateisystem",
"The following DKMS-managed drivers will now be rebuilt against it so they keep working after reboot:": "Die folgenden von DKMS verwalteten Treiber werden nun entsprechend neu erstellt, sodass sie nach dem Neustart weiterhin funktionieren:",
"The following LXC containers have NVIDIA passthrough configured:": "Für die folgenden LXC-Container ist NVIDIA-Passthrough konfiguriert:",
"The following backup paths are kernel-tied and are excluded from the picker to keep the target's boot safe. The operator's own tuning inside these paths (IOMMU cmdline, VFIO IDs, custom quirks) is merged back automatically via kernel-agnostic merge:": "Die folgenden Sicherungspfade sind an den Kernel gebunden und werden von der Auswahl ausgeschlossen, um den Start des Ziels zu gewährleisten.Die eigene Abstimmung des Betreibers innerhalb dieser Pfade (IOMMU-Cmdline, VFIO-IDs, benutzerdefinierte Macken) wird automatisch über die Kernel-agnostische Zusammenführung wieder zusammengeführt:",
"The following changes will be applied": "Die folgenden Änderungen werden angewendet",
"The following devices were excluded because they are part of an SR-IOV configuration:": "Die folgenden Geräte wurden ausgeschlossen, da sie Teil einer SR-IOV-Konfiguration sind:",
"The following devices were excluded from Controller/NVMe passthrough because they are part of an SR-IOV configuration:": "Die folgenden Geräte wurden vom Controller/NVMe-Passthrough ausgeschlossen, da sie Teil einer SR-IOV-Konfiguration sind:",
"The following drivers could not be rebuilt for the new kernel — run their installer manually after reboot:": "Die folgenden Treiber konnten für den neuen Kernel nicht neu erstellt werden führen Sie ihr Installationsprogramm nach dem Neustart manuell aus:",
"The following entries exist on the host but were NOT in the backup. To make the host EXACTLY match the backup state, they must be removed:": "Die folgenden Einträge sind auf dem Host vorhanden, waren aber NICHT in der Sicherung.Damit der Host GENAU mit dem Backup-Status übereinstimmt, müssen sie entfernt werden:",
"The following selected GPU(s) are currently in GPU -> VM mode (vfio-pci):": "Die folgenden ausgewählten GPU(s) befinden sich derzeit im GPU -> VM-Modus (vfio-pci):",
"The following selected device(s) are Physical Functions with active Virtual Functions:": "Bei den folgenden ausgewählten Geräten handelt es sich um physische Funktionen mit aktiven virtuellen Funktionen:",
@@ -4368,6 +4390,7 @@
"This is unexpected since credentials were validated.": "Dies ist unerwartet, da die Anmeldeinformationen validiert wurden.",
"This marks the container as unprivileged": "Dadurch wird der Container als nicht privilegiert markiert",
"This may be normal for a fresh installation": "Dies kann bei einer Neuinstallation normal sein",
"This may take a few minutes. Press OK to proceed.": "Dies kann einige Minuten dauern.Drücken Sie OK, um fortzufahren.",
"This may take a few seconds...": "Dies kann einige Sekunden dauern...",
"This may take several minutes...": "Dies kann einige Minuten dauern...",
"This means Proxmox handles mount lifecycle natively (no manual /etc/fstab needed for NFS/CIFS host storages).": "Das bedeutet, dass Proxmox den Mount-Lebenszyklus nativ verwaltet (für NFS/CIFS-Hostspeicher ist kein manuelles /etc/fstab erforderlich).",
@@ -4388,6 +4411,8 @@
"This script must be run on a Proxmox host.": "Dieses Skript muss auf einem Proxmox-Host ausgeführt werden.",
"This script will apply the following optimizations and advanced adjustments to your Proxmox VE server": "Dieses Skript wendet die folgenden Optimierungen und erweiterten Anpassungen auf Ihren Proxmox VE-Server an",
"This script will update your Proxmox VE system with advanced options:": "Dieses Skript aktualisiert Ihr Proxmox VE-System mit erweiterten Optionen:",
"This session is running in the Monitor terminal. Running it from here would cut the connection mid-install and leave the switch in a broken state.": "Diese Sitzung wird im Monitor-Terminal ausgeführt.Wenn Sie es von hier aus ausführen, wird die Verbindung während der Installation unterbrochen und der Switch bleibt in einem defekten Zustand.",
"This session is running in the Monitor terminal. Updating from here would restart the Monitor service and cut the connection mid-install, leaving the update in a broken state.": "Diese Sitzung wird im Monitor-Terminal ausgeführt.Eine Aktualisierung von hier aus würde den Monitor-Dienst neu starten und die Verbindung während der Installation unterbrechen, sodass das Update in einem fehlerhaften Zustand verbleibt.",
"This shows the storage type and disk identifier": "Hier werden der Speichertyp und die Festplattenkennung angezeigt",
"This state has a high probability of VM startup/reset failures.": "In diesem Zustand besteht eine hohe Wahrscheinlichkeit für VM-Start-/Reset-Fehler.",
"This state indicates a high risk of passthrough failure due to": "Dieser Zustand weist auf ein hohes Risiko eines Passthrough-Fehlers hin",
@@ -4475,7 +4500,9 @@
"Udev rules for Coral USB devices already exist.": "Udev-Regeln für Coral USB-Geräte existieren bereits.",
"Udev rules for Coral USB devices appended and rules reloaded.": "Udev-Regeln für Coral USB-Geräte angehängt und Regeln neu geladen.",
"Umbrel OS installer script by Helper Scripts\n\nVisit the GitHub repo to learn more, contribute, or support the project:\n\nhttps://community-scripts.github.io/ProxmoxVE/scripts?id=umbrel-os-vm": "Umbrel OS-Installationsskript von Helper Scripts\n\nBesuchen Sie das GitHub-Repo, um mehr zu erfahren, einen Beitrag zu leisten oder das Projekt zu unterstützen:\n\nhttps://community-scripts.github.io/ProxmoxVE/scripts?id=umbrel-os-vm",
"Unable to detect Proxmox version": "Proxmox-Version konnte nicht erkannt werden",
"Unable to detect Proxmox version.": "Proxmox-Version kann nicht erkannt werden.",
"Unable to determine the installed memory.": "Der installierte Speicher kann nicht ermittelt werden.",
"Understand the security implications of privileged containers": "Verstehen Sie die Sicherheitsauswirkungen privilegierter Container",
"Uninstall Coral drivers and configuration": "Deinstallieren Sie Coral-Treiber und -Konfiguration",
"Uninstall Fail2Ban": "Deinstallieren Sie Fail2Ban",
@@ -4564,6 +4591,7 @@
"Updating cluster certificates...": "Clusterzertifikate werden aktualisiert...",
"Updating initramfs (this may take a minute)...": "Initramfs wird aktualisiert (dies kann eine Minute dauern) ...",
"Updating initramfs for all kernels...": "Initramfs für alle Kernel wird aktualisiert ...",
"Updating initramfs so the ARC cap applies at next boot...": "Initramfs wird aktualisiert, sodass die ARC-Obergrenze beim nächsten Start gilt ...",
"Updating initramfs, GRUB, and EFI boot, patience...": "Aktualisieren von initramfs, GRUB und EFI-Boot, Geduld ...",
"Updating journald to store info-level messages...": "Journald wird aktualisiert, um Nachrichten auf Informationsebene zu speichern ...",
"Updating kernel panic configuration...": "Kernel-Panic-Konfiguration wird aktualisiert...",
@@ -4818,6 +4846,7 @@
"You can add servers manually.": "Sie können Server manuell hinzufügen.",
"You can enter the export path manually.": "Sie können den Exportpfad manuell eingeben.",
"You can enter the share name manually.": "Sie können den Freigabenamen manuell eingeben.",
"You can keep using ProxMenux from this terminal.": "Sie können ProxMenux weiterhin von diesem Terminal aus verwenden.",
"You can now monitor your AMD GPU using:": "Sie können Ihre AMD-GPU jetzt überwachen mit:",
"You can now monitor your Intel GPU using:": "Sie können Ihre Intel-GPU jetzt überwachen mit:",
"You can now select Controller/NVMe devices in Storage Plan.": "Sie können jetzt Controller/NVMe-Geräte im Speicherplan auswählen.",
@@ -4839,8 +4868,7 @@
"You should now be able to access the Proxmox web interface.": "Sie sollten nun auf die Proxmox-Weboberfläche zugreifen können.",
"You will need a Tailscale auth key from: https://login.tailscale.com/admin/settings/keys": "Sie benötigen einen Tailscale-Authentifizierungsschlüssel von: https://login.tailscale.com/admin/settings/keys",
"ZFS ARC config removed (kernel defaults will apply on reboot)": "ZFS ARC-Konfiguration entfernt (Kernel-Standardeinstellungen gelten beim Neustart)",
"ZFS ARC configuration file created/updated successfully": "ZFS ARC-Konfigurationsdatei erfolgreich erstellt/aktualisiert",
"ZFS ARC configuration is up to date": "Die ZFS ARC-Konfiguration ist auf dem neuesten Stand",
"ZFS ARC maximum configured:": "ZFS ARC maximal konfiguriert:",
"ZFS ARC optimization completed": "ZFS ARC-Optimierung abgeschlossen",
"ZFS Management Commands": "ZFS-Verwaltungsbefehle",
"ZFS Pool Name": "ZFS-Poolname",
@@ -4921,6 +4949,8 @@
"did not become ready. Skipping.": "nicht fertig geworden. Überspringen.",
"disk(s) added to CT": "Festplatte(n) zu CT hinzugefügt",
"disk(s) added to VM": "Festplatte(n) zur VM hinzugefügt",
"disks present": "Festplatten vorhanden",
"dkms autoinstall did not activate:": "Die automatische Installation von dkms wurde nicht aktiviert:",
"dkms.conf generated.": "dkms.conf generiert.",
"does not exist on this host. Path not added.": "existiert auf diesem Host nicht. Pfad nicht hinzugefügt.",
"does not exist. Exiting.": "existiert nicht. Verlassen.",
@@ -5039,6 +5069,7 @@
"kexec-tools and related settings removed": "kexec-tools und zugehörige Einstellungen entfernt",
"kexec-tools installed successfully": "kexec-tools erfolgreich installiert",
"kexec-tools is not installed or already removed.": "kexec-tools ist nicht installiert oder bereits entfernt.",
"legacy .link file(s) to the ProxMenux-managed format": "ältere .link-Dateien in das von ProxMenux verwaltete Format",
"log2ram completely removed from system": "log2ram vollständig aus dem System entfernt",
"manually inside the container before starting it.": "manuell in den Behälter hinein, bevor Sie ihn starten.",
"manually inside the container.": "manuell in den Behälter einfüllen.",
@@ -5114,6 +5145,7 @@
"remove the (now-empty) directory if possible": "Entfernen Sie nach Möglichkeit das (jetzt leere) Verzeichnis",
"removed from Proxmox": "aus Proxmox entfernt",
"removed successfully from Proxmox.": "erfolgreich aus Proxmox entfernt.",
"requires running the official installer, which restarts the Monitor service.": "erfordert die Ausführung des offiziellen Installationsprogramms, das den Monitor-Dienst neu startet.",
"requires the package": "erfordert das Paket",
"restarted successfully": "erfolgreich neu gestartet",
"restoring /etc/network would lose connectivity": "Das Wiederherstellen von /etc/network würde zum Verlust der Konnektivität führen",
@@ -5143,6 +5175,7 @@
"sources.list update skipped (no change)": "Aktualisierung der Quellenliste übersprungen (keine Änderung)",
"sources.list updated to Trixie": "Quellen.Liste auf Trixie aktualisiert",
"ssh-keygen failed. Cannot create a new SSH key.": "ssh-keygen ist fehlgeschlagen. Es kann kein neuer SSH-Schlüssel erstellt werden.",
"stale entry/entries for interfaces no longer present": "veralteter Eintrag/Einträge für Schnittstellen, die nicht mehr vorhanden sind",
"standard performance": "Standardleistung",
"start/restart failures and reset instability.": "Start-/Neustartfehler und Reset-Instabilität.",
"started successfully.": "erfolgreich gestartet.",
+45 -12
View File
@@ -43,6 +43,8 @@
"A host reboot is required before starting the VM. Reboot now?": "Es necesario reiniciar el host antes de iniciar la VM. ¿Reiniciar ahora?",
"A job with this ID already exists.": "Ya existe un trabajo con este ID.",
"A keyfile is installed at:": "un archivo de claves está instalado en:",
"A new ProxMenux version is available:": "Una nueva versión de ProxMenux está disponible:",
"A new kernel is staged for the next boot:": "Se prepara un nuevo kernel para el siguiente arranque:",
"A newer version is available:": "Hay una versión más nueva disponible:",
"A reboot is required after installation to load the new kernel modules.": "Es necesario reiniciar después de la instalación para cargar los nuevos módulos del kernel.",
"A reboot is required for VFIO binding to take effect. Do you want to restart now?": "Es necesario reiniciar para que la vinculación de VFIO surta efecto. ¿Quieres reiniciar ahora?",
@@ -287,6 +289,7 @@
"Authorized": "Autorizado",
"Auto-detected firewall backend (nftables/iptables)": "Backend de firewall detectado automáticamente (nftables/iptables)",
"Auto-discover servers on network": "Servidores de descubrimiento automático en la red",
"Auto-importing ZFS data pools from backup...": "Importación automática de grupos de datos ZFS desde la copia de seguridad...",
"Auto-negotiate:": "Negociar automáticamente:",
"Auto-start was skipped because GPU passthrough setup was requested.": "Se omitió el inicio automático porque se solicitó la configuración de transferencia de GPU.",
"Auto-sync enabled when /var/log exceeds 80% of": "Sincronización automática habilitada cuando /var/log excede el 80% de",
@@ -480,7 +483,6 @@
"Change Language": "Cambiar idioma",
"Change Release Channel": "Cambiar canal de lanzamiento",
"Changes applied. A system reboot is recommended for them to take full effect.": "Se aplicaron cambios. Se recomienda reiniciar el sistema para que surtan efecto completo.",
"Changes detected. Updating ZFS ARC configuration...": "Cambios detectados. Actualizando la configuración de ZFS ARC...",
"Changes have been applied to the configuration file.": "Se han aplicado cambios al archivo de configuración.",
"Changes will apply after reboot.": "Los cambios se aplicarán después del reinicio.",
"Changing Release Channel": "Cambiar el canal de lanzamiento",
@@ -515,7 +517,6 @@
"Checking ZFS autotrim configuration...": "Comprobando la configuración de recorte automático de ZFS...",
"Checking and repairing old LVM PV headers (if needed)...": "Comprobación y reparación de cabezales fotovoltaicos LVM antiguos (si es necesario)...",
"Checking conflicting drivers blacklist...": "Comprobando la lista negra de controladores en conflicto...",
"Checking existing ZFS ARC configuration...": "Comprobando la configuración ZFS ARC existente...",
"Checking for updates...": "Buscando actualizaciones...",
"Checking free space in /var/cache/apt/archives...": "Comprobando espacio libre en /var/cache/apt/archives...",
"Checking if system disk is SSD or M.2...": "Comprobando si el disco del sistema es SSD o M.2...",
@@ -972,7 +973,6 @@
"Creating export archive...": "Creando archivo de exportación...",
"Creating local archive...": "Creando archivo local...",
"Creating mount point...": "Creando punto de montaje...",
"Creating new ZFS ARC configuration...": "Creando nueva configuración ZFS ARC...",
"Creating partition table and partition...": "Creando tabla de particiones y partición...",
"Creating partition...": "Creando partición...",
"Creating pigz wrapper script...": "Creando script contenedor pigz...",
@@ -1050,6 +1050,9 @@
"DKMS add failed. Check": "Error al agregar DKMS. Controlar",
"DKMS build failed.": "Error en la compilación de DKMS.",
"DKMS build failed. Last lines of make.log:": "Error en la compilación de DKMS. Últimas líneas de make.log:",
"DKMS driver rebuild": "reconstrucción del controlador DKMS",
"DKMS drivers rebuilt for kernel": "controladores DKMS reconstruidos para el kernel",
"DKMS drivers reinstalled for kernel": "controladores DKMS reinstalados para el kernel",
"DKMS install failed.": "La instalación de DKMS falló.",
"DKMS module registered.": "Módulo DKMS registrado.",
"DNS Resolution": "Resolución DNS",
@@ -1603,10 +1606,8 @@
"Failed to create partition table on disk": "No se pudo crear la tabla de particiones en el disco",
"Failed to create partition.": "No se pudo crear la partición.",
"Failed to create temporary directory:": "No se pudo crear el directorio temporal:",
"Failed to create/update ZFS ARC configuration file": "No se pudo crear/actualizar el archivo de configuración ZFS ARC",
"Failed to destroy LXC:": "No se pudo destruir LXC:",
"Failed to destroy VM:": "No se pudo destruir la VM:",
"Failed to detect RAM size. Using default value of 16GB for ZFS ARC optimization.": "No se pudo detectar el tamaño de la RAM. Utilizando el valor predeterminado de 16 GB para la optimización ZFS ARC.",
"Failed to detect installed NVIDIA driver version.": "No se pudo detectar la versión del controlador NVIDIA instalada.",
"Failed to detect partition on disk": "No se pudo detectar la partición en el disco",
"Failed to determine timezone from IP address - keeping current timezone settings": "No se pudo determinar la zona horaria a partir de la dirección IP: se mantiene la configuración de zona horaria actual",
@@ -1697,12 +1698,14 @@
"Failed to update amdgpu_top": "No se pudo actualizar amdgpu_top",
"Failed to update auth.json — restoring backup.": "No se pudo actualizar auth.json: restaurar la copia de seguridad.",
"Failed to update cluster certificates (might not be in a cluster)": "No se pudieron actualizar los certificados del clúster (puede que no estén en un clúster)",
"Failed to update initramfs.": "No se pudo actualizar initramfs.",
"Failed to update intel-gpu-tools": "No se pudo actualizar Intel-gpu-tools",
"Failed to update package list.": "No se pudo actualizar la lista de paquetes.",
"Failed to update package lists": "No se pudieron actualizar las listas de paquetes",
"Failed to write": "No se pudo escribir",
"Failed transitions from D3cold to D0": "Transiciones fallidas de D3cold a D0",
"Failed. See log:": "Fallido. Ver registro:",
"Falling back to each installer with --auto-reinstall...": "recurrir a cada instalador con --auto-reinstall...",
"Falling back to manual paste mode.": "volver al modo de pegado manual.",
"Fastfetch Logo Selection": "Selección de logotipo de búsqueda rápida",
"Fastfetch configuration updated": "Configuración Fastfetch actualizada",
@@ -2081,6 +2084,8 @@
"Import failed for:": "La importación falló para:",
"Important: both VMs cannot be running at the same time with the same GPU.": "Importante: ambas VM no pueden ejecutarse al mismo tiempo con la misma GPU.",
"Important: some GPUs may still fail in passthrough and can affect host stability or overall performance depending on hardware/firmware quality.": "Importante: algunas GPU aún pueden fallar en la transferencia y pueden afectar la estabilidad del host o el rendimiento general según la calidad del hardware/firmware.",
"Imported (foreign hostid, forced):": "Importado (hostid extranjero, forzado):",
"Imported:": "Importado:",
"Importing": "Importador",
"Importing disk": "Importando disco",
"Importing:": "Importador:",
@@ -2216,6 +2221,7 @@
"Installing host drivers while the GPU is assigned to a VM could break passthrough and destabilize the system.": "La instalación de controladores de host mientras la GPU está asignada a una máquina virtual podría interrumpir el paso y desestabilizar el sistema.",
"Installing iSCSI initiator tools...": "Instalando herramientas de iniciador iSCSI...",
"Installing intel-gpu-tools...": "Instalando herramientas Intel-Gpu...",
"Installing kernel headers:": "Instalación de encabezados del kernel:",
"Installing kexec-tools...": "Instalando herramientas kexec...",
"Installing latest Lynis security scan tool...": "Instalando la última herramienta de análisis de seguridad de Lynis...",
"Installing packages...": "Instalando paquetes...",
@@ -2307,6 +2313,8 @@
"Kept sharedfiles group (has regular users assigned).": "Se mantiene el grupo de archivos compartidos (tiene usuarios habituales asignados).",
"Kernel and architecture info": "Información sobre el kernel y la arquitectura",
"Kernel headers and build tools verified.": "Encabezados del kernel y herramientas de compilación verificados.",
"Kernel headers install failed — DKMS rebuild will likely fail:": "Falló la instalación de los encabezados del kernel; es probable que la reconstrucción de DKMS falle:",
"Kernel headers installed": "encabezados del kernel instalados",
"Kernel max Key limit configured": "Límite máximo de claves del kernel configurado",
"Kernel panic behavior configuration completed": "Configuración del comportamiento de pánico del kernel completada",
"Kernel panic configuration removed": "Se eliminó la configuración de pánico del kernel",
@@ -2484,6 +2492,7 @@
"Method:": "Método:",
"Migrate VMs away from node being upgraded": "Migrar las máquinas virtuales fuera del nodo que se está actualizando",
"Migrate away any guests that must keep running": "Migrar cualquier invitado que deba seguir ejecutándose",
"Migrated": "migrado",
"Migrated legacy ProxMenux NVIDIA blacklist state — module will reload after reboot": "Estado de lista negra de NVIDIA ProxMenux heredado migrado: el módulo se recargará después del reinicio",
"Mirror URL not available for this script.": "La URL reflejada no está disponible para este script.",
"Missing": "Desaparecido",
@@ -2730,6 +2739,7 @@
"New Virtual Machine": "Nueva máquina virtual",
"New backup job": "Nuevo trabajo de copia de seguridad",
"New backups on this host will be unencrypted until a new keyfile is set up.": "Las nuevas copias de seguridad en este host no estarán cifradas hasta que se configure un nuevo archivo de claves.",
"New kernel staged; rebuilding DKMS drivers:": "Nuevo kernel preparado;Reconstrucción de controladores DKMS:",
"New mount options to apply:": "Nuevas opciones de montaje para aplicar:",
"New scheduled job (own timer + retention)": "Nuevo trabajo programado (temporizador propio + retención)",
"New version available": "Nueva versión disponible",
@@ -2738,7 +2748,6 @@
"Next Steps:": "Próximos pasos:",
"Next step: stop that VM first, then run": "Siguiente paso: detenga esa VM primero y luego ejecútela",
"No": "No",
"No .link files found in": "No se encontraron archivos .link en",
"No .ova or .ovf files found in:": "No se encontraron archivos .ova o .ovf en:",
"No .ovf descriptor found inside OVA.": "No se encontró ningún descriptor .ovf dentro de OVA.",
"No .pxar archives were found in this backup:": "No se encontraron archivos .pxar en esta copia de seguridad:",
@@ -2802,6 +2811,7 @@
"No PVs with old headers found.": "No se encontraron PV con encabezados antiguos.",
"No ProxMenux ZFS autotrim state file found.": "No se encontró ningún archivo de estado de recorte automático de ProxMenux ZFS.",
"No ProxMenux host-backup archives were found in:": "No se encontraron archivos de copia de seguridad del host de ProxMenux en:",
"No ProxMenux-managed .link files found — nothing to remove.": "No se encontraron archivos .link administrados por ProxMenux; no hay nada que eliminar.",
"No Recent Servers": "No hay servidores recientes",
"No Samba mounts found.": "No se encontraron monturas Samba.",
"No Samba ports found": "No se encontraron puertos Samba",
@@ -2828,6 +2838,7 @@
"No VirtIO ISO found. Please download one.": "No se encontró ningún ISO de VirtIO. Descargue uno.",
"No VirtIO ISO selected. Please choose again.": "No se seleccionó ningún ISO de VirtIO. Por favor elige de nuevo.",
"No Virtual Machines found on this system.": "No se encontraron máquinas virtuales en este sistema.",
"No ZFS pools detected. Skipping ZFS ARC optimization.": "No se detectaron grupos ZFS.Saltándose la optimización ZFS ARC.",
"No ZFS pools detected. Skipping ZFS autotrim.": "No se detectaron grupos ZFS. Saltarse el recorte automático de ZFS.",
"No accessible": "No accesible",
"No accessible NFS servers found.": "No se encontraron servidores NFS accesibles.",
@@ -3056,7 +3067,7 @@
"Optimize ZFS ARC size": "Optimizar tamaño de ZFS ARC",
"Optimize journald": "Optimizar journald",
"Optimize logrotate": "Optimizar logrotate",
"Optimizing ZFS ARC size according to available memory...": "Optimizando el tamaño de ZFS ARC según la memoria disponible...",
"Optimizing ZFS ARC maximum size...": "Optimización del tamaño máximo de ZFS ARC...",
"Optimizing logrotate configuration...": "Optimizando la configuración de logrotate...",
"Optimizing memory settings...": "Optimizando la configuración de la memoria...",
"Optimizing network settings...": "Optimizando la configuración de red...",
@@ -3082,6 +3093,7 @@
"Owner:": "Dueño:",
"Ownership set to root:sharedfiles with 2775 on:": "Propiedad establecida en root:sharedfiles con 2775 en:",
"PAM limits configured": "Límites PAM configurados",
"PBS API log rotation configured (hourly, size-based)": "Rotación de registros de API de PBS configurada (por horas, según el tamaño)",
"PBS backup error log": "Registro de errores de copia de seguridad de PBS",
"PBS backup failed.": "La copia de seguridad de PBS falló.",
"PBS encryption keyfile (show / replace / remove)": "archivo de claves de cifrado PBS (mostrar/reemplazar/eliminar)",
@@ -3299,6 +3311,7 @@
"ProxMenux logo applied": "Logotipo de ProxMenux aplicado",
"ProxMenux only acts as a launcher — once the script starts, control leaves ProxMenux.": "ProxMenux solo actúa como Lanzador del script.",
"ProxMenux saved it locally at:": "ProxMenux lo guardó localmente en:",
"ProxMenux-managed .link file(s). User-authored .link files were left in place.": "archivo(s) .link administrado por ProxMenux.Los archivos .link creados por el usuario se dejaron en su lugar.",
"Proxmology logo applied": "Logotipo de Proxmología aplicado.",
"Proxmox 9 system update allready": "Actualización del sistema Proxmox 9 ya",
"Proxmox APT repositories configured": "Repositorios Proxmox APT configurados",
@@ -3313,6 +3326,7 @@
"Proxmox VE 9.x no-subscription repository created": "Se creó el repositorio sin suscripción Proxmox VE 9.x",
"Proxmox VE Helper Scripts": "Proxmox VE Helper Scripts",
"Proxmox VE configuration completed.": "Configuración de Proxmox VE completada.",
"Proxmox VE safe update completed": "Actualización segura de Proxmox VE completada",
"Proxmox auth logger service created and started": "Servicio de registro de autenticación Proxmox creado e iniciado",
"Proxmox enterprise repo might be missing or inaccessible. Trying to switch to no-subscription...": "Es posible que falte el repositorio empresarial de Proxmox o que no se pueda acceder a él. Intentando cambiar a sin suscripción...",
"Proxmox filter configured": "Filtro Proxmox configurado",
@@ -3326,7 +3340,6 @@
"Proxmox storages:": "Almacenamientos Proxmox:",
"Proxmox system repair completed successfully!": "¡La reparación del sistema Proxmox se completó con éxito!",
"Proxmox system repair completed with some issues.": "Reparación del sistema Proxmox completada con algunos problemas.",
"Proxmox system update": "Actualización del sistema Proxmox",
"Proxmox web interface protection": "Protección de la interfaz web de Proxmox",
"Proxmox web interface: Datacenter > Storage > Add > Directory": "Interfaz web de Proxmox: Centro de datos > Almacenamiento > Agregar > Directorio",
"Proxmox web interface: Datacenter > Storage > Add > NFS": "Interfaz web de Proxmox: Centro de datos > Almacenamiento > Agregar > NFS",
@@ -3396,6 +3409,7 @@
"Recommended: schedule these paths for next boot to avoid immediate SSH disconnection.": "Recomendado: programe estas rutas para el próximo inicio para evitar la desconexión SSH inmediata.",
"Recommended: use GPU -> LXC mode for these devices.": "Recomendado: use GPU -> modo LXC para estos dispositivos.",
"Recommended: use GPU with LXC workloads instead of VM passthrough on this hardware.": "Recomendado: use GPU con cargas de trabajo LXC en lugar de transferencia de VM en este hardware.",
"Reconciled": "reconciliado",
"Recover the keyfile using your recovery passphrase?": "¿Recuperar el archivo clave usando su frase de contraseña de recuperación?",
"Recoverable:": "Recuperable:",
"Recovery blob upload failed — main backup is OK, but keyfile recovery from PBS will not be available for this backup.": "Error en la carga del blob de recuperación: la copia de seguridad principal está bien, pero la recuperación del archivo clave de PBS no estará disponible para esta copia de seguridad.",
@@ -3420,6 +3434,7 @@
"Reinstall/Update NVIDIA drivers": "Reinstalar/actualizar controladores NVIDIA",
"Reinstalled": "Reinstalado",
"Reinstalled Proxmox packages successfully": "Paquetes Proxmox reinstalados exitosamente",
"Reinstalling": "reinstalar",
"Reinstalling core Proxmox packages...": "Reinstalando los paquetes principales de Proxmox...",
"Release Channel": "Canal de lanzamiento",
"Release channel set to Beta.": "Canal de lanzamiento configurado en Beta.",
@@ -3480,7 +3495,6 @@
"Removed": "Remoto",
"Removed KVM MSR options from configuration": "Se eliminaron las opciones de KVM MSR de la configuración.",
"Removed Mount:": "Montaje eliminado:",
"Removed all .link files from": "Se eliminaron todos los archivos .link de",
"Removed bwlimit/ionice tuning (no .bak found)": "Se eliminó el ajuste de bwlimit/ionice (no se encontró .bak)",
"Removed configurations for": "Configuraciones eliminadas para",
"Removed credentials file:": "Archivo de credenciales eliminado:",
@@ -3503,8 +3517,8 @@
"Removing NVIDIA packages...": "Eliminando paquetes de NVIDIA...",
"Removing OVH RTM...": "Eliminando OVH RTM...",
"Removing OpenVSwitch...": "Eliminando OpenVSwitch...",
"Removing ProxMenux persistent NIC .link files...": "Eliminando archivos .link NIC persistentes de ProxMenux...",
"Removing VFIO ownership for selected GPU(s)...": "Eliminando la propiedad de VFIO para GPU seleccionadas...",
"Removing all .link files from": "Eliminando todos los archivos .link de",
"Removing any pre-existing gasket-dkms package...": "Eliminando cualquier paquete de junta-dkms preexistente...",
"Removing conflicting utilities...": "Eliminando utilidades en conflicto...",
"Removing entropy generation optimization...": "Eliminando la optimización de la generación de entropía...",
@@ -3640,6 +3654,8 @@
"Run mode: Unattended": "Modo de ejecución: desatendido",
"Run security audit now": "Ejecute la auditoría de seguridad ahora",
"Run the following inside the VM:": "Ejecute lo siguiente dentro de la VM:",
"Run the release-channel switch from an SSH session or the Proxmox host console with:": "ejecute el cambio de canal de lanzamiento desde una sesión SSH o la consola host de Proxmox con:",
"Run the update from an SSH session or the Proxmox host console with:": "ejecute la actualización desde una sesión SSH o la consola host de Proxmox con:",
"Run upgrade checklist script:": "Ejecute el script de la lista de verificación de actualización:",
"Running": "Correr",
"Running Lynis security audit...": "Ejecutando auditoría de seguridad de Lynis...",
@@ -3648,6 +3664,7 @@
"Running VM detected": "VM en ejecución detectada",
"Running backup job:": "Ejecutando trabajo de respaldo:",
"Running containers detected": "Contenedores en ejecución detectados",
"Running dkms autoinstall for kernel": "Ejecutando la instalación automática de dkms para el kernel",
"Running pre-upgrade simulation to verify 'proxmox-ve' will remain installed...": "Ejecutando una simulación previa a la actualización para verificar que 'proxmox-ve' permanecerá instalado...",
"SATA (standard - high compatibility)": "SATA (estándar - alta compatibilidad)",
"SCSI (recommended for Linux and Windows)": "SCSI (recomendado para Linux y Windows)",
@@ -4049,6 +4066,8 @@
"Skip this step if using no-subscription repository": "Omita este paso si utiliza un repositorio sin suscripción",
"Skip — I will add it as PCIe device": "Saltar: lo agregaré como dispositivo PCIe",
"Skip — leave as-is": "Saltar: dejar como está",
"Skipped (no disks of the pool are present on this host):": "omitido (no hay discos del grupo presentes en este host):",
"Skipped (some disks missing):": "omitido (faltan algunos discos):",
"Skipped device": "Dispositivo omitido",
"Skipped to protect target system (would cascade-remove packages)": "omitido para proteger el sistema de destino (eliminaría paquetes en cascada)",
"Skipped, not in apt cache:": "omitido, no en caché apto:",
@@ -4183,6 +4202,7 @@
"Switch to GPU -> LXC (native driver mode)": "Cambie a GPU -> LXC (modo de controlador nativo)",
"Switch to GPU -> VM (VFIO passthrough mode)": "Cambie a GPU -> VM (modo de paso VFIO)",
"Switches to the free no-subscription repository": "Cambia al repositorio gratuito sin suscripción",
"Switching to": "Cambiando a",
"Switching to GPU -> LXC mode removes VFIO exclusivity.": "Cambiar a GPU -> modo LXC elimina la exclusividad de VFIO.",
"Switching to GPU -> VM mode requires exclusive VFIO binding.": "Cambiar a GPU -> modo VM requiere un enlace VFIO exclusivo.",
"Synchronize time automatically": "Sincronizar la hora automáticamente",
@@ -4265,11 +4285,13 @@
"The disk": "el disco",
"The file does not exist, is empty or is not readable.": "El archivo no existe, está vacío o no es legible.",
"The filesystem": "El sistema de archivos",
"The following DKMS-managed drivers will now be rebuilt against it so they keep working after reboot:": "Los siguientes controladores administrados por DKMS ahora se reconstruirán para que sigan funcionando después del reinicio:",
"The following LXC containers have NVIDIA passthrough configured:": "Los siguientes contenedores LXC tienen configurado el paso a través de NVIDIA:",
"The following backup paths are kernel-tied and are excluded from the picker to keep the target's boot safe. The operator's own tuning inside these paths (IOMMU cmdline, VFIO IDs, custom quirks) is merged back automatically via kernel-agnostic merge:": "las siguientes rutas de respaldo están vinculadas al kernel y se excluyen del selector para mantener seguro el arranque del destino.El propio ajuste del operador dentro de estas rutas (línea cmd de IOMMU, ID de VFIO, peculiaridades personalizadas) se fusiona automáticamente mediante una fusión independiente del kernel:",
"The following changes will be applied": "Se aplicarán los siguientes cambios.",
"The following devices were excluded because they are part of an SR-IOV configuration:": "Se excluyeron los siguientes dispositivos porque forman parte de una configuración SR-IOV:",
"The following devices were excluded from Controller/NVMe passthrough because they are part of an SR-IOV configuration:": "Los siguientes dispositivos se excluyeron del paso directo de Controlador/NVMe porque forman parte de una configuración SR-IOV:",
"The following drivers could not be rebuilt for the new kernel — run their installer manually after reboot:": "Los siguientes controladores no se pudieron reconstruir para el nuevo kernel; ejecute su instalador manualmente después de reiniciar:",
"The following entries exist on the host but were NOT in the backup. To make the host EXACTLY match the backup state, they must be removed:": "Las siguientes entradas existen en el host pero NO estaban en la copia de seguridad.Para que el host coincida EXACTAMENTE con el estado de la copia de seguridad, se deben eliminar:",
"The following selected GPU(s) are currently in GPU -> VM mode (vfio-pci):": "Las siguientes GPU seleccionadas se encuentran actualmente en modo GPU -> VM (vfio-pci):",
"The following selected device(s) are Physical Functions with active Virtual Functions:": "Los siguientes dispositivos seleccionados son funciones físicas con funciones virtuales activas:",
@@ -4368,6 +4390,7 @@
"This is unexpected since credentials were validated.": "Esto es inesperado ya que se validaron las credenciales.",
"This marks the container as unprivileged": "Esto marca el contenedor como sin privilegios.",
"This may be normal for a fresh installation": "Esto puede ser normal para una instalación nueva.",
"This may take a few minutes. Press OK to proceed.": "Esto puede tardar unos minutos.Presione Aceptar para continuar.",
"This may take a few seconds...": "Esto puede tardar unos segundos...",
"This may take several minutes...": "Esto puede tardar varios minutos...",
"This means Proxmox handles mount lifecycle natively (no manual /etc/fstab needed for NFS/CIFS host storages).": "Esto significa que Proxmox maneja el ciclo de vida del montaje de forma nativa (no se necesita /etc/fstab manual para almacenamientos de host NFS/CIFS).",
@@ -4388,6 +4411,8 @@
"This script must be run on a Proxmox host.": "Este script debe ejecutarse en un host Proxmox.",
"This script will apply the following optimizations and advanced adjustments to your Proxmox VE server": "Este script aplicará las siguientes optimizaciones y ajustes avanzados a su servidor Proxmox VE",
"This script will update your Proxmox VE system with advanced options:": "Este script actualizará su sistema Proxmox VE con opciones avanzadas:",
"This session is running in the Monitor terminal. Running it from here would cut the connection mid-install and leave the switch in a broken state.": "esta sesión se ejecuta en la terminal Monitor.Ejecutarlo desde aquí cortaría la conexión durante la instalación y dejaría el conmutador en un estado roto.",
"This session is running in the Monitor terminal. Updating from here would restart the Monitor service and cut the connection mid-install, leaving the update in a broken state.": "esta sesión se ejecuta en la terminal Monitor.La actualización desde aquí reiniciaría el servicio Monitor y cortaría la conexión durante la instalación, dejando la actualización en un estado roto.",
"This shows the storage type and disk identifier": "Esto muestra el tipo de almacenamiento y el identificador del disco.",
"This state has a high probability of VM startup/reset failures.": "Este estado tiene una alta probabilidad de que se produzcan errores de inicio/reinicio de la máquina virtual.",
"This state indicates a high risk of passthrough failure due to": "Este estado indica un alto riesgo de fallo de paso debido a",
@@ -4475,7 +4500,9 @@
"Udev rules for Coral USB devices already exist.": "Las reglas de Udev para dispositivos Coral USB ya existen.",
"Udev rules for Coral USB devices appended and rules reloaded.": "Se agregaron reglas de Udev para dispositivos Coral USB y se recargaron reglas.",
"Umbrel OS installer script by Helper Scripts\n\nVisit the GitHub repo to learn more, contribute, or support the project:\n\nhttps://community-scripts.github.io/ProxmoxVE/scripts?id=umbrel-os-vm": "Script de instalación del sistema operativo Umbrel de Helper Scripts\n\nVisite el repositorio de GitHub para obtener más información, contribuir o apoyar el proyecto:\n\nhttps://community-scripts.github.io/ProxmoxVE/scripts?id=umbrel-os-vm",
"Unable to detect Proxmox version": "No se puede detectar la versión de Proxmox",
"Unable to detect Proxmox version.": "No se puede detectar la versión de Proxmox.",
"Unable to determine the installed memory.": "No se puede determinar la memoria instalada.",
"Understand the security implications of privileged containers": "Comprender las implicaciones de seguridad de los contenedores privilegiados",
"Uninstall Coral drivers and configuration": "Desinstalar los controladores y la configuración de Coral",
"Uninstall Fail2Ban": "Desinstalar Fail2Ban",
@@ -4564,6 +4591,7 @@
"Updating cluster certificates...": "Actualizando certificados de clúster...",
"Updating initramfs (this may take a minute)...": "Actualizando initramfs (esto puede tardar un minuto)...",
"Updating initramfs for all kernels...": "Actualizando initramfs para todos los kernels...",
"Updating initramfs so the ARC cap applies at next boot...": "Actualizando initramfs para que el límite ARC se aplique en el próximo arranque...",
"Updating initramfs, GRUB, and EFI boot, patience...": "Actualizando initramfs, GRUB y arranque EFI, paciencia...",
"Updating journald to store info-level messages...": "Actualizando el diario para almacenar mensajes a nivel de información...",
"Updating kernel panic configuration...": "Actualizando la configuración de pánico del kernel...",
@@ -4818,6 +4846,7 @@
"You can add servers manually.": "Puede agregar servidores manualmente.",
"You can enter the export path manually.": "Puede ingresar la ruta de exportación manualmente.",
"You can enter the share name manually.": "Puede ingresar el nombre del recurso compartido manualmente.",
"You can keep using ProxMenux from this terminal.": "Puedes seguir usando ProxMenux desde esta terminal.",
"You can now monitor your AMD GPU using:": "Ahora puedes monitorear tu GPU AMD usando:",
"You can now monitor your Intel GPU using:": "Ahora puedes monitorear tu GPU Intel usando:",
"You can now select Controller/NVMe devices in Storage Plan.": "Ahora puede seleccionar dispositivos Controlador/NVMe en el Plan de almacenamiento.",
@@ -4839,8 +4868,7 @@
"You should now be able to access the Proxmox web interface.": "Ahora debería poder acceder a la interfaz web de Proxmox.",
"You will need a Tailscale auth key from: https://login.tailscale.com/admin/settings/keys": "Necesitará una clave de autenticación de Tailscale de: https://login.tailscale.com/admin/settings/keys",
"ZFS ARC config removed (kernel defaults will apply on reboot)": "Se eliminó la configuración de ZFS ARC (los valores predeterminados del kernel se aplicarán al reiniciar)",
"ZFS ARC configuration file created/updated successfully": "Archivo de configuración ZFS ARC creado/actualizado correctamente",
"ZFS ARC configuration is up to date": "La configuración de ZFS ARC está actualizada",
"ZFS ARC maximum configured:": "ZFS ARC máximo configurado:",
"ZFS ARC optimization completed": "Optimización ZFS ARC completada",
"ZFS Management Commands": "Comandos de gestión ZFS",
"ZFS Pool Name": "Nombre del grupo ZFS",
@@ -4921,6 +4949,8 @@
"did not become ready. Skipping.": "no estuvo listo. Salto a la comba.",
"disk(s) added to CT": "disco(s) agregado(s) a CT",
"disk(s) added to VM": "discos agregados a la VM",
"disks present": "discos presentes",
"dkms autoinstall did not activate:": "la instalación automática de dkms no se activó:",
"dkms.conf generated.": "dkms.conf generado.",
"does not exist on this host. Path not added.": "no existe en este host.Ruta no agregada.",
"does not exist. Exiting.": "no existe. Saliendo.",
@@ -5039,6 +5069,7 @@
"kexec-tools and related settings removed": "Se eliminaron kexec-tools y configuraciones relacionadas",
"kexec-tools installed successfully": "kexec-tools instalado correctamente",
"kexec-tools is not installed or already removed.": "kexec-tools no está instalado o ya se ha eliminado.",
"legacy .link file(s) to the ProxMenux-managed format": "archivos .link heredados al formato administrado por ProxMenux",
"log2ram completely removed from system": "log2ram completamente eliminado del sistema",
"manually inside the container before starting it.": "manualmente dentro del contenedor antes de ponerlo en marcha.",
"manually inside the container.": "manualmente dentro del contenedor.",
@@ -5114,6 +5145,7 @@
"remove the (now-empty) directory if possible": "elimine el directorio (ahora vacío) si es posible",
"removed from Proxmox": "eliminado de Proxmox",
"removed successfully from Proxmox.": "eliminado exitosamente de Proxmox.",
"requires running the official installer, which restarts the Monitor service.": "requiere ejecutar el instalador oficial, que reinicia el servicio Monitor.",
"requires the package": "requiere el paquete",
"restarted successfully": "reiniciado exitosamente",
"restoring /etc/network would lose connectivity": "restaurar /etc/network perdería conectividad",
@@ -5143,6 +5175,7 @@
"sources.list update skipped (no change)": "Actualización de fuentes.list omitida (sin cambios)",
"sources.list updated to Trixie": "fuentes.lista actualizada a Trixie",
"ssh-keygen failed. Cannot create a new SSH key.": "ssh-keygen falló.No se puede crear una nueva clave SSH.",
"stale entry/entries for interfaces no longer present": "entrada obsoleta/entradas para interfaces que ya no están presentes",
"standard performance": "rendimiento estándar",
"start/restart failures and reset instability.": "fallos de inicio/reinicio y reinicio de inestabilidad.",
"started successfully.": "comenzó exitosamente.",
+45 -12
View File
@@ -43,6 +43,8 @@
"A host reboot is required before starting the VM. Reboot now?": "Un redémarrage de l'hôte est requis avant de démarrer la VM. Redémarrer maintenant ?",
"A job with this ID already exists.": "Une tâche avec cet ID existe déjà.",
"A keyfile is installed at:": "Un fichier de clés est installé à :",
"A new ProxMenux version is available:": "Une nouvelle version de ProxMenux est disponible :",
"A new kernel is staged for the next boot:": "Un nouveau noyau est préparé pour le prochain démarrage :",
"A newer version is available:": "Une version plus récente est disponible :",
"A reboot is required after installation to load the new kernel modules.": "Un redémarrage est requis après l'installation pour charger les nouveaux modules du noyau.",
"A reboot is required for VFIO binding to take effect. Do you want to restart now?": "Un redémarrage est requis pour que la liaison VFIO prenne effet. Voulez-vous redémarrer maintenant ?",
@@ -287,6 +289,7 @@
"Authorized": "Autorisé",
"Auto-detected firewall backend (nftables/iptables)": "Backend de pare-feu détecté automatiquement (nftables/iptables)",
"Auto-discover servers on network": "Détection automatique des serveurs sur le réseau",
"Auto-importing ZFS data pools from backup...": "Importation automatique des pools de données ZFS à partir de la sauvegarde...",
"Auto-negotiate:": "Négociation automatique :",
"Auto-start was skipped because GPU passthrough setup was requested.": "Le démarrage automatique a été ignoré car la configuration du relais GPU a été demandée.",
"Auto-sync enabled when /var/log exceeds 80% of": "Synchronisation automatique activée lorsque /var/log dépasse 80 % de",
@@ -480,7 +483,6 @@
"Change Language": "Changer de langue",
"Change Release Channel": "Changer le canal de publication",
"Changes applied. A system reboot is recommended for them to take full effect.": "Modifications appliquées. Un redémarrage du système est recommandé pour qu'ils prennent pleinement effet.",
"Changes detected. Updating ZFS ARC configuration...": "Modifications détectées. Mise à jour de la configuration ZFS ARC...",
"Changes have been applied to the configuration file.": "Les modifications ont été appliquées au fichier de configuration.",
"Changes will apply after reboot.": "Les modifications s'appliqueront après le redémarrage.",
"Changing Release Channel": "Changer le canal de publication",
@@ -515,7 +517,6 @@
"Checking ZFS autotrim configuration...": "Vérification de la configuration du découpage automatique ZFS...",
"Checking and repairing old LVM PV headers (if needed)...": "Vérification et réparation des anciens collecteurs PV LVM (si nécessaire)...",
"Checking conflicting drivers blacklist...": "Vérification de la liste noire des pilotes en conflit...",
"Checking existing ZFS ARC configuration...": "Vérification de la configuration ZFS ARC existante...",
"Checking for updates...": "Vérification des mises à jour...",
"Checking free space in /var/cache/apt/archives...": "Vérification de l'espace libre dans /var/cache/apt/archives...",
"Checking if system disk is SSD or M.2...": "Vérifier si le disque système est SSD ou M.2...",
@@ -972,7 +973,6 @@
"Creating export archive...": "Création d'une archive d'exportation...",
"Creating local archive...": "Création d'archives locales...",
"Creating mount point...": "Création du point de montage...",
"Creating new ZFS ARC configuration...": "Création d'une nouvelle configuration ZFS ARC...",
"Creating partition table and partition...": "Création d'une table de partition et d'une partition...",
"Creating partition...": "Création d'une partition...",
"Creating pigz wrapper script...": "Création du script wrapper pigz...",
@@ -1050,6 +1050,9 @@
"DKMS add failed. Check": "L'ajout de DKMS a échoué. Vérifier",
"DKMS build failed.": "La construction de DKMS a échoué.",
"DKMS build failed. Last lines of make.log:": "La construction de DKMS a échoué. Dernières lignes de make.log :",
"DKMS driver rebuild": "Reconstruction du pilote DKMS",
"DKMS drivers rebuilt for kernel": "Pilotes DKMS reconstruits pour le noyau",
"DKMS drivers reinstalled for kernel": "pilotes DKMS réinstallés pour le noyau",
"DKMS install failed.": "L'installation de DKMS a échoué.",
"DKMS module registered.": "Module DKMS enregistré.",
"DNS Resolution": "Résolution DNS",
@@ -1603,10 +1606,8 @@
"Failed to create partition table on disk": "Échec de la création de la table de partition sur le disque",
"Failed to create partition.": "Échec de la création de la partition.",
"Failed to create temporary directory:": "Échec de la création du répertoire temporaire :",
"Failed to create/update ZFS ARC configuration file": "Échec de la création/mise à jour du fichier de configuration ZFS ARC",
"Failed to destroy LXC:": "Échec de la destruction de LXC :",
"Failed to destroy VM:": "Échec de la destruction de la VM :",
"Failed to detect RAM size. Using default value of 16GB for ZFS ARC optimization.": "Échec de la détection de la taille de la RAM. Utilisation de la valeur par défaut de 16 Go pour l'optimisation ZFS ARC.",
"Failed to detect installed NVIDIA driver version.": "Échec de la détection de la version du pilote NVIDIA installé.",
"Failed to detect partition on disk": "Échec de la détection de la partition sur le disque",
"Failed to determine timezone from IP address - keeping current timezone settings": "Échec de la détermination du fuseau horaire à partir de l'adresse IP - conservation des paramètres de fuseau horaire actuels",
@@ -1697,12 +1698,14 @@
"Failed to update amdgpu_top": "Échec de la mise à jour d'amdgpu_top",
"Failed to update auth.json — restoring backup.": "Échec de la mise à jour d'auth.json — restauration de la sauvegarde.",
"Failed to update cluster certificates (might not be in a cluster)": "Échec de la mise à jour des certificats de cluster (il se peut qu'il ne se trouve pas dans un cluster)",
"Failed to update initramfs.": "Échec de la mise à jour d'initramfs.",
"Failed to update intel-gpu-tools": "Échec de la mise à jour des outils Intel-Gpu",
"Failed to update package list.": "Échec de la mise à jour de la liste des packages.",
"Failed to update package lists": "Échec de la mise à jour des listes de packages",
"Failed to write": "Échec de l'écriture",
"Failed transitions from D3cold to D0": "Échec des transitions de D3cold à D0",
"Failed. See log:": "Échoué. Voir le journal :",
"Falling back to each installer with --auto-reinstall...": "Revenir à chaque installateur avec --auto-reinstall...",
"Falling back to manual paste mode.": "Revenir au mode de collage manuel.",
"Fastfetch Logo Selection": "Sélection de logos Fastfetch",
"Fastfetch configuration updated": "Configuration Fastfetch mise à jour",
@@ -2081,6 +2084,8 @@
"Import failed for:": "Échec de l'importation pour :",
"Important: both VMs cannot be running at the same time with the same GPU.": "Important : les deux VM ne peuvent pas fonctionner en même temps avec le même GPU.",
"Important: some GPUs may still fail in passthrough and can affect host stability or overall performance depending on hardware/firmware quality.": "Important : certains GPU peuvent toujours échouer en mode relais et affecter la stabilité de l'hôte ou les performances globales en fonction de la qualité du matériel/micrologiciel.",
"Imported (foreign hostid, forced):": "Importé (hostid étranger, forcé) :",
"Imported:": "Importé :",
"Importing": "Importation",
"Importing disk": "Importer un disque",
"Importing:": "Importation :",
@@ -2216,6 +2221,7 @@
"Installing host drivers while the GPU is assigned to a VM could break passthrough and destabilize the system.": "L'installation de pilotes hôtes alors que le GPU est attribué à une VM pourrait interrompre le relais et déstabiliser le système.",
"Installing iSCSI initiator tools...": "Installation des outils d'initiateur iSCSI...",
"Installing intel-gpu-tools...": "Installation des outils Intel-Gpu...",
"Installing kernel headers:": "Installation des en-têtes du noyau :",
"Installing kexec-tools...": "Installation des outils kexec...",
"Installing latest Lynis security scan tool...": "Installation du dernier outil d'analyse de sécurité Lynis...",
"Installing packages...": "Installation des packages...",
@@ -2307,6 +2313,8 @@
"Kept sharedfiles group (has regular users assigned).": "Groupe de fichiers partagés conservé (des utilisateurs réguliers sont attribués).",
"Kernel and architecture info": "Informations sur le noyau et l'architecture",
"Kernel headers and build tools verified.": "En-têtes du noyau et outils de build vérifiés.",
"Kernel headers install failed — DKMS rebuild will likely fail:": "Échec de l'installation des en-têtes du noyau  La reconstruction de DKMS échouera probablement :",
"Kernel headers installed": "en-têtes de noyau installés",
"Kernel max Key limit configured": "Limite de clé maximale du noyau configurée",
"Kernel panic behavior configuration completed": "Configuration du comportement de panique du noyau terminée",
"Kernel panic configuration removed": "Configuration de panique du noyau supprimée",
@@ -2484,6 +2492,7 @@
"Method:": "Méthode:",
"Migrate VMs away from node being upgraded": "Migrer les VM hors du nœud en cours de mise à niveau",
"Migrate away any guests that must keep running": "Migrez tous les invités qui doivent continuer à fonctionner",
"Migrated": "Migré",
"Migrated legacy ProxMenux NVIDIA blacklist state — module will reload after reboot": "État de la liste noire ProxMenux NVIDIA héritée migrée : le module se rechargera après le redémarrage",
"Mirror URL not available for this script.": "URL miroir non disponible pour ce script.",
"Missing": "Manquant",
@@ -2730,6 +2739,7 @@
"New Virtual Machine": "Nouvelle machine virtuelle",
"New backup job": "Nouveau travail de sauvegarde",
"New backups on this host will be unencrypted until a new keyfile is set up.": "Les nouvelles sauvegardes sur cet hôte ne seront pas chiffrées jusqu'à ce qu'un nouveau fichier de clés soit configuré.",
"New kernel staged; rebuilding DKMS drivers:": "Nouveau noyau mis en scène ;reconstruction des pilotes DKMS :",
"New mount options to apply:": "Nouvelles options de montage à appliquer :",
"New scheduled job (own timer + retention)": "Nouvelle tâche planifiée (propre minuterie + rétention)",
"New version available": "Nouvelle version disponible",
@@ -2738,7 +2748,6 @@
"Next Steps:": "Prochaines étapes :",
"Next step: stop that VM first, then run": "Étape suivante : arrêtez d'abord cette VM, puis exécutez",
"No": "Non",
"No .link files found in": "Aucun fichier .link trouvé dans",
"No .ova or .ovf files found in:": "Aucun fichier .ova ou .ovf trouvé dans :",
"No .ovf descriptor found inside OVA.": "Aucun descripteur .ovf trouvé dans OVA.",
"No .pxar archives were found in this backup:": "Aucune archive .pxar n'a été trouvée dans cette sauvegarde :",
@@ -2802,6 +2811,7 @@
"No PVs with old headers found.": "Aucun PV avec d'anciens en-têtes trouvé.",
"No ProxMenux ZFS autotrim state file found.": "Aucun fichier d'état de découpage automatique ProxMenux ZFS trouvé.",
"No ProxMenux host-backup archives were found in:": "Aucune archive de sauvegarde d'hôte ProxMenux n'a été trouvée dans :",
"No ProxMenux-managed .link files found — nothing to remove.": "aucun fichier .link géré par ProxMenux trouvé  rien à supprimer.",
"No Recent Servers": "Aucun serveur récent",
"No Samba mounts found.": "Aucune monture Samba trouvée.",
"No Samba ports found": "Aucun port Samba trouvé",
@@ -2828,6 +2838,7 @@
"No VirtIO ISO found. Please download one.": "Aucun ISO VirtIO trouvé. Veuillez en télécharger un.",
"No VirtIO ISO selected. Please choose again.": "Aucun ISO VirtIO sélectionné. Veuillez choisir à nouveau.",
"No Virtual Machines found on this system.": "Aucune machine virtuelle trouvée sur ce système.",
"No ZFS pools detected. Skipping ZFS ARC optimization.": "Aucun pool ZFS détecté.Ignorer l'optimisation ZFS ARC.",
"No ZFS pools detected. Skipping ZFS autotrim.": "Aucun pool ZFS détecté. Ignorer le découpage automatique ZFS.",
"No accessible": "Non accessible",
"No accessible NFS servers found.": "Aucun serveur NFS accessible trouvé.",
@@ -3056,7 +3067,7 @@
"Optimize ZFS ARC size": "Optimiser la taille de ZFS ARC",
"Optimize journald": "Optimiser journald",
"Optimize logrotate": "Optimiser la rotation du log",
"Optimizing ZFS ARC size according to available memory...": "Optimisation de la taille de ZFS ARC en fonction de la mémoire disponible...",
"Optimizing ZFS ARC maximum size...": "Optimisation de la taille maximale de ZFS ARC...",
"Optimizing logrotate configuration...": "Optimisation de la configuration de la rotation des logs...",
"Optimizing memory settings...": "Optimisation des paramètres de mémoire...",
"Optimizing network settings...": "Optimisation des paramètres réseau...",
@@ -3082,6 +3093,7 @@
"Owner:": "Propriétaire:",
"Ownership set to root:sharedfiles with 2775 on:": "Propriété définie sur root:sharedfiles avec 2775 sur :",
"PAM limits configured": "Limites PAM configurées",
"PBS API log rotation configured (hourly, size-based)": "rotation des journaux de l'API PBS configurée (horaire, basée sur la taille)",
"PBS backup error log": "Journal des erreurs de sauvegarde PBS",
"PBS backup failed.": "La sauvegarde PBS a échoué.",
"PBS encryption keyfile (show / replace / remove)": "fichier de clé de chiffrement PBS (afficher/remplacer/supprimer)",
@@ -3299,6 +3311,7 @@
"ProxMenux logo applied": "Logo ProxMenux appliqué",
"ProxMenux only acts as a launcher — once the script starts, control leaves ProxMenux.": "ProxMenux agit uniquement comme un lanceur : une fois le script démarré, le contrôle quitte ProxMenux.",
"ProxMenux saved it locally at:": "ProxMenux l'a enregistré localement à :",
"ProxMenux-managed .link file(s). User-authored .link files were left in place.": "fichier(s) .link gérés par ProxMenux.Les fichiers .link créés par l'utilisateur ont été laissés en place.",
"Proxmology logo applied": "Logo Proxmologie appliqué",
"Proxmox 9 system update allready": "La mise à jour du système Proxmox 9 est déjà terminée",
"Proxmox APT repositories configured": "Dépôts Proxmox APT configurés",
@@ -3313,6 +3326,7 @@
"Proxmox VE 9.x no-subscription repository created": "Création d'un référentiel sans abonnement Proxmox VE 9.x",
"Proxmox VE Helper Scripts": "Scripts d'assistance Proxmox VE",
"Proxmox VE configuration completed.": "Configuration de Proxmox VE terminée.",
"Proxmox VE safe update completed": "Mise à jour sécurisée de Proxmox VE terminée",
"Proxmox auth logger service created and started": "Service d'enregistrement d'authentification Proxmox créé et démarré",
"Proxmox enterprise repo might be missing or inaccessible. Trying to switch to no-subscription...": "Le référentiel d'entreprise Proxmox peut être manquant ou inaccessible. J'essaie de passer au sans abonnement...",
"Proxmox filter configured": "Filtre Proxmox configuré",
@@ -3326,7 +3340,6 @@
"Proxmox storages:": "Stockages Proxmox :",
"Proxmox system repair completed successfully!": "Réparation du système Proxmox terminée avec succès !",
"Proxmox system repair completed with some issues.": "Réparation du système Proxmox terminée avec quelques problèmes.",
"Proxmox system update": "Mise à jour du système Proxmox",
"Proxmox web interface protection": "Protection de l'interface Web Proxmox",
"Proxmox web interface: Datacenter > Storage > Add > Directory": "Interface web Proxmox : Datacenter > Stockage > Ajouter > Annuaire",
"Proxmox web interface: Datacenter > Storage > Add > NFS": "Interface web Proxmox : Datacenter > Stockage > Ajouter > NFS",
@@ -3396,6 +3409,7 @@
"Recommended: schedule these paths for next boot to avoid immediate SSH disconnection.": "Recommandé : planifiez ces chemins pour le prochain démarrage afin d'éviter une déconnexion SSH immédiate.",
"Recommended: use GPU -> LXC mode for these devices.": "Recommandé : utilisez le mode GPU -> LXC pour ces appareils.",
"Recommended: use GPU with LXC workloads instead of VM passthrough on this hardware.": "Recommandé : utilisez le GPU avec les charges de travail LXC au lieu du relais VM sur ce matériel.",
"Reconciled": "Réconcilié",
"Recover the keyfile using your recovery passphrase?": "Récupérer le fichier de clés à l'aide de votre phrase secrète de récupération ?",
"Recoverable:": "Récupérable :",
"Recovery blob upload failed — main backup is OK, but keyfile recovery from PBS will not be available for this backup.": "Échec du téléchargement du blob de récupération  la sauvegarde principale est OK, mais la récupération du fichier de clé à partir de PBS ne sera pas disponible pour cette sauvegarde.",
@@ -3420,6 +3434,7 @@
"Reinstall/Update NVIDIA drivers": "Réinstaller/mettre à jour les pilotes NVIDIA",
"Reinstalled": "Réinstallé",
"Reinstalled Proxmox packages successfully": "Packages Proxmox réinstallés avec succès",
"Reinstalling": "Réinstallation",
"Reinstalling core Proxmox packages...": "Réinstallation des packages Proxmox principaux...",
"Release Channel": "Canal de sortie",
"Release channel set to Beta.": "Canal de sortie défini sur Bêta.",
@@ -3480,7 +3495,6 @@
"Removed": "Supprimé",
"Removed KVM MSR options from configuration": "Options KVM MSR supprimées de la configuration",
"Removed Mount:": "Support supprimé :",
"Removed all .link files from": "Suppression de tous les fichiers .link de",
"Removed bwlimit/ionice tuning (no .bak found)": "Suppression du réglage bwlimit/ionice (aucun .bak trouvé)",
"Removed configurations for": "Configurations supprimées pour",
"Removed credentials file:": "Fichier d'informations d'identification supprimé :",
@@ -3503,8 +3517,8 @@
"Removing NVIDIA packages...": "Suppression des packages NVIDIA...",
"Removing OVH RTM...": "Suppression d'OVH RTM...",
"Removing OpenVSwitch...": "Suppression d'OpenVSwitch...",
"Removing ProxMenux persistent NIC .link files...": "Suppression des fichiers .link de la carte réseau persistante de ProxMenux...",
"Removing VFIO ownership for selected GPU(s)...": "Suppression de la propriété VFIO pour les GPU sélectionnés...",
"Removing all .link files from": "Suppression de tous les fichiers .link de",
"Removing any pre-existing gasket-dkms package...": "Suppression de tout paquet joint-dkms préexistant...",
"Removing conflicting utilities...": "Suppression des utilitaires en conflit...",
"Removing entropy generation optimization...": "Suppression de l'optimisation de la génération d'entropie...",
@@ -3640,6 +3654,8 @@
"Run mode: Unattended": "Mode d'exécution : sans surveillance",
"Run security audit now": "Exécutez un audit de sécurité maintenant",
"Run the following inside the VM:": "Exécutez la commande suivante dans la VM :",
"Run the release-channel switch from an SSH session or the Proxmox host console with:": "Exécutez le commutateur de canal de version à partir d'une session SSH ou de la console hôte Proxmox avec :",
"Run the update from an SSH session or the Proxmox host console with:": "Exécutez la mise à jour depuis une session SSH ou la console hôte Proxmox avec :",
"Run upgrade checklist script:": "Exécutez le script de liste de contrôle de mise à niveau :",
"Running": "En cours d'exécution",
"Running Lynis security audit...": "Exécution de l'audit de sécurité Lynis...",
@@ -3648,6 +3664,7 @@
"Running VM detected": "VM en cours d'exécution détectée",
"Running backup job:": "Exécution d'une tâche de sauvegarde :",
"Running containers detected": "Conteneurs en cours d'exécution détectés",
"Running dkms autoinstall for kernel": "Exécution de l'installation automatique de dkms pour le noyau",
"Running pre-upgrade simulation to verify 'proxmox-ve' will remain installed...": "Exécution d'une simulation de pré-mise à niveau pour vérifier que « proxmox-ve » restera installé...",
"SATA (standard - high compatibility)": "SATA (standard - haute compatibilité)",
"SCSI (recommended for Linux and Windows)": "SCSI (recommandé pour Linux et Windows)",
@@ -4049,6 +4066,8 @@
"Skip this step if using no-subscription repository": "Ignorez cette étape si vous utilisez un référentiel sans abonnement",
"Skip — I will add it as PCIe device": "Ignorer — Je vais l'ajouter en tant que périphérique PCIe",
"Skip — leave as-is": "Sauter — laisser tel quel",
"Skipped (no disks of the pool are present on this host):": "Ignoré (aucun disque du pool nest présent sur cet hôte) :",
"Skipped (some disks missing):": "Sauté (certains disques manquants) :",
"Skipped device": "Appareil ignoré",
"Skipped to protect target system (would cascade-remove packages)": "Ignoré pour protéger le système cible (supprimerait les packages en cascade)",
"Skipped, not in apt cache:": "Ignoré, pas dans le cache apt :",
@@ -4183,6 +4202,7 @@
"Switch to GPU -> LXC (native driver mode)": "Passer au GPU -> LXC (mode pilote natif)",
"Switch to GPU -> VM (VFIO passthrough mode)": "Passer au GPU -> VM (mode passthrough VFIO)",
"Switches to the free no-subscription repository": "Passe au référentiel gratuit sans abonnement",
"Switching to": "Passer à",
"Switching to GPU -> LXC mode removes VFIO exclusivity.": "Le passage au mode GPU -> LXC supprime l'exclusivité VFIO.",
"Switching to GPU -> VM mode requires exclusive VFIO binding.": "Le passage au mode GPU -> VM nécessite une liaison VFIO exclusive.",
"Synchronize time automatically": "Synchroniser l'heure automatiquement",
@@ -4265,11 +4285,13 @@
"The disk": "Le disque",
"The file does not exist, is empty or is not readable.": "Le fichier n'existe pas, est vide ou n'est pas lisible.",
"The filesystem": "Le système de fichiers",
"The following DKMS-managed drivers will now be rebuilt against it so they keep working after reboot:": "Les pilotes gérés par DKMS suivants seront désormais reconstruits afin qu'ils continuent de fonctionner après le redémarrage :",
"The following LXC containers have NVIDIA passthrough configured:": "Les conteneurs LXC suivants ont configuré le relais NVIDIA :",
"The following backup paths are kernel-tied and are excluded from the picker to keep the target's boot safe. The operator's own tuning inside these paths (IOMMU cmdline, VFIO IDs, custom quirks) is merged back automatically via kernel-agnostic merge:": "Les chemins de sauvegarde suivants sont liés au noyau et sont exclus du sélecteur pour assurer la sécurité du démarrage de la cible.Les propres réglages de l'opérateur à l'intérieur de ces chemins (ligne de commande IOMMU, ID VFIO, bizarreries personnalisées) sont automatiquement fusionnés via une fusion indépendante du noyau :",
"The following changes will be applied": "Les modifications suivantes seront appliquées",
"The following devices were excluded because they are part of an SR-IOV configuration:": "Les appareils suivants ont été exclus car ils font partie d'une configuration SR-IOV :",
"The following devices were excluded from Controller/NVMe passthrough because they are part of an SR-IOV configuration:": "Les périphériques suivants ont été exclus du relais Controller/NVMe car ils font partie d'une configuration SR-IOV :",
"The following drivers could not be rebuilt for the new kernel — run their installer manually after reboot:": "Les pilotes suivants n'ont pas pu être reconstruits pour le nouveau noyau — exécutez leur programme d'installation manuellement après le redémarrage :",
"The following entries exist on the host but were NOT in the backup. To make the host EXACTLY match the backup state, they must be removed:": "Les entrées suivantes existent sur l'hôte mais n'étaient PAS dans la sauvegarde.Pour que l'hôte corresponde EXACTEMENT à l'état de la sauvegarde, ils doivent être supprimés :",
"The following selected GPU(s) are currently in GPU -> VM mode (vfio-pci):": "Les GPU sélectionnés suivants sont actuellement en mode GPU -> VM (vfio-pci) :",
"The following selected device(s) are Physical Functions with active Virtual Functions:": "Les appareils sélectionnés suivants sont des fonctions physiques avec des fonctions virtuelles actives :",
@@ -4368,6 +4390,7 @@
"This is unexpected since credentials were validated.": "C'est inattendu puisque les informations d'identification ont été validées.",
"This marks the container as unprivileged": "Cela marque le conteneur comme non privilégié",
"This may be normal for a fresh installation": "Cela peut être normal pour une nouvelle installation",
"This may take a few minutes. Press OK to proceed.": "Cela peut prendre quelques minutes.Appuyez sur OK pour continuer.",
"This may take a few seconds...": "Cela peut prendre quelques secondes...",
"This may take several minutes...": "Cela peut prendre plusieurs minutes...",
"This means Proxmox handles mount lifecycle natively (no manual /etc/fstab needed for NFS/CIFS host storages).": "Cela signifie que Proxmox gère le cycle de vie du montage de manière native (aucun /etc/fstab manuel n'est nécessaire pour les stockages hôtes NFS/CIFS).",
@@ -4388,6 +4411,8 @@
"This script must be run on a Proxmox host.": "Ce script doit être exécuté sur un hôte Proxmox.",
"This script will apply the following optimizations and advanced adjustments to your Proxmox VE server": "Ce script appliquera les optimisations et ajustements avancés suivants à votre serveur Proxmox VE",
"This script will update your Proxmox VE system with advanced options:": "Ce script mettra à jour votre système Proxmox VE avec des options avancées :",
"This session is running in the Monitor terminal. Running it from here would cut the connection mid-install and leave the switch in a broken state.": "Cette session est en cours d'exécution dans le terminal Monitor.Lexécuter à partir dici couperait la connexion en cours dinstallation et laisserait le commutateur dans un état cassé.",
"This session is running in the Monitor terminal. Updating from here would restart the Monitor service and cut the connection mid-install, leaving the update in a broken state.": "Cette session est en cours d'exécution dans le terminal Monitor.La mise à jour à partir d'ici redémarrerait le service Monitor et couperait la connexion en cours d'installation, laissant la mise à jour dans un état interrompu.",
"This shows the storage type and disk identifier": "Ceci montre le type de stockage et l'identifiant du disque",
"This state has a high probability of VM startup/reset failures.": "Cet état présente une forte probabilité d’échecs de démarrage/réinitialisation de la VM.",
"This state indicates a high risk of passthrough failure due to": "Cet état indique un risque élevé d'échec du relais en raison de",
@@ -4475,7 +4500,9 @@
"Udev rules for Coral USB devices already exist.": "Les règles Udev pour les périphériques Coral USB existent déjà.",
"Udev rules for Coral USB devices appended and rules reloaded.": "Règles Udev pour les périphériques USB Coral ajoutées et règles rechargées.",
"Umbrel OS installer script by Helper Scripts\n\nVisit the GitHub repo to learn more, contribute, or support the project:\n\nhttps://community-scripts.github.io/ProxmoxVE/scripts?id=umbrel-os-vm": "Script d'installation du système d'exploitation Umbrel par Helper Scripts\n\nVisitez le dépôt GitHub pour en savoir plus, contribuer ou soutenir le projet :\n\nhttps://community-scripts.github.io/ProxmoxVE/scripts?id=umbrel-os-vm",
"Unable to detect Proxmox version": "Impossible de détecter la version de Proxmox",
"Unable to detect Proxmox version.": "Impossible de détecter la version de Proxmox.",
"Unable to determine the installed memory.": "Impossible de déterminer la mémoire installée.",
"Understand the security implications of privileged containers": "Comprendre les implications de sécurité des conteneurs privilégiés",
"Uninstall Coral drivers and configuration": "Désinstaller les pilotes et la configuration Coral",
"Uninstall Fail2Ban": "Désinstaller Fail2Ban",
@@ -4564,6 +4591,7 @@
"Updating cluster certificates...": "Mise à jour des certificats de cluster...",
"Updating initramfs (this may take a minute)...": "Mise à jour d'initramfs (cela peut prendre une minute)...",
"Updating initramfs for all kernels...": "Mise à jour d'initramfs pour tous les noyaux...",
"Updating initramfs so the ARC cap applies at next boot...": "Mise à jour d'initramfs pour que le plafond ARC s'applique au prochain démarrage...",
"Updating initramfs, GRUB, and EFI boot, patience...": "Mise à jour d'initramfs, GRUB et boot EFI, patience...",
"Updating journald to store info-level messages...": "Mise à jour de journald pour stocker les messages au niveau des informations...",
"Updating kernel panic configuration...": "Mise à jour de la configuration de panique du noyau...",
@@ -4818,6 +4846,7 @@
"You can add servers manually.": "Vous pouvez ajouter des serveurs manuellement.",
"You can enter the export path manually.": "Vous pouvez saisir le chemin d'exportation manuellement.",
"You can enter the share name manually.": "Vous pouvez saisir le nom de partage manuellement.",
"You can keep using ProxMenux from this terminal.": "Vous pouvez continuer à utiliser ProxMenux depuis ce terminal.",
"You can now monitor your AMD GPU using:": "Vous pouvez désormais surveiller votre GPU AMD en utilisant :",
"You can now monitor your Intel GPU using:": "Vous pouvez désormais surveiller votre GPU Intel en utilisant :",
"You can now select Controller/NVMe devices in Storage Plan.": "Vous pouvez désormais sélectionner les périphériques Controller/NVMe dans le plan de stockage.",
@@ -4839,8 +4868,7 @@
"You should now be able to access the Proxmox web interface.": "Vous devriez maintenant pouvoir accéder à l'interface Web de Proxmox.",
"You will need a Tailscale auth key from: https://login.tailscale.com/admin/settings/keys": "Vous aurez besoin d'une clé d'authentification Tailscale provenant de : https://login.tailscale.com/admin/settings/keys",
"ZFS ARC config removed (kernel defaults will apply on reboot)": "Configuration ZFS ARC supprimée (les valeurs par défaut du noyau s'appliqueront au redémarrage)",
"ZFS ARC configuration file created/updated successfully": "Fichier de configuration ZFS ARC créé/mis à jour avec succès",
"ZFS ARC configuration is up to date": "La configuration de ZFS ARC est à jour",
"ZFS ARC maximum configured:": "ZFS ARC maximum configuré :",
"ZFS ARC optimization completed": "Optimisation ZFS ARC terminée",
"ZFS Management Commands": "Commandes de gestion ZFS",
"ZFS Pool Name": "Nom du pool ZFS",
@@ -4921,6 +4949,8 @@
"did not become ready. Skipping.": "n'est pas devenu prêt. Saut.",
"disk(s) added to CT": "disque(s) ajouté(s) à CT",
"disk(s) added to VM": "disque(s) ajouté(s) à la VM",
"disks present": "disques présents",
"dkms autoinstall did not activate:": "l'installation automatique de dkms n'a pas été activée :",
"dkms.conf generated.": "dkms.conf généré.",
"does not exist on this host. Path not added.": "n'existe pas sur cet hôte. Chemin non ajouté.",
"does not exist. Exiting.": "n'existe pas. Sortir.",
@@ -5039,6 +5069,7 @@
"kexec-tools and related settings removed": "kexec-tools et paramètres associés supprimés",
"kexec-tools installed successfully": "kexec-tools installé avec succès",
"kexec-tools is not installed or already removed.": "kexec-tools n'est pas installé ou déjà supprimé.",
"legacy .link file(s) to the ProxMenux-managed format": "les anciens fichiers .link au format géré par ProxMenux",
"log2ram completely removed from system": "log2ram complètement supprimé du système",
"manually inside the container before starting it.": "manuellement à l'intérieur du conteneur avant de le démarrer.",
"manually inside the container.": "manuellement à l'intérieur du conteneur.",
@@ -5114,6 +5145,7 @@
"remove the (now-empty) directory if possible": "supprimez le répertoire (maintenant vide) si possible",
"removed from Proxmox": "supprimé de Proxmox",
"removed successfully from Proxmox.": "supprimé avec succès de Proxmox.",
"requires running the official installer, which restarts the Monitor service.": "nécessite l'exécution du programme d'installation officiel, qui redémarre le service Monitor.",
"requires the package": "nécessite le paquet",
"restarted successfully": "redémarré avec succès",
"restoring /etc/network would lose connectivity": "la restauration de /etc/network perdrait la connectivité",
@@ -5143,6 +5175,7 @@
"sources.list update skipped (no change)": "mise à jour de sources.list ignorée (aucun changement)",
"sources.list updated to Trixie": "sources.list mis à jour vers Trixie",
"ssh-keygen failed. Cannot create a new SSH key.": "ssh-keygen a échoué. Impossible de créer une nouvelle clé SSH.",
"stale entry/entries for interfaces no longer present": "les entrées obsolètes pour les interfaces ne sont plus présentes",
"standard performance": "performances standards",
"start/restart failures and reset instability.": "échecs de démarrage/redémarrage et réinitialisation de l'instabilité.",
"started successfully.": "démarré avec succès.",
+45 -12
View File
@@ -43,6 +43,8 @@
"A host reboot is required before starting the VM. Reboot now?": "È necessario il riavvio dell'host prima di avviare la VM. Riavviare adesso?",
"A job with this ID already exists.": "Esiste già un lavoro con questo ID.",
"A keyfile is installed at:": "un file di chiavi è installato in:",
"A new ProxMenux version is available:": "È disponibile una nuova versione di ProxMenux:",
"A new kernel is staged for the next boot:": "viene messo in scena un nuovo kernel per il prossimo avvio:",
"A newer version is available:": "È disponibile una versione più recente:",
"A reboot is required after installation to load the new kernel modules.": "Dopo l'installazione è necessario un riavvio per caricare i nuovi moduli del kernel.",
"A reboot is required for VFIO binding to take effect. Do you want to restart now?": "È necessario un riavvio affinché l'associazione VFIO abbia effetto. Vuoi riavviare adesso?",
@@ -287,6 +289,7 @@
"Authorized": "Autorizzato",
"Auto-detected firewall backend (nftables/iptables)": "Backend firewall rilevato automaticamente (nftables/iptables)",
"Auto-discover servers on network": "Individuazione automatica dei server sulla rete",
"Auto-importing ZFS data pools from backup...": "importazione automatica dei pool di dati ZFS dal backup...",
"Auto-negotiate:": "Negoziazione automatica:",
"Auto-start was skipped because GPU passthrough setup was requested.": "L'avvio automatico è stato saltato perché è stata richiesta la configurazione del passthrough della GPU.",
"Auto-sync enabled when /var/log exceeds 80% of": "Sincronizzazione automatica abilitata quando /var/log supera l'80% di",
@@ -480,7 +483,6 @@
"Change Language": "Cambia lingua",
"Change Release Channel": "Cambia canale di rilascio",
"Changes applied. A system reboot is recommended for them to take full effect.": "Modifiche applicate. Si consiglia di riavviare il sistema affinché abbiano pieno effetto.",
"Changes detected. Updating ZFS ARC configuration...": "Modifiche rilevate. Aggiornamento della configurazione ZFS ARC in corso...",
"Changes have been applied to the configuration file.": "Le modifiche sono state applicate al file di configurazione.",
"Changes will apply after reboot.": "Le modifiche verranno applicate dopo il riavvio.",
"Changing Release Channel": "Modifica del canale di rilascio",
@@ -515,7 +517,6 @@
"Checking ZFS autotrim configuration...": "Controllo della configurazione del taglio automatico ZFS in corso...",
"Checking and repairing old LVM PV headers (if needed)...": "Controllo e riparazione dei vecchi collettori FV LVM (se necessario)...",
"Checking conflicting drivers blacklist...": "Controllo della lista nera dei driver in conflitto...",
"Checking existing ZFS ARC configuration...": "Controllo della configurazione ZFS ARC esistente...",
"Checking for updates...": "Controllo aggiornamenti...",
"Checking free space in /var/cache/apt/archives...": "Controllo dello spazio libero in /var/cache/apt/archives...",
"Checking if system disk is SSD or M.2...": "Verifica se il disco di sistema è SSD o M.2...",
@@ -972,7 +973,6 @@
"Creating export archive...": "creazione dell'archivio di esportazione in corso...",
"Creating local archive...": "creazione dell'archivio locale...",
"Creating mount point...": "Creazione del punto di montaggio...",
"Creating new ZFS ARC configuration...": "Creazione nuova configurazione ZFS ARC...",
"Creating partition table and partition...": "Creazione della tabella delle partizioni e della partizione in corso...",
"Creating partition...": "Creazione della partizione...",
"Creating pigz wrapper script...": "Creazione dello script wrapper pigz in corso...",
@@ -1050,6 +1050,9 @@
"DKMS add failed. Check": "Aggiunta DKMS non riuscita. Controllo",
"DKMS build failed.": "Creazione DKMS non riuscita.",
"DKMS build failed. Last lines of make.log:": "Creazione DKMS non riuscita. Ultime righe di make.log:",
"DKMS driver rebuild": "ricostruzione del driver DKMS",
"DKMS drivers rebuilt for kernel": "driver DKMS ricostruiti per il kernel",
"DKMS drivers reinstalled for kernel": "driver DKMS reinstallati per il kernel",
"DKMS install failed.": "Installazione DKMS non riuscita.",
"DKMS module registered.": "Modulo DKMS registrato.",
"DNS Resolution": "Risoluzione DNS",
@@ -1603,10 +1606,8 @@
"Failed to create partition table on disk": "Impossibile creare la tabella delle partizioni sul disco",
"Failed to create partition.": "Impossibile creare la partizione.",
"Failed to create temporary directory:": "Impossibile creare la directory temporanea:",
"Failed to create/update ZFS ARC configuration file": "Impossibile creare/aggiornare il file di configurazione ZFS ARC",
"Failed to destroy LXC:": "Impossibile distruggere LXC:",
"Failed to destroy VM:": "Impossibile distruggere la VM:",
"Failed to detect RAM size. Using default value of 16GB for ZFS ARC optimization.": "Impossibile rilevare la dimensione della RAM. Utilizzo del valore predefinito di 16 GB per l'ottimizzazione ZFS ARC.",
"Failed to detect installed NVIDIA driver version.": "Impossibile rilevare la versione del driver NVIDIA installata.",
"Failed to detect partition on disk": "Impossibile rilevare la partizione sul disco",
"Failed to determine timezone from IP address - keeping current timezone settings": "Impossibile determinare il fuso orario dall'indirizzo IP: mantenendo le impostazioni attuali del fuso orario",
@@ -1697,12 +1698,14 @@
"Failed to update amdgpu_top": "Impossibile aggiornare amdgpu_top",
"Failed to update auth.json — restoring backup.": "Impossibile aggiornare auth.json: ripristino del backup.",
"Failed to update cluster certificates (might not be in a cluster)": "Impossibile aggiornare i certificati del cluster (potrebbe non trovarsi in un cluster)",
"Failed to update initramfs.": "Messaggio tecnico per Proxmox e IT.Traduzione: impossibile aggiornare initramfs.",
"Failed to update intel-gpu-tools": "Impossibile aggiornare Intel-GPU-Tools",
"Failed to update package list.": "Impossibile aggiornare l'elenco dei pacchetti.",
"Failed to update package lists": "Impossibile aggiornare gli elenchi dei pacchetti",
"Failed to write": "Impossibile scrivere",
"Failed transitions from D3cold to D0": "Transizioni non riuscite da D3cold a D0",
"Failed. See log:": "Fallito. Vedi registro:",
"Falling back to each installer with --auto-reinstall...": "ricorrere a ciascun programma di installazione con --auto-reinstall...",
"Falling back to manual paste mode.": "ritorno alla modalità incolla manuale.",
"Fastfetch Logo Selection": "Selezione logo Fastfetch",
"Fastfetch configuration updated": "Configurazione fastfetch aggiornata",
@@ -2081,6 +2084,8 @@
"Import failed for:": "Importazione non riuscita per:",
"Important: both VMs cannot be running at the same time with the same GPU.": "Importante: entrambe le VM non possono essere eseguite contemporaneamente con la stessa GPU.",
"Important: some GPUs may still fail in passthrough and can affect host stability or overall performance depending on hardware/firmware quality.": "Importante: alcune GPU potrebbero comunque non riuscire a eseguire il passthrough e influire sulla stabilità dell'host o sulle prestazioni generali a seconda della qualità dell'hardware/firmware.",
"Imported (foreign hostid, forced):": "importato (hostid straniero, forzato):",
"Imported:": "Importato:",
"Importing": "Importazione",
"Importing disk": "Importazione del disco",
"Importing:": "Importazione:",
@@ -2216,6 +2221,7 @@
"Installing host drivers while the GPU is assigned to a VM could break passthrough and destabilize the system.": "L'installazione dei driver host mentre la GPU è assegnata a una VM potrebbe interrompere il passthrough e destabilizzare il sistema.",
"Installing iSCSI initiator tools...": "Installazione degli strumenti iniziatori iSCSI in corso...",
"Installing intel-gpu-tools...": "Installazione di Intel-Gpu-Tools...",
"Installing kernel headers:": "Installazione degli header del kernel:",
"Installing kexec-tools...": "Installazione di kexec-tools...",
"Installing latest Lynis security scan tool...": "Installazione dell'ultimo strumento di scansione di sicurezza di Lynis in corso...",
"Installing packages...": "Installazione dei pacchetti...",
@@ -2307,6 +2313,8 @@
"Kept sharedfiles group (has regular users assigned).": "Mantenuto il gruppo sharedfiles (ha utenti regolari assegnati).",
"Kernel and architecture info": "Informazioni sul kernel e sull'architettura",
"Kernel headers and build tools verified.": "Intestazioni del kernel e strumenti di creazione verificati.",
"Kernel headers install failed — DKMS rebuild will likely fail:": "installazione delle intestazioni del kernel non riuscita: la ricostruzione DKMS probabilmente fallirà:",
"Kernel headers installed": "intestazioni del kernel installate",
"Kernel max Key limit configured": "Limite massimo della chiave del kernel configurato",
"Kernel panic behavior configuration completed": "Configurazione del comportamento di panico del kernel completata",
"Kernel panic configuration removed": "Configurazione Kernel Panic rimossa",
@@ -2484,6 +2492,7 @@
"Method:": "Metodo:",
"Migrate VMs away from node being upgraded": "Migrare le VM dal nodo in fase di aggiornamento",
"Migrate away any guests that must keep running": "Migrare tutti gli ospiti che devono continuare a funzionare",
"Migrated": "Migrato",
"Migrated legacy ProxMenux NVIDIA blacklist state — module will reload after reboot": "Stato della lista nera NVIDIA ProxMenux legacy migrato: il modulo verrà ricaricato dopo il riavvio",
"Mirror URL not available for this script.": "URL mirror non disponibile per questo script.",
"Missing": "Mancante",
@@ -2730,6 +2739,7 @@
"New Virtual Machine": "Nuova macchina virtuale",
"New backup job": "Nuovo lavoro di backup",
"New backups on this host will be unencrypted until a new keyfile is set up.": "i nuovi backup su questo host non saranno crittografati finché non verrà impostato un nuovo file di chiavi.",
"New kernel staged; rebuilding DKMS drivers:": "nuovo kernel messo in scena;ricostruzione dei driver DKMS:",
"New mount options to apply:": "Nuove opzioni di montaggio da applicare:",
"New scheduled job (own timer + retention)": "Nuovo lavoro pianificato (timer personale + conservazione)",
"New version available": "Nuova versione disponibile",
@@ -2738,7 +2748,6 @@
"Next Steps:": "Passaggi successivi:",
"Next step: stop that VM first, then run": "Passaggio successivo: arresta prima la VM, quindi eseguila",
"No": "NO",
"No .link files found in": "Nessun file .link trovato in",
"No .ova or .ovf files found in:": "Nessun file .ova o .ovf trovato in:",
"No .ovf descriptor found inside OVA.": "Nessun descrittore .ovf trovato in OVA.",
"No .pxar archives were found in this backup:": "in questo backup non è stato trovato alcun archivio .pxar:",
@@ -2802,6 +2811,7 @@
"No PVs with old headers found.": "Nessun PV con intestazioni vecchie trovato.",
"No ProxMenux ZFS autotrim state file found.": "Nessun file di stato di taglio automatico ProxMenux ZFS trovato.",
"No ProxMenux host-backup archives were found in:": "Nessun archivio di backup host ProxMenux trovato in:",
"No ProxMenux-managed .link files found — nothing to remove.": "nessun file .link gestito da ProxMenux trovato: niente da rimuovere.",
"No Recent Servers": "Nessun server recente",
"No Samba mounts found.": "Nessun supporto Samba trovato.",
"No Samba ports found": "Nessuna porta Samba trovata",
@@ -2828,6 +2838,7 @@
"No VirtIO ISO found. Please download one.": "Nessuna ISO VirtIO trovata. Per favore scaricane uno.",
"No VirtIO ISO selected. Please choose again.": "Nessun ISO VirtIO selezionato. Per favore scegli di nuovo.",
"No Virtual Machines found on this system.": "Nessuna macchina virtuale trovata su questo sistema.",
"No ZFS pools detected. Skipping ZFS ARC optimization.": "nessun pool ZFS rilevato.Saltare l'ottimizzazione ZFS ARC.",
"No ZFS pools detected. Skipping ZFS autotrim.": "Nessun pool ZFS rilevato. Saltare l'autotrim ZFS.",
"No accessible": "Non accessibile",
"No accessible NFS servers found.": "Nessun server NFS accessibile trovato.",
@@ -3056,7 +3067,7 @@
"Optimize ZFS ARC size": "ottimizza le dimensioni ZFS ARC",
"Optimize journald": "ottimizza journald",
"Optimize logrotate": "ottimizza logrotate",
"Optimizing ZFS ARC size according to available memory...": "Ottimizzazione delle dimensioni di ZFS ARC in base alla memoria disponibile...",
"Optimizing ZFS ARC maximum size...": "Ottimizzazione della dimensione massima di ZFS ARC...",
"Optimizing logrotate configuration...": "Ottimizzazione della configurazione di logrotate...",
"Optimizing memory settings...": "Ottimizzazione delle impostazioni della memoria...",
"Optimizing network settings...": "Ottimizzazione delle impostazioni di rete...",
@@ -3082,6 +3093,7 @@
"Owner:": "Proprietario:",
"Ownership set to root:sharedfiles with 2775 on:": "Proprietà impostata su root:sharedfiles con 2775 su:",
"PAM limits configured": "Limiti PAM configurati",
"PBS API log rotation configured (hourly, size-based)": "rotazione del log API PBS configurata (oraria, in base alle dimensioni)",
"PBS backup error log": "Registro degli errori del backup PBS",
"PBS backup failed.": "Il backup PBS non è riuscito.",
"PBS encryption keyfile (show / replace / remove)": "file chiave di crittografia PBS (mostra/sostituisci/rimuovi)",
@@ -3299,6 +3311,7 @@
"ProxMenux logo applied": "Logo ProxMenux applicato",
"ProxMenux only acts as a launcher — once the script starts, control leaves ProxMenux.": "ProxMenux funge solo da launcher: una volta avviato lo script, il controllo lascia ProxMenux.",
"ProxMenux saved it locally at:": "ProxMenux lo ha salvato localmente in:",
"ProxMenux-managed .link file(s). User-authored .link files were left in place.": "file .link gestiti da ProxMenux.I file .link creati dall'utente sono stati lasciati al loro posto.",
"Proxmology logo applied": "Logo Proxmology applicato",
"Proxmox 9 system update allready": "Già l'aggiornamento del sistema Proxmox 9",
"Proxmox APT repositories configured": "repository APT Proxmox configurati",
@@ -3313,6 +3326,7 @@
"Proxmox VE 9.x no-subscription repository created": "Repository senza abbonamento Proxmox VE 9.x creato",
"Proxmox VE Helper Scripts": "Script di supporto Proxmox VE",
"Proxmox VE configuration completed.": "Configurazione Proxmox VE completata.",
"Proxmox VE safe update completed": "Aggiornamento sicuro di Proxmox VE completato",
"Proxmox auth logger service created and started": "Servizio di registrazione di autenticazione Proxmox creato e avviato",
"Proxmox enterprise repo might be missing or inaccessible. Trying to switch to no-subscription...": "Il repository aziendale Proxmox potrebbe essere mancante o inaccessibile. Tentativo di passare alla modalità senza abbonamento...",
"Proxmox filter configured": "Filtro Proxmox configurato",
@@ -3326,7 +3340,6 @@
"Proxmox storages:": "Memorie Proxmox:",
"Proxmox system repair completed successfully!": "Riparazione del sistema Proxmox completata con successo!",
"Proxmox system repair completed with some issues.": "Riparazione del sistema Proxmox completata con alcuni problemi.",
"Proxmox system update": "Aggiornamento del sistema Proxmox",
"Proxmox web interface protection": "Protezione dell'interfaccia web Proxmox",
"Proxmox web interface: Datacenter > Storage > Add > Directory": "Interfaccia web Proxmox: Datacenter > Archiviazione > Aggiungi > Directory",
"Proxmox web interface: Datacenter > Storage > Add > NFS": "Interfaccia web Proxmox: Datacenter > Archiviazione > Aggiungi > NFS",
@@ -3396,6 +3409,7 @@
"Recommended: schedule these paths for next boot to avoid immediate SSH disconnection.": "Consigliato: pianifica questi percorsi per il prossimo avvio per evitare la disconnessione SSH immediata.",
"Recommended: use GPU -> LXC mode for these devices.": "Consigliato: utilizzare GPU -> modalità LXC per questi dispositivi.",
"Recommended: use GPU with LXC workloads instead of VM passthrough on this hardware.": "Consigliato: utilizzare la GPU con carichi di lavoro LXC invece del passthrough VM su questo hardware.",
"Reconciled": "riconciliato",
"Recover the keyfile using your recovery passphrase?": "Recuperare il file di chiavi utilizzando la passphrase di ripristino?",
"Recoverable:": "Recuperabile:",
"Recovery blob upload failed — main backup is OK, but keyfile recovery from PBS will not be available for this backup.": "caricamento del BLOB di ripristino non riuscito: il backup principale è OK, ma il ripristino del file di chiavi da PBS non sarà disponibile per questo backup.",
@@ -3420,6 +3434,7 @@
"Reinstall/Update NVIDIA drivers": "Reinstallare/aggiornare i driver NVIDIA",
"Reinstalled": "Reinstallato",
"Reinstalled Proxmox packages successfully": "Pacchetti Proxmox reinstallati correttamente",
"Reinstalling": "Reinstallazione",
"Reinstalling core Proxmox packages...": "Reinstallazione dei pacchetti Proxmox principali in corso...",
"Release Channel": "Canale di rilascio",
"Release channel set to Beta.": "Canale di rilascio impostato su Beta.",
@@ -3480,7 +3495,6 @@
"Removed": "RIMOSSO",
"Removed KVM MSR options from configuration": "Rimosse le opzioni KVM MSR dalla configurazione",
"Removed Mount:": "Supporto rimosso:",
"Removed all .link files from": "Rimossi tutti i file .link da",
"Removed bwlimit/ionice tuning (no .bak found)": "Rimosso il tuning bwlimit/ionice (nessun .bak trovato)",
"Removed configurations for": "Configurazioni rimosse per",
"Removed credentials file:": "File delle credenziali rimosso:",
@@ -3503,8 +3517,8 @@
"Removing NVIDIA packages...": "Rimozione dei pacchetti NVIDIA...",
"Removing OVH RTM...": "Rimozione dell'RTM OVH...",
"Removing OpenVSwitch...": "Rimozione di OpenVSwitch...",
"Removing ProxMenux persistent NIC .link files...": "Rimozione dei file .link NIC persistenti di ProxMenux...",
"Removing VFIO ownership for selected GPU(s)...": "Rimozione della proprietà VFIO per le GPU selezionate in corso...",
"Removing all .link files from": "Rimozione di tutti i file .link da",
"Removing any pre-existing gasket-dkms package...": "Rimozione dell'eventuale pacchetto guarnizioni-dkms preesistente...",
"Removing conflicting utilities...": "Rimozione delle utilità in conflitto...",
"Removing entropy generation optimization...": "Rimozione dell'ottimizzazione della generazione di entropia in corso...",
@@ -3640,6 +3654,8 @@
"Run mode: Unattended": "Modalità di esecuzione: automatica",
"Run security audit now": "Esegui subito il controllo di sicurezza",
"Run the following inside the VM:": "Esegui quanto segue all'interno della VM:",
"Run the release-channel switch from an SSH session or the Proxmox host console with:": "esegui lo switch del canale di rilascio da una sessione SSH o dalla console host Proxmox con:",
"Run the update from an SSH session or the Proxmox host console with:": "esegui l'aggiornamento da una sessione SSH o dalla console host Proxmox con:",
"Run upgrade checklist script:": "Esegui lo script dell'elenco di controllo dell'aggiornamento:",
"Running": "Corsa",
"Running Lynis security audit...": "Esecuzione del controllo di sicurezza di Lynis in corso...",
@@ -3648,6 +3664,7 @@
"Running VM detected": "Rilevata VM in esecuzione",
"Running backup job:": "Esecuzione del processo di backup:",
"Running containers detected": "Contenitori in esecuzione rilevati",
"Running dkms autoinstall for kernel": "esecuzione dell'installazione automatica di dkms per il kernel",
"Running pre-upgrade simulation to verify 'proxmox-ve' will remain installed...": "Esecuzione della simulazione pre-aggiornamento per verificare che 'proxmox-ve' rimanga installato...",
"SATA (standard - high compatibility)": "SATA (standard - alta compatibilità)",
"SCSI (recommended for Linux and Windows)": "SCSI (consigliato per Linux e Windows)",
@@ -4049,6 +4066,8 @@
"Skip this step if using no-subscription repository": "Salta questo passaggio se utilizzi un repository senza abbonamento",
"Skip — I will add it as PCIe device": "Salta: lo aggiungerò come dispositivo PCIe",
"Skip — leave as-is": "Salta: lascia così com'è",
"Skipped (no disks of the pool are present on this host):": "Messaggio tecnico per Proxmox e IT.Traduzione: saltato (nessun disco del pool è presente su questo host):",
"Skipped (some disks missing):": "saltato (alcuni dischi mancanti):",
"Skipped device": "Dispositivo saltato",
"Skipped to protect target system (would cascade-remove packages)": "Messaggio tecnico per Proxmox e IT.Traduzione: saltato per proteggere il sistema di destinazione (rimuoverebbe i pacchetti a cascata)",
"Skipped, not in apt cache:": "Saltato, non nella cache di apt:",
@@ -4183,6 +4202,7 @@
"Switch to GPU -> LXC (native driver mode)": "Passa a GPU -> LXC (modalità driver nativo)",
"Switch to GPU -> VM (VFIO passthrough mode)": "Passa a GPU -> VM (modalità passthrough VFIO)",
"Switches to the free no-subscription repository": "Passa al repository gratuito senza abbonamento",
"Switching to": "passaggio a",
"Switching to GPU -> LXC mode removes VFIO exclusivity.": "Il passaggio a GPU -> modalità LXC rimuove l'esclusività VFIO.",
"Switching to GPU -> VM mode requires exclusive VFIO binding.": "Il passaggio alla modalità GPU -> VM richiede l'associazione VFIO esclusiva.",
"Synchronize time automatically": "sincronizza l'ora automaticamente",
@@ -4265,11 +4285,13 @@
"The disk": "Il disco",
"The file does not exist, is empty or is not readable.": "il file non esiste, è vuoto o non è leggibile.",
"The filesystem": "Il file system",
"The following DKMS-managed drivers will now be rebuilt against it so they keep working after reboot:": "i seguenti driver gestiti da DKMS verranno ora ricostruiti in modo che continuino a funzionare dopo il riavvio:",
"The following LXC containers have NVIDIA passthrough configured:": "I seguenti contenitori LXC hanno il passthrough NVIDIA configurato:",
"The following backup paths are kernel-tied and are excluded from the picker to keep the target's boot safe. The operator's own tuning inside these paths (IOMMU cmdline, VFIO IDs, custom quirks) is merged back automatically via kernel-agnostic merge:": "i seguenti percorsi di backup sono legati al kernel e sono esclusi dal selettore per mantenere sicuro l'avvio della destinazione.L'ottimizzazione dell'operatore all'interno di questi percorsi (linea cmd IOMMU, ID VFIO, stranezze personalizzate) viene riunita automaticamente tramite unione indipendente dal kernel:",
"The following changes will be applied": "Verranno applicate le seguenti modifiche",
"The following devices were excluded because they are part of an SR-IOV configuration:": "I seguenti dispositivi sono stati esclusi perché fanno parte di una configurazione SR-IOV:",
"The following devices were excluded from Controller/NVMe passthrough because they are part of an SR-IOV configuration:": "I seguenti dispositivi sono stati esclusi dal passthrough Controller/NVMe perché fanno parte di una configurazione SR-IOV:",
"The following drivers could not be rebuilt for the new kernel — run their installer manually after reboot:": "non è stato possibile ricostruire i seguenti driver per il nuovo kernel: esegui manualmente il programma di installazione dopo il riavvio:",
"The following entries exist on the host but were NOT in the backup. To make the host EXACTLY match the backup state, they must be removed:": "le seguenti voci esistono sull'host ma NON erano nel backup.Per fare in modo che l'host corrisponda ESATTAMENTE allo stato del backup, è necessario rimuoverli:",
"The following selected GPU(s) are currently in GPU -> VM mode (vfio-pci):": "Le seguenti GPU selezionate sono attualmente in modalità GPU -> VM (vfio-pci):",
"The following selected device(s) are Physical Functions with active Virtual Functions:": "I seguenti dispositivi selezionati sono funzioni fisiche con funzioni virtuali attive:",
@@ -4368,6 +4390,7 @@
"This is unexpected since credentials were validated.": "Ciò è inaspettato poiché le credenziali sono state convalidate.",
"This marks the container as unprivileged": "Ciò contrassegna il contenitore come non privilegiato",
"This may be normal for a fresh installation": "Questo potrebbe essere normale per una nuova installazione",
"This may take a few minutes. Press OK to proceed.": "l'operazione potrebbe richiedere alcuni minuti.Premere OK per procedere.",
"This may take a few seconds...": "L'operazione potrebbe richiedere alcuni secondi...",
"This may take several minutes...": "L'operazione potrebbe richiedere diversi minuti...",
"This means Proxmox handles mount lifecycle natively (no manual /etc/fstab needed for NFS/CIFS host storages).": "Ciò significa che Proxmox gestisce il ciclo di vita del montaggio in modo nativo (non è necessario il manuale /etc/fstab per gli archivi host NFS/CIFS).",
@@ -4388,6 +4411,8 @@
"This script must be run on a Proxmox host.": "Questo script deve essere eseguito su un host Proxmox.",
"This script will apply the following optimizations and advanced adjustments to your Proxmox VE server": "Questo script applicherà le seguenti ottimizzazioni e regolazioni avanzate al tuo server Proxmox VE",
"This script will update your Proxmox VE system with advanced options:": "Questo script aggiornerà il tuo sistema Proxmox VE con opzioni avanzate:",
"This session is running in the Monitor terminal. Running it from here would cut the connection mid-install and leave the switch in a broken state.": "questa sessione è in esecuzione nel terminale Monitor.Eseguirlo da qui interromperebbe la connessione a metà installazione e lascerebbe l'interruttore in uno stato interrotto.",
"This session is running in the Monitor terminal. Updating from here would restart the Monitor service and cut the connection mid-install, leaving the update in a broken state.": "questa sessione è in esecuzione nel terminale Monitor.L'aggiornamento da qui riavvierebbe il servizio Monitor e interromperebbe la connessione durante l'installazione, lasciando l'aggiornamento in uno stato interrotto.",
"This shows the storage type and disk identifier": "Mostra il tipo di archiviazione e l'identificatore del disco",
"This state has a high probability of VM startup/reset failures.": "Questo stato ha un'alta probabilità di errori di avvio/reimpostazione della VM.",
"This state indicates a high risk of passthrough failure due to": "Questo stato indica un rischio elevato di errore passthrough dovuto a",
@@ -4475,7 +4500,9 @@
"Udev rules for Coral USB devices already exist.": "Esistono già regole Udev per i dispositivi USB Coral.",
"Udev rules for Coral USB devices appended and rules reloaded.": "Aggiunte regole Udev per i dispositivi USB Coral e ricaricate.",
"Umbrel OS installer script by Helper Scripts\n\nVisit the GitHub repo to learn more, contribute, or support the project:\n\nhttps://community-scripts.github.io/ProxmoxVE/scripts?id=umbrel-os-vm": "Script di installazione del sistema operativo Umbrel di Helper Scripts\n\nVisita il repository GitHub per saperne di più, contribuire o supportare il progetto:\n\nhttps://community-scripts.github.io/ProxmoxVE/scripts?id=umbrel-os-vm",
"Unable to detect Proxmox version": "impossibile rilevare la versione di Proxmox",
"Unable to detect Proxmox version.": "Impossibile rilevare la versione di Proxmox.",
"Unable to determine the installed memory.": "impossibile determinare la memoria installata.",
"Understand the security implications of privileged containers": "Comprendere le implicazioni sulla sicurezza dei contenitori privilegiati",
"Uninstall Coral drivers and configuration": "Disinstallare i driver e la configurazione Coral",
"Uninstall Fail2Ban": "Disinstallare Fail2Ban",
@@ -4564,6 +4591,7 @@
"Updating cluster certificates...": "Aggiornamento dei certificati cluster in corso...",
"Updating initramfs (this may take a minute)...": "Aggiornamento di initramfs (l'operazione potrebbe richiedere un minuto)...",
"Updating initramfs for all kernels...": "Aggiornamento di initramfs per tutti i kernel...",
"Updating initramfs so the ARC cap applies at next boot...": "aggiornamento di initramfs in modo che il limite ARC si applichi al prossimo avvio...",
"Updating initramfs, GRUB, and EFI boot, patience...": "Aggiornamento di initramfs, GRUB e avvio EFI, pazienza...",
"Updating journald to store info-level messages...": "Aggiornamento journald per archiviare messaggi a livello di informazioni in corso...",
"Updating kernel panic configuration...": "Aggiornamento della configurazione del kernel panico in corso...",
@@ -4818,6 +4846,7 @@
"You can add servers manually.": "È possibile aggiungere server manualmente.",
"You can enter the export path manually.": "È possibile inserire manualmente il percorso di esportazione.",
"You can enter the share name manually.": "È possibile immettere manualmente il nome della condivisione.",
"You can keep using ProxMenux from this terminal.": "puoi continuare a utilizzare ProxMenux da questo terminale.",
"You can now monitor your AMD GPU using:": "Ora puoi monitorare la tua GPU AMD utilizzando:",
"You can now monitor your Intel GPU using:": "Ora puoi monitorare la tua GPU Intel utilizzando:",
"You can now select Controller/NVMe devices in Storage Plan.": "Ora puoi selezionare i dispositivi Controller/NVMe nel Piano di archiviazione.",
@@ -4839,8 +4868,7 @@
"You should now be able to access the Proxmox web interface.": "Ora dovresti essere in grado di accedere all'interfaccia web di Proxmox.",
"You will need a Tailscale auth key from: https://login.tailscale.com/admin/settings/keys": "Avrai bisogno di una chiave di autenticazione Tailscale da: https://login.tailscale.com/admin/settings/keys",
"ZFS ARC config removed (kernel defaults will apply on reboot)": "Configurazione ZFS ARC rimossa (le impostazioni predefinite del kernel verranno applicate al riavvio)",
"ZFS ARC configuration file created/updated successfully": "File di configurazione ZFS ARC creato/aggiornato correttamente",
"ZFS ARC configuration is up to date": "La configurazione di ZFS ARC è aggiornata",
"ZFS ARC maximum configured:": "ZFS ARC massimo configurato:",
"ZFS ARC optimization completed": "Ottimizzazione ZFS ARC completata",
"ZFS Management Commands": "Comandi di gestione ZFS",
"ZFS Pool Name": "Nome del pool ZFS",
@@ -4921,6 +4949,8 @@
"did not become ready. Skipping.": "non è diventato pronto. Saltare.",
"disk(s) added to CT": "disco(i) aggiunto(i) a CT",
"disk(s) added to VM": "disco/i aggiunto/i alla VM",
"disks present": "dischi presenti",
"dkms autoinstall did not activate:": "l'installazione automatica di dkms non è stata attivata:",
"dkms.conf generated.": "dkms.conf generato.",
"does not exist on this host. Path not added.": "non esiste su questo host. Percorso non aggiunto.",
"does not exist. Exiting.": "non esiste. In uscita.",
@@ -5039,6 +5069,7 @@
"kexec-tools and related settings removed": "kexec-tools e impostazioni correlate rimossi",
"kexec-tools installed successfully": "kexec-tools è stato installato correttamente",
"kexec-tools is not installed or already removed.": "kexec-tools non è installato o è già stato rimosso.",
"legacy .link file(s) to the ProxMenux-managed format": "file .link legacy nel formato gestito da ProxMenux",
"log2ram completely removed from system": "log2ram completamente rimosso dal sistema",
"manually inside the container before starting it.": "manualmente all'interno del contenitore prima di avviarlo.",
"manually inside the container.": "manualmente all'interno del contenitore.",
@@ -5114,6 +5145,7 @@
"remove the (now-empty) directory if possible": "rimuovere la directory (ora vuota) se possibile",
"removed from Proxmox": "rimosso da Proxmox",
"removed successfully from Proxmox.": "rimosso con successo da Proxmox.",
"requires running the official installer, which restarts the Monitor service.": "richiede l'esecuzione del programma di installazione ufficiale, che riavvia il servizio Monitor.",
"requires the package": "richiede il pacchetto",
"restarted successfully": "riavviato con successo",
"restoring /etc/network would lose connectivity": "il ripristino di /etc/network perderebbe la connettività",
@@ -5143,6 +5175,7 @@
"sources.list update skipped (no change)": "Aggiornamento di source.list saltato (nessuna modifica)",
"sources.list updated to Trixie": "source.list aggiornato a Trixie",
"ssh-keygen failed. Cannot create a new SSH key.": "ssh-keygen non è riuscito. Impossibile creare una nuova chiave SSH.",
"stale entry/entries for interfaces no longer present": "voce/voci obsolete per le interfacce non più presenti",
"standard performance": "prestazione standard",
"start/restart failures and reset instability.": "errori di avvio/riavvio e ripristino di instabilità.",
"started successfully.": "iniziato con successo.",
+45 -12
View File
@@ -43,6 +43,8 @@
"A host reboot is required before starting the VM. Reboot now?": "É necessária uma reinicialização do host antes de iniciar a VM. Reiniciar agora?",
"A job with this ID already exists.": "Já existe um trabalho com este ID.",
"A keyfile is installed at:": "Um arquivo-chave está instalado em:",
"A new ProxMenux version is available:": "Uma nova versão do ProxMenux está disponível:",
"A new kernel is staged for the next boot:": "Um novo kernel está preparado para a próxima inicialização:",
"A newer version is available:": "Uma versão mais recente está disponível:",
"A reboot is required after installation to load the new kernel modules.": "Uma reinicialização é necessária após a instalação para carregar os novos módulos do kernel.",
"A reboot is required for VFIO binding to take effect. Do you want to restart now?": "É necessária uma reinicialização para que a ligação VFIO entre em vigor. Quer reiniciar agora?",
@@ -287,6 +289,7 @@
"Authorized": "Autorizado",
"Auto-detected firewall backend (nftables/iptables)": "Back-end de firewall detectado automaticamente (nftables/iptables)",
"Auto-discover servers on network": "Descoberta automática de servidores na rede",
"Auto-importing ZFS data pools from backup...": "Importação automática de pools de dados ZFS do backup...",
"Auto-negotiate:": "Negociar automaticamente:",
"Auto-start was skipped because GPU passthrough setup was requested.": "A inicialização automática foi ignorada porque a configuração de passagem da GPU foi solicitada.",
"Auto-sync enabled when /var/log exceeds 80% of": "Sincronização automática ativada quando /var/log excede 80% de",
@@ -480,7 +483,6 @@
"Change Language": "Alterar idioma",
"Change Release Channel": "Alterar canal de lançamento",
"Changes applied. A system reboot is recommended for them to take full effect.": "Alterações aplicadas. Recomenda-se uma reinicialização do sistema para que tenham efeito total.",
"Changes detected. Updating ZFS ARC configuration...": "Alterações detectadas. Atualizando configuração do ZFS ARC...",
"Changes have been applied to the configuration file.": "As alterações foram aplicadas ao arquivo de configuração.",
"Changes will apply after reboot.": "As alterações serão aplicadas após a reinicialização.",
"Changing Release Channel": "Alterando o canal de lançamento",
@@ -515,7 +517,6 @@
"Checking ZFS autotrim configuration...": "Verificando a configuração do ajuste automático do ZFS...",
"Checking and repairing old LVM PV headers (if needed)...": "Verificando e reparando coletores LVM PV antigos (se necessário)...",
"Checking conflicting drivers blacklist...": "Verificando a lista negra de drivers conflitantes...",
"Checking existing ZFS ARC configuration...": "Verificando a configuração existente do ZFS ARC...",
"Checking for updates...": "Verificando atualizações...",
"Checking free space in /var/cache/apt/archives...": "Verificando espaço livre em /var/cache/apt/archives...",
"Checking if system disk is SSD or M.2...": "Verificando se o disco do sistema é SSD ou M.2...",
@@ -972,7 +973,6 @@
"Creating export archive...": "Criando arquivo de exportação...",
"Creating local archive...": "Criando arquivo local...",
"Creating mount point...": "Criando ponto de montagem...",
"Creating new ZFS ARC configuration...": "Criando nova configuração ZFS ARC...",
"Creating partition table and partition...": "Criando tabela de partição e partição...",
"Creating partition...": "Criando partição...",
"Creating pigz wrapper script...": "Criando script wrapper pigz...",
@@ -1050,6 +1050,9 @@
"DKMS add failed. Check": "Falha na adição do DKMS. Verificar",
"DKMS build failed.": "Falha na compilação do DKMS.",
"DKMS build failed. Last lines of make.log:": "Falha na compilação do DKMS. Últimas linhas do make.log:",
"DKMS driver rebuild": "reconstrução do driver DKMS",
"DKMS drivers rebuilt for kernel": "Drivers DKMS reconstruídos para kernel",
"DKMS drivers reinstalled for kernel": "Drivers DKMS reinstalados para kernel",
"DKMS install failed.": "Falha na instalação do DKMS.",
"DKMS module registered.": "Módulo DKMS registrado.",
"DNS Resolution": "Resolução DNS",
@@ -1603,10 +1606,8 @@
"Failed to create partition table on disk": "Falha ao criar tabela de partição no disco",
"Failed to create partition.": "Falha ao criar partição.",
"Failed to create temporary directory:": "Falha ao criar diretório temporário:",
"Failed to create/update ZFS ARC configuration file": "Falha ao criar/atualizar arquivo de configuração ARC do ZFS",
"Failed to destroy LXC:": "Falha ao destruir LXC:",
"Failed to destroy VM:": "Falha ao destruir VM:",
"Failed to detect RAM size. Using default value of 16GB for ZFS ARC optimization.": "Falha ao detectar o tamanho da RAM. Usando o valor padrão de 16 GB para otimização ZFS ARC.",
"Failed to detect installed NVIDIA driver version.": "Falha ao detectar a versão do driver NVIDIA instalada.",
"Failed to detect partition on disk": "Falha ao detectar partição no disco",
"Failed to determine timezone from IP address - keeping current timezone settings": "Falha ao determinar o fuso horário a partir do endereço IP mantendo as configurações atuais de fuso horário",
@@ -1697,12 +1698,14 @@
"Failed to update amdgpu_top": "Falha ao atualizar amdgpu_top",
"Failed to update auth.json — restoring backup.": "Falha ao atualizar auth.json restaurando o backup.",
"Failed to update cluster certificates (might not be in a cluster)": "Falha ao atualizar certificados de cluster (pode não estar em um cluster)",
"Failed to update initramfs.": "Falha ao atualizar o initramfs.",
"Failed to update intel-gpu-tools": "Falha ao atualizar ferramentas Intel-GPU",
"Failed to update package list.": "Falha ao atualizar a lista de pacotes.",
"Failed to update package lists": "Falha ao atualizar listas de pacotes",
"Failed to write": "Falha ao escrever",
"Failed transitions from D3cold to D0": "Falha nas transições de D3cold para D0",
"Failed. See log:": "Fracassado. Veja registro:",
"Falling back to each installer with --auto-reinstall...": "recorrendo a cada instalador com --auto-reinstall...",
"Falling back to manual paste mode.": "voltando ao modo de colagem manual.",
"Fastfetch Logo Selection": "Seleção de logotipo Fastfetch",
"Fastfetch configuration updated": "Configuração do Fastfetch atualizada",
@@ -2081,6 +2084,8 @@
"Import failed for:": "Falha na importação para:",
"Important: both VMs cannot be running at the same time with the same GPU.": "Importante: ambas as VMs não podem estar em execução ao mesmo tempo com a mesma GPU.",
"Important: some GPUs may still fail in passthrough and can affect host stability or overall performance depending on hardware/firmware quality.": "Importante: algumas GPUs ainda podem falhar na passagem e afetar a estabilidade do host ou o desempenho geral, dependendo da qualidade do hardware/firmware.",
"Imported (foreign hostid, forced):": "Importado (hostid estrangeiro, forçado):",
"Imported:": "Importado:",
"Importing": "Importando",
"Importing disk": "Importando disco",
"Importing:": "Importando:",
@@ -2216,6 +2221,7 @@
"Installing host drivers while the GPU is assigned to a VM could break passthrough and destabilize the system.": "Instalar drivers de host enquanto a GPU está atribuída a uma VM pode interromper a passagem e desestabilizar o sistema.",
"Installing iSCSI initiator tools...": "Instalando ferramentas do iniciador iSCSI...",
"Installing intel-gpu-tools...": "Instalando ferramentas Intel-GPU...",
"Installing kernel headers:": "Instalando cabeçalhos do kernel:",
"Installing kexec-tools...": "Instalando ferramentas kexec...",
"Installing latest Lynis security scan tool...": "Instalando a ferramenta de verificação de segurança Lynis mais recente...",
"Installing packages...": "Instalando pacotes...",
@@ -2307,6 +2313,8 @@
"Kept sharedfiles group (has regular users assigned).": "Manteve o grupo sharedfiles (tem usuários regulares atribuídos).",
"Kernel and architecture info": "Informações sobre kernel e arquitetura",
"Kernel headers and build tools verified.": "Cabeçalhos de kernel e ferramentas de construção verificadas.",
"Kernel headers install failed — DKMS rebuild will likely fail:": "falha na instalação dos cabeçalhos do kernel - a reconstrução do DKMS provavelmente falhará:",
"Kernel headers installed": "cabeçalhos do kernel instalados",
"Kernel max Key limit configured": "Limite máximo de chaves do kernel configurado",
"Kernel panic behavior configuration completed": "Configuração do comportamento de pânico do kernel concluída",
"Kernel panic configuration removed": "Configuração de pânico do kernel removida",
@@ -2484,6 +2492,7 @@
"Method:": "Método:",
"Migrate VMs away from node being upgraded": "Migrar VMs para fora do nó que está sendo atualizado",
"Migrate away any guests that must keep running": "Migre todos os convidados que precisam continuar em execução",
"Migrated": "Migrado",
"Migrated legacy ProxMenux NVIDIA blacklist state — module will reload after reboot": "Estado de lista negra legado ProxMenux NVIDIA migrado o módulo será recarregado após a reinicialização",
"Mirror URL not available for this script.": "URL espelhado não disponível para este script.",
"Missing": "Ausente",
@@ -2730,6 +2739,7 @@
"New Virtual Machine": "Nova máquina virtual",
"New backup job": "Nova tarefa de backup",
"New backups on this host will be unencrypted until a new keyfile is set up.": "Novos backups neste host serão descriptografados até que um novo arquivo-chave seja configurado.",
"New kernel staged; rebuilding DKMS drivers:": "Novo kernel testado;reconstruindo drivers DKMS:",
"New mount options to apply:": "Novas opções de montagem para aplicar:",
"New scheduled job (own timer + retention)": "Novo trabalho agendado (cronômetro próprio + retenção)",
"New version available": "Nova versão disponível",
@@ -2738,7 +2748,6 @@
"Next Steps:": "Próximas etapas:",
"Next step: stop that VM first, then run": "Próxima etapa: pare a VM primeiro e depois execute",
"No": "Não",
"No .link files found in": "Nenhum arquivo .link encontrado em",
"No .ova or .ovf files found in:": "Nenhum arquivo .ova ou .ovf encontrado em:",
"No .ovf descriptor found inside OVA.": "Nenhum descritor .ovf encontrado dentro do OVA.",
"No .pxar archives were found in this backup:": "Nenhum arquivo .pxar foi encontrado neste backup:",
@@ -2802,6 +2811,7 @@
"No PVs with old headers found.": "Nenhum PV com cabeçalhos antigos encontrados.",
"No ProxMenux ZFS autotrim state file found.": "Nenhum arquivo de estado de ajuste automático do ProxMenux ZFS foi encontrado.",
"No ProxMenux host-backup archives were found in:": "Nenhum arquivo de backup de host ProxMenux foi encontrado em:",
"No ProxMenux-managed .link files found — nothing to remove.": "Nenhum arquivo .link gerenciado pelo ProxMenux encontrado - nada para remover.",
"No Recent Servers": "Nenhum servidor recente",
"No Samba mounts found.": "Nenhuma montagem do Samba encontrada.",
"No Samba ports found": "Nenhuma porta Samba encontrada",
@@ -2828,6 +2838,7 @@
"No VirtIO ISO found. Please download one.": "Nenhum ISO do VirtIO encontrado. Por favor baixe um.",
"No VirtIO ISO selected. Please choose again.": "Nenhum VirtIO ISO selecionado. Por favor, escolha novamente.",
"No Virtual Machines found on this system.": "Nenhuma máquina virtual encontrada neste sistema.",
"No ZFS pools detected. Skipping ZFS ARC optimization.": "Nenhum pool ZFS detectado.Ignorando a otimização do ZFS ARC.",
"No ZFS pools detected. Skipping ZFS autotrim.": "Nenhum pool ZFS detectado. Ignorando o ajuste automático do ZFS.",
"No accessible": "Não acessível",
"No accessible NFS servers found.": "Nenhum servidor NFS acessível encontrado.",
@@ -3056,7 +3067,7 @@
"Optimize ZFS ARC size": "otimizar o tamanho do ZFS ARC",
"Optimize journald": "Otimizar diário",
"Optimize logrotate": "Otimizar logrotate",
"Optimizing ZFS ARC size according to available memory...": "Otimizando o tamanho do ZFS ARC de acordo com a memória disponível...",
"Optimizing ZFS ARC maximum size...": "Otimizando o tamanho máximo do ZFS ARC...",
"Optimizing logrotate configuration...": "Otimizando a configuração do logrotate...",
"Optimizing memory settings...": "Otimizando configurações de memória...",
"Optimizing network settings...": "Otimizando configurações de rede...",
@@ -3082,6 +3093,7 @@
"Owner:": "Proprietário:",
"Ownership set to root:sharedfiles with 2775 on:": "Propriedade definida como root:sharedfiles com 2775 em:",
"PAM limits configured": "Limites PAM configurados",
"PBS API log rotation configured (hourly, size-based)": "rotação de log da API PBS configurada (por hora, com base no tamanho)",
"PBS backup error log": "Log de erros de backup do PBS",
"PBS backup failed.": "Falha no backup do PBS.",
"PBS encryption keyfile (show / replace / remove)": "arquivo-chave de criptografia PBS (mostrar/substituir/remover)",
@@ -3299,6 +3311,7 @@
"ProxMenux logo applied": "Logotipo ProxMenux aplicado",
"ProxMenux only acts as a launcher — once the script starts, control leaves ProxMenux.": "ProxMenux atua apenas como um iniciador assim que o script é iniciado, o controle sai do ProxMenux.",
"ProxMenux saved it locally at:": "ProxMenux salvou localmente em:",
"ProxMenux-managed .link file(s). User-authored .link files were left in place.": "arquivo(s) .link gerenciado(s) pelo ProxMenux.Os arquivos .link de autoria do usuário foram deixados no lugar.",
"Proxmology logo applied": "Logotipo da Proxmologia aplicado",
"Proxmox 9 system update allready": "Atualização do sistema Proxmox 9 já",
"Proxmox APT repositories configured": "repositórios Proxmox APT configurados",
@@ -3313,6 +3326,7 @@
"Proxmox VE 9.x no-subscription repository created": "Repositório sem assinatura Proxmox VE 9.x criado",
"Proxmox VE Helper Scripts": "Scripts auxiliares do Proxmox VE",
"Proxmox VE configuration completed.": "Configuração do Proxmox VE concluída.",
"Proxmox VE safe update completed": "Atualização segura do Proxmox VE concluída",
"Proxmox auth logger service created and started": "Serviço de registrador de autenticação Proxmox criado e iniciado",
"Proxmox enterprise repo might be missing or inaccessible. Trying to switch to no-subscription...": "O repositório corporativo Proxmox pode estar ausente ou inacessível. Tentando mudar para sem assinatura...",
"Proxmox filter configured": "Filtro Proxmox configurado",
@@ -3326,7 +3340,6 @@
"Proxmox storages:": "Armazenamentos Proxmox:",
"Proxmox system repair completed successfully!": "Reparo do sistema Proxmox concluído com sucesso!",
"Proxmox system repair completed with some issues.": "Reparo do sistema Proxmox concluído com alguns problemas.",
"Proxmox system update": "Atualização do sistema Proxmox",
"Proxmox web interface protection": "Proteção da interface web Proxmox",
"Proxmox web interface: Datacenter > Storage > Add > Directory": "Interface web Proxmox: Datacenter > Armazenamento > Adicionar > Diretório",
"Proxmox web interface: Datacenter > Storage > Add > NFS": "Interface web Proxmox: Datacenter > Armazenamento > Adicionar > NFS",
@@ -3396,6 +3409,7 @@
"Recommended: schedule these paths for next boot to avoid immediate SSH disconnection.": "Recomendado: agende esses caminhos para a próxima inicialização para evitar a desconexão imediata do SSH.",
"Recommended: use GPU -> LXC mode for these devices.": "Recomendado: use o modo GPU -> LXC para esses dispositivos.",
"Recommended: use GPU with LXC workloads instead of VM passthrough on this hardware.": "Recomendado: use GPU com cargas de trabalho LXC em vez de passagem de VM neste hardware.",
"Reconciled": "Reconciliado",
"Recover the keyfile using your recovery passphrase?": "Recuperar o arquivo-chave usando sua senha de recuperação?",
"Recoverable:": "Recuperável:",
"Recovery blob upload failed — main backup is OK, but keyfile recovery from PBS will not be available for this backup.": "falha no upload do blob de recuperação o backup principal está OK, mas a recuperação do arquivo-chave do PBS não estará disponível para este backup.",
@@ -3420,6 +3434,7 @@
"Reinstall/Update NVIDIA drivers": "Reinstale/atualize drivers NVIDIA",
"Reinstalled": "Reinstalado",
"Reinstalled Proxmox packages successfully": "Pacotes Proxmox reinstalados com sucesso",
"Reinstalling": "Reinstalando",
"Reinstalling core Proxmox packages...": "Reinstalando pacotes principais do Proxmox...",
"Release Channel": "Canal de lançamento",
"Release channel set to Beta.": "Canal de lançamento definido como Beta.",
@@ -3480,7 +3495,6 @@
"Removed": "Removido",
"Removed KVM MSR options from configuration": "Opções KVM MSR removidas da configuração",
"Removed Mount:": "Montagem removida:",
"Removed all .link files from": "Removidos todos os arquivos .link de",
"Removed bwlimit/ionice tuning (no .bak found)": "Ajuste de bwlimit/ionice removido (nenhum .bak encontrado)",
"Removed configurations for": "Configurações removidas para",
"Removed credentials file:": "Arquivo de credenciais removido:",
@@ -3503,8 +3517,8 @@
"Removing NVIDIA packages...": "Removendo pacotes NVIDIA...",
"Removing OVH RTM...": "Removendo OVH RTM...",
"Removing OpenVSwitch...": "Removendo OpenVSwitch...",
"Removing ProxMenux persistent NIC .link files...": "Removendo arquivos .link da NIC persistente do ProxMenux...",
"Removing VFIO ownership for selected GPU(s)...": "Removendo propriedade de VFIO para GPU(s) selecionada(s)...",
"Removing all .link files from": "Removendo todos os arquivos .link de",
"Removing any pre-existing gasket-dkms package...": "Removendo qualquer pacote de junta-dkms pré-existente...",
"Removing conflicting utilities...": "Removendo utilitários conflitantes...",
"Removing entropy generation optimization...": "Removendo otimização de geração de entropia...",
@@ -3640,6 +3654,8 @@
"Run mode: Unattended": "Modo de execução: Autônomo",
"Run security audit now": "Execute a auditoria de segurança agora",
"Run the following inside the VM:": "Execute o seguinte dentro da VM:",
"Run the release-channel switch from an SSH session or the Proxmox host console with:": "execute a opção de canal de lançamento a partir de uma sessão SSH ou do console do host Proxmox com:",
"Run the update from an SSH session or the Proxmox host console with:": "execute a atualização a partir de uma sessão SSH ou do console do host Proxmox com:",
"Run upgrade checklist script:": "Execute o script da lista de verificação de atualização:",
"Running": "Correndo",
"Running Lynis security audit...": "Executando auditoria de segurança do Lynis...",
@@ -3648,6 +3664,7 @@
"Running VM detected": "VM em execução detectada",
"Running backup job:": "Executando tarefa de backup:",
"Running containers detected": "Contêineres em execução detectados",
"Running dkms autoinstall for kernel": "Executando a instalação automática do dkms para o kernel",
"Running pre-upgrade simulation to verify 'proxmox-ve' will remain installed...": "Executando simulação de pré-atualização para verificar se o 'proxmox-ve' permanecerá instalado...",
"SATA (standard - high compatibility)": "SATA (padrão - alta compatibilidade)",
"SCSI (recommended for Linux and Windows)": "SCSI (recomendado para Linux e Windows)",
@@ -4049,6 +4066,8 @@
"Skip this step if using no-subscription repository": "Pule esta etapa se estiver usando um repositório sem assinatura",
"Skip — I will add it as PCIe device": "Ignorar vou adicioná-lo como dispositivo PCIe",
"Skip — leave as-is": "Pular deixe como está",
"Skipped (no disks of the pool are present on this host):": "Ignorado (nenhum disco do pool está presente neste host):",
"Skipped (some disks missing):": "Ignorado (alguns discos faltando):",
"Skipped device": "Dispositivo ignorado",
"Skipped to protect target system (would cascade-remove packages)": "Ignorado para proteger o sistema de destino (removeria pacotes em cascata)",
"Skipped, not in apt cache:": "Ignorado, não no cache do apt:",
@@ -4183,6 +4202,7 @@
"Switch to GPU -> LXC (native driver mode)": "Mude para GPU -> LXC (modo de driver nativo)",
"Switch to GPU -> VM (VFIO passthrough mode)": "Mude para GPU -> VM (modo de passagem VFIO)",
"Switches to the free no-subscription repository": "Muda para o repositório gratuito sem assinatura",
"Switching to": "Mudando para",
"Switching to GPU -> LXC mode removes VFIO exclusivity.": "Mudar para GPU -> modo LXC remove a exclusividade VFIO.",
"Switching to GPU -> VM mode requires exclusive VFIO binding.": "Mudar para o modo GPU -> VM requer ligação VFIO exclusiva.",
"Synchronize time automatically": "sincronizar a hora automaticamente",
@@ -4265,11 +4285,13 @@
"The disk": "O disco",
"The file does not exist, is empty or is not readable.": "O arquivo não existe, está vazio ou não é legível.",
"The filesystem": "O sistema de arquivos",
"The following DKMS-managed drivers will now be rebuilt against it so they keep working after reboot:": "Os seguintes drivers gerenciados pelo DKMS agora serão reconstruídos para que continuem funcionando após a reinicialização:",
"The following LXC containers have NVIDIA passthrough configured:": "Os seguintes contêineres LXC têm passagem NVIDIA configurada:",
"The following backup paths are kernel-tied and are excluded from the picker to keep the target's boot safe. The operator's own tuning inside these paths (IOMMU cmdline, VFIO IDs, custom quirks) is merged back automatically via kernel-agnostic merge:": "Os seguintes caminhos de backup estão vinculados ao kernel e são excluídos do seletor para manter a inicialização do destino segura.O próprio ajuste do operador dentro desses caminhos (cmdline IOMMU, IDs VFIO, peculiaridades personalizadas) é mesclado automaticamente por meio de mesclagem independente de kernel:",
"The following changes will be applied": "As seguintes alterações serão aplicadas",
"The following devices were excluded because they are part of an SR-IOV configuration:": "Os seguintes dispositivos foram excluídos porque fazem parte de uma configuração SR-IOV:",
"The following devices were excluded from Controller/NVMe passthrough because they are part of an SR-IOV configuration:": "Os seguintes dispositivos foram excluídos da passagem do Controlador/NVMe porque fazem parte de uma configuração SR-IOV:",
"The following drivers could not be rebuilt for the new kernel — run their installer manually after reboot:": "Os seguintes drivers não puderam ser reconstruídos para o novo kernel execute seu instalador manualmente após a reinicialização:",
"The following entries exist on the host but were NOT in the backup. To make the host EXACTLY match the backup state, they must be removed:": "As seguintes entradas existem no host, mas NÃO estavam no backup.Para fazer com que o host corresponda EXATAMENTE ao estado de backup, eles devem ser removidos:",
"The following selected GPU(s) are currently in GPU -> VM mode (vfio-pci):": "As seguintes GPUs selecionadas estão atualmente no modo GPU -> VM (vfio-pci):",
"The following selected device(s) are Physical Functions with active Virtual Functions:": "Os seguintes dispositivos selecionados são funções físicas com funções virtuais ativas:",
@@ -4368,6 +4390,7 @@
"This is unexpected since credentials were validated.": "Isto é inesperado, uma vez que as credenciais foram validadas.",
"This marks the container as unprivileged": "Isso marca o contêiner como sem privilégios",
"This may be normal for a fresh installation": "Isso pode ser normal para uma nova instalação",
"This may take a few minutes. Press OK to proceed.": "Isso pode levar alguns minutos.Pressione OK para continuar.",
"This may take a few seconds...": "Isso pode levar alguns segundos...",
"This may take several minutes...": "Isso pode levar vários minutos...",
"This means Proxmox handles mount lifecycle natively (no manual /etc/fstab needed for NFS/CIFS host storages).": "Isso significa que o Proxmox lida com o ciclo de vida da montagem nativamente (não é necessário /etc/fstab manual para armazenamentos de host NFS/CIFS).",
@@ -4388,6 +4411,8 @@
"This script must be run on a Proxmox host.": "Este script deve ser executado em um host Proxmox.",
"This script will apply the following optimizations and advanced adjustments to your Proxmox VE server": "Este script aplicará as seguintes otimizações e ajustes avançados ao seu servidor Proxmox VE",
"This script will update your Proxmox VE system with advanced options:": "Este script atualizará seu sistema Proxmox VE com opções avançadas:",
"This session is running in the Monitor terminal. Running it from here would cut the connection mid-install and leave the switch in a broken state.": "Esta sessão está sendo executada no terminal Monitor.Executá-lo a partir daqui cortaria a conexão no meio da instalação e deixaria o switch quebrado.",
"This session is running in the Monitor terminal. Updating from here would restart the Monitor service and cut the connection mid-install, leaving the update in a broken state.": "Esta sessão está sendo executada no terminal Monitor.A atualização a partir daqui reiniciaria o serviço Monitor e cortaria a conexão no meio da instalação, deixando a atualização em um estado interrompido.",
"This shows the storage type and disk identifier": "Isso mostra o tipo de armazenamento e o identificador do disco",
"This state has a high probability of VM startup/reset failures.": "Este estado tem uma alta probabilidade de falhas de inicialização/redefinição da VM.",
"This state indicates a high risk of passthrough failure due to": "Este estado indica um alto risco de falha de passagem devido a",
@@ -4475,7 +4500,9 @@
"Udev rules for Coral USB devices already exist.": "As regras do Udev para dispositivos USB Coral já existem.",
"Udev rules for Coral USB devices appended and rules reloaded.": "Regras Udev para dispositivos Coral USB anexadas e regras recarregadas.",
"Umbrel OS installer script by Helper Scripts\n\nVisit the GitHub repo to learn more, contribute, or support the project:\n\nhttps://community-scripts.github.io/ProxmoxVE/scripts?id=umbrel-os-vm": "Script de instalação do Umbrel OS por Helper Scripts\n\nVisite o repositório GitHub para saber mais, contribuir ou apoiar o projeto:\n\nhttps://community-scripts.github.io/ProxmoxVE/scripts?id=umbrel-os-vm",
"Unable to detect Proxmox version": "Não foi possível detectar a versão do Proxmox",
"Unable to detect Proxmox version.": "Não foi possível detectar a versão do Proxmox.",
"Unable to determine the installed memory.": "Não é possível determinar a memória instalada.",
"Understand the security implications of privileged containers": "Entenda as implicações de segurança de contêineres privilegiados",
"Uninstall Coral drivers and configuration": "Desinstale os drivers e configuração do Coral",
"Uninstall Fail2Ban": "Desinstalar Fail2Ban",
@@ -4564,6 +4591,7 @@
"Updating cluster certificates...": "Atualizando certificados de cluster...",
"Updating initramfs (this may take a minute)...": "Atualizando initramfs (isso pode levar um minuto)...",
"Updating initramfs for all kernels...": "Atualizando initramfs para todos os kernels...",
"Updating initramfs so the ARC cap applies at next boot...": "Atualizando o initramfs para que o limite ARC se aplique na próxima inicialização...",
"Updating initramfs, GRUB, and EFI boot, patience...": "Atualizando initramfs, GRUB e inicialização EFI, paciência...",
"Updating journald to store info-level messages...": "Atualizando o diário para armazenar mensagens de nível informativo...",
"Updating kernel panic configuration...": "Atualizando a configuração do kernel panic...",
@@ -4818,6 +4846,7 @@
"You can add servers manually.": "Você pode adicionar servidores manualmente.",
"You can enter the export path manually.": "Você pode inserir o caminho de exportação manualmente.",
"You can enter the share name manually.": "Você pode inserir o nome do compartilhamento manualmente.",
"You can keep using ProxMenux from this terminal.": "Você pode continuar usando o ProxMenux neste terminal.",
"You can now monitor your AMD GPU using:": "Agora você pode monitorar sua GPU AMD usando:",
"You can now monitor your Intel GPU using:": "Agora você pode monitorar sua GPU Intel usando:",
"You can now select Controller/NVMe devices in Storage Plan.": "Agora você pode selecionar dispositivos Controlador/NVMe no Plano de Armazenamento.",
@@ -4839,8 +4868,7 @@
"You should now be able to access the Proxmox web interface.": "Agora você deve conseguir acessar a interface da web do Proxmox.",
"You will need a Tailscale auth key from: https://login.tailscale.com/admin/settings/keys": "Você precisará de uma chave de autenticação Tailscale de: https://login.tailscale.com/admin/settings/keys",
"ZFS ARC config removed (kernel defaults will apply on reboot)": "Configuração do ZFS ARC removida (os padrões do kernel serão aplicados na reinicialização)",
"ZFS ARC configuration file created/updated successfully": "Arquivo de configuração ZFS ARC criado/atualizado com sucesso",
"ZFS ARC configuration is up to date": "A configuração do ZFS ARC está atualizada",
"ZFS ARC maximum configured:": "máximo do ZFS ARC configurado:",
"ZFS ARC optimization completed": "Otimização ZFS ARC concluída",
"ZFS Management Commands": "Comandos de gerenciamento ZFS",
"ZFS Pool Name": "Nome do conjunto ZFS",
@@ -4921,6 +4949,8 @@
"did not become ready. Skipping.": "não ficou pronto. Pulando.",
"disk(s) added to CT": "disco(s) adicionado(s) ao CT",
"disk(s) added to VM": "disco(s) adicionado(s) à VM",
"disks present": "discos presentes",
"dkms autoinstall did not activate:": "a instalação automática do dkms não foi ativada:",
"dkms.conf generated.": "dkms.conf gerado.",
"does not exist on this host. Path not added.": "não existe neste host. Caminho não adicionado.",
"does not exist. Exiting.": "não existe. Saindo.",
@@ -5039,6 +5069,7 @@
"kexec-tools and related settings removed": "kexec-tools e configurações relacionadas removidas",
"kexec-tools installed successfully": "kexec-tools instalado com sucesso",
"kexec-tools is not installed or already removed.": "kexec-tools não está instalado ou já foi removido.",
"legacy .link file(s) to the ProxMenux-managed format": "arquivo(s) .link herdado(s) para o formato gerenciado pelo ProxMenux",
"log2ram completely removed from system": "log2ram completamente removido do sistema",
"manually inside the container before starting it.": "manualmente dentro do contêiner antes de iniciá-lo.",
"manually inside the container.": "manualmente dentro do contêiner.",
@@ -5114,6 +5145,7 @@
"remove the (now-empty) directory if possible": "remova o diretório (agora vazio), se possível",
"removed from Proxmox": "removido do Proxmox",
"removed successfully from Proxmox.": "removido com sucesso do Proxmox.",
"requires running the official installer, which restarts the Monitor service.": "requer a execução do instalador oficial, que reinicia o serviço Monitor.",
"requires the package": "requer o pacote",
"restarted successfully": "reiniciado com sucesso",
"restoring /etc/network would lose connectivity": "restaurar /etc/network perderia conectividade",
@@ -5143,6 +5175,7 @@
"sources.list update skipped (no change)": "Atualização de sources.list ignorada (sem alteração)",
"sources.list updated to Trixie": "fontes.list atualizado para Trixie",
"ssh-keygen failed. Cannot create a new SSH key.": "ssh-keygen falhou. Não é possível criar uma nova chave SSH.",
"stale entry/entries for interfaces no longer present": "entradas/entradas obsoletas para interfaces não estão mais presentes",
"standard performance": "desempenho padrão",
"start/restart failures and reset instability.": "falhas de inicialização/reinício e instabilidade de redefinição.",
"started successfully.": "iniciado com sucesso.",
+32
View File
@@ -149,6 +149,24 @@ check_updates_stable() {
PROMPT_AVAIL="$(translate 'New version available')"
PROMPT_ASK="$(translate 'Do you want to update now?')"
# Running inside the Monitor's WebSocket terminal: the installer will
# restart the Monitor service, which kills this shell mid-install and
# leaves the update broken. Inform and route to SSH / host console.
if [[ "${PROXMENUX_TERMINAL:-}" == "monitor" ]]; then
local WS_INFO
WS_INFO="$(translate 'A new ProxMenux version is available:') ${REMOTE_VERSION}
$(translate 'This session is running in the Monitor terminal. Updating from here would restart the Monitor service and cut the connection mid-install, leaving the update in a broken state.')
$(translate 'Run the update from an SSH session or the Proxmox host console with:')
bash -c \"\$(wget -qLO - ${INSTALL_URL})\"
$(translate 'You can keep using ProxMenux from this terminal.')"
whiptail --title "$PROMPT_TITLE" --msgbox "$WS_INFO" 20 78
return 0
fi
if whiptail --title "$PROMPT_TITLE" \
--yesno "$PROMPT_AVAIL ($REMOTE_VERSION)\n\n$PROMPT_ASK" \
10 60 --defaultno; then
@@ -180,6 +198,20 @@ check_updates_beta() {
[[ -z "$REMOTE_BETA" || -z "$LOCAL_BETA" || "$LOCAL_BETA" = "$REMOTE_BETA" ]] && return 0
[[ "$(printf '%s\n%s\n' "$LOCAL_BETA" "$REMOTE_BETA" | sort -V | tail -1)" = "$REMOTE_BETA" ]] || return 0
if [[ "${PROXMENUX_TERMINAL:-}" == "monitor" ]]; then
whiptail --title "Beta Update Available" --msgbox "\
A new beta build is available: $REMOTE_BETA
This session is running in the Monitor terminal. Updating from here would restart the Monitor service and cut the connection mid-install, leaving the update in a broken state.
Run the update from an SSH session or the Proxmox host console with:
bash -c \"\$(wget -qLO - $REPO_DEVELOP/install_proxmenux_beta.sh)\"
You can keep using ProxMenux from this terminal." 20 78
return 0
fi
if whiptail --title "Beta Update Available" \
--yesno "A new beta build is available!\n\nInstalled beta : $LOCAL_BETA\nNew beta build : $REMOTE_BETA\n\nDo you want to update now?" \
12 64 --defaultno; then
@@ -221,7 +221,17 @@ read -r -p "Type YES to continue: " ans
systemctl stop pve-cluster || true
[[ -d "\$RECOVERY_ROOT/etc/pve" ]] && mkdir -p /etc/pve && cp -a "\$RECOVERY_ROOT/etc/pve/." /etc/pve/ || true
[[ -d "\$RECOVERY_ROOT/var/lib/pve-cluster" ]] && mkdir -p /var/lib/pve-cluster && cp -a "\$RECOVERY_ROOT/var/lib/pve-cluster/." /var/lib/pve-cluster/ || true
if [[ -d "\$RECOVERY_ROOT/var/lib/pve-cluster" ]]; then
mkdir -p /var/lib/pve-cluster
cp -a "\$RECOVERY_ROOT/var/lib/pve-cluster/." /var/lib/pve-cluster/ || true
# If the backup only carried the raw-fallback (no sqlite3 dump),
# promote it to config.db so pve-cluster picks it up on start.
if [[ ! -f /var/lib/pve-cluster/config.db && -f /var/lib/pve-cluster/config.db.raw-fallback ]]; then
mv -f /var/lib/pve-cluster/config.db.raw-fallback /var/lib/pve-cluster/config.db
else
rm -f /var/lib/pve-cluster/config.db.raw-fallback
fi
fi
systemctl start pve-cluster || true
echo "Cluster recovery finished."
EOF
+209
View File
@@ -1648,6 +1648,13 @@ fi
if [[ -d "\$RECOVERY_ROOT/var/lib/pve-cluster" ]]; then
mkdir -p /var/lib/pve-cluster
cp -a "\$RECOVERY_ROOT/var/lib/pve-cluster/." /var/lib/pve-cluster/ || true
# If the backup only carried the raw-fallback (no sqlite3 dump), promote
# it to config.db so pve-cluster picks it up on start.
if [[ ! -f /var/lib/pve-cluster/config.db && -f /var/lib/pve-cluster/config.db.raw-fallback ]]; then
mv -f /var/lib/pve-cluster/config.db.raw-fallback /var/lib/pve-cluster/config.db
else
rm -f /var/lib/pve-cluster/config.db.raw-fallback
fi
fi
systemctl start pve-cluster || true
@@ -3397,6 +3404,202 @@ _rs_run_custom_restore() {
# and missing on this host, regardless of strategy — installing
# user-installed packages is a prerequisite for the restored
# systemd units and config files to actually do anything.
# Import non-root ZFS pools from the backup manifest whose disks are
# all present on this host. Retry with -f on foreign hostid.
_rs_import_data_pools() {
local staging_root="$1"
local manifest="$staging_root/manifest.json"
[[ -f "$manifest" ]] || return 0
command -v zpool >/dev/null 2>&1 || return 0
command -v jq >/dev/null 2>&1 || return 0
local root_pool=""
if command -v zfs >/dev/null 2>&1; then
local root_dataset
root_dataset=$(zfs get -Ho value name / 2>/dev/null | head -1)
[[ "$root_dataset" == "-" ]] && root_dataset=""
root_pool="${root_dataset%%/*}"
fi
local live_pools
live_pools=$(zpool list -H -o name 2>/dev/null || true)
local -a ok=() forced=() partial=() missing=() failed=()
local pool_name
while IFS= read -r pool_name; do
[[ -z "$pool_name" ]] && continue
[[ "$pool_name" == "$root_pool" ]] && continue
if grep -qFx "$pool_name" <<<"$live_pools"; then
continue
fi
local devs_all devs_total=0 devs_present=0 devs_missing=0 dev dev_path
devs_all=$(jq -r --arg n "$pool_name" \
'.storage_inventory.zfs_pools[]? | select(.name==$n) | .devices_by_id[]?' \
"$manifest" 2>/dev/null)
[[ -z "$devs_all" ]] && continue
# devices_by_id entries can be a by-id basename or an absolute path.
while IFS= read -r dev; do
[[ -z "$dev" ]] && continue
((devs_total++))
if [[ "$dev" == /* ]]; then
dev_path="$dev"
else
dev_path="/dev/disk/by-id/$dev"
fi
if [[ -e "$dev_path" ]]; then
((devs_present++))
else
((devs_missing++))
fi
done <<<"$devs_all"
if (( devs_present == 0 )); then
missing+=("$pool_name")
continue
fi
if (( devs_missing > 0 )); then
partial+=("$pool_name (${devs_present}/${devs_total} $(translate 'disks present'))")
continue
fi
if zpool import "$pool_name" 2>/dev/null; then
ok+=("$pool_name")
elif zpool import -f "$pool_name" 2>/dev/null; then
forced+=("$pool_name")
else
failed+=("$pool_name")
fi
done < <(jq -r '.storage_inventory.zfs_pools[]?.name' "$manifest" 2>/dev/null)
if (( ${#ok[@]} == 0 && ${#forced[@]} == 0 && ${#partial[@]} == 0 && \
${#missing[@]} == 0 && ${#failed[@]} == 0 )); then
return 0
fi
echo
msg_info "$(translate 'Auto-importing ZFS data pools from backup...')"
stop_spinner
if (( ${#ok[@]} > 0 )); then
msg_ok "$(translate 'Imported:') ${ok[*]}"
fi
if (( ${#forced[@]} > 0 )); then
msg_ok "$(translate 'Imported (foreign hostid, forced):') ${forced[*]}"
fi
if (( ${#partial[@]} > 0 )); then
msg_warn "$(translate 'Skipped (some disks missing):') ${partial[*]}"
fi
if (( ${#missing[@]} > 0 )); then
msg_warn "$(translate 'Skipped (no disks of the pool are present on this host):') ${missing[*]}"
fi
if (( ${#failed[@]} > 0 )); then
msg_error "$(translate 'Import failed — inspect with `zpool import`:') ${failed[*]}"
fi
_rs_persist_datapool_import "${ok[@]}" "|FORCED|" "${forced[@]}" \
"|PARTIAL|" "${partial[@]}" "|MISSING|" "${missing[@]}" \
"|FAILED|" "${failed[@]}"
}
# Write the data_pools_import section into restore-state.json (which
# the Backups tab polls) and an append-only log under /var/log/proxmenux.
# Seeds the state file with placeholder fields when it doesn't exist yet
# so the UI can render just this section without crashing on undefined keys.
_rs_persist_datapool_import() {
command -v jq >/dev/null 2>&1 || return 0
local mode="OK"
local -a ok=() forced=() partial=() missing=() failed=()
local arg
for arg in "$@"; do
case "$arg" in
"|FORCED|") mode="FORCED" ;;
"|PARTIAL|") mode="PARTIAL" ;;
"|MISSING|") mode="MISSING" ;;
"|FAILED|") mode="FAILED" ;;
*)
case "$mode" in
OK) ok+=("$arg") ;;
FORCED) forced+=("$arg") ;;
PARTIAL) partial+=("$arg") ;;
MISSING) missing+=("$arg") ;;
FAILED) failed+=("$arg") ;;
esac
;;
esac
done
local state_dir="/var/lib/proxmenux"
local state_file="$state_dir/restore-state.json"
local log_dir="/var/log/proxmenux"
local log_file="$log_dir/restore-datapools-$(date +%Y%m%d_%H%M%S).log"
mkdir -p "$state_dir" "$log_dir" 2>/dev/null || true
{
echo "=== ProxMenux data-pool auto-import at $(date -Iseconds) ==="
local p
for p in "${ok[@]}"; do echo "OK $p"; done
for p in "${forced[@]}"; do echo "FORCED $p (foreign hostid, imported with -f)"; done
for p in "${partial[@]}"; do echo "PARTIAL $p (some vdev disks missing — not imported)"; done
for p in "${missing[@]}"; do echo "MISSING $p (no disks of the pool are present on this host)"; done
for p in "${failed[@]}"; do echo "FAILED $p (zpool import failed — inspect manually)"; done
} >>"$log_file" 2>/dev/null || true
# jq -sR reads each array from newline-separated stdin so entries
# containing spaces (partial pools carry a count fragment) round-trip.
local ok_json forced_json partial_json missing_json failed_json section
ok_json=$(printf '%s\n' "${ok[@]:-}" | jq -Rsc 'split("\n") | map(select(length>0))')
forced_json=$(printf '%s\n' "${forced[@]:-}" | jq -Rsc 'split("\n") | map(select(length>0))')
partial_json=$(printf '%s\n' "${partial[@]:-}" | jq -Rsc 'split("\n") | map(select(length>0))')
missing_json=$(printf '%s\n' "${missing[@]:-}" | jq -Rsc 'split("\n") | map(select(length>0))')
failed_json=$(printf '%s\n' "${failed[@]:-}" | jq -Rsc 'split("\n") | map(select(length>0))')
section=$(jq -n \
--arg finished_at "$(date -Iseconds)" \
--arg log_path "$log_file" \
--argjson ok "$ok_json" \
--argjson forced "$forced_json" \
--argjson partial "$partial_json" \
--argjson missing "$missing_json" \
--argjson failed "$failed_json" \
'{data_pools_import: {
ok:$ok, forced:$forced, partial:$partial,
missing:$missing, failed:$failed,
finished_at:$finished_at, log_path:$log_path
}}')
local tmp
tmp=$(mktemp "${state_file}.XXXXXX") || return 0
if [[ -f "$state_file" ]]; then
if jq -c ". * $section" "$state_file" > "$tmp" 2>/dev/null; then
mv -f "$tmp" "$state_file"
else
rm -f "$tmp"
fi
else
local seed
seed=$(jq -n \
--arg started "$(date -Iseconds)" \
--arg log_path "$log_file" \
--argjson section "$section" \
'{status:"complete",
started_at:$started,
finished_at:$started,
current_step:"Data pools imported",
steps_done:1,
steps_total:1,
log_path:$log_path,
components:[],
rollback_delta:{},
sanity_warnings:[],
summary:null,
acknowledged:false} * $section')
printf '%s\n' "$seed" > "$tmp"
mv -f "$tmp" "$state_file"
fi
}
_rs_run_complete_extras() {
local staging_root="$1"
local include_guests="${2:-1}"
@@ -3618,6 +3821,12 @@ _rs_run_complete_extras() {
fi
fi
# ─ Data ZFS pools — auto-import ───────────────────────────
# Skips the root pool and any pool whose disks aren't all present.
# Retries with -f when ZFS rejects the import as foreign and flags
# the pool as forced in the report.
_rs_import_data_pools "$staging_root"
# ─ Guest configs — only in full strategies ────────────────
if [[ "$include_guests" == "1" ]]; then
local nodes_root="$staging_root/rootfs/etc/pve/nodes"
@@ -107,6 +107,7 @@ hb_default_profile_paths() {
# ── Common Proxmox tooling (skipped if not present) ──
"/etc/systemd/system" # custom units (including log2ram.service if installed)
"/etc/systemd/journald.conf" # journal retention tuning from post-install
"/etc/systemd/network" # .link files that pin NIC names to MAC
"/etc/log2ram.conf"
"/etc/logrotate.conf"
"/etc/logrotate.d" # post-install drops log2ram + custom logrotate here
@@ -524,6 +525,30 @@ hb_prepare_staging() {
: > "$selected_file"
: > "$missing_file"
# pmxcfs (/etc/pve) is served from this SQLite DB with pve-cluster
# running. A plain rsync of the raw file can catch it mid-WAL
# checkpoint. sqlite3's `.backup` is the canonical way to grab a
# consistent snapshot with the DB in use, matching Proxmox's own
# documented advice. The raw file is also kept as `.raw-fallback`
# for environments without sqlite3.
if [[ -f /var/lib/pve-cluster/config.db ]]; then
mkdir -p "$staging_root/rootfs/var/lib/pve-cluster"
if command -v sqlite3 >/dev/null 2>&1; then
if sqlite3 /var/lib/pve-cluster/config.db \
".backup '$staging_root/rootfs/var/lib/pve-cluster/config.db'" 2>/dev/null; then
echo "pmxcfs_config_db=sqlite_backup" >> "$staging_root/metadata/run_info.env.tmp"
else
cp -a /var/lib/pve-cluster/config.db \
"$staging_root/rootfs/var/lib/pve-cluster/config.db.raw-fallback" 2>/dev/null || true
echo "pmxcfs_config_db=raw_fallback" >> "$staging_root/metadata/run_info.env.tmp"
fi
else
cp -a /var/lib/pve-cluster/config.db \
"$staging_root/rootfs/var/lib/pve-cluster/config.db.raw-fallback" 2>/dev/null || true
echo "pmxcfs_config_db=raw_fallback" >> "$staging_root/metadata/run_info.env.tmp"
fi
fi
local p rel target
for p in "${paths[@]}"; do
rel="${p#/}"
@@ -540,6 +565,19 @@ hb_prepare_staging() {
--exclude "*.log"
)
# /var/lib/pve-cluster: skip the raw config.db and its WAL/SHM
# sidecars — they were already captured atomically above via
# sqlite3 .backup (or as raw-fallback when sqlite3 isn't
# available). Everything else in the directory (backup subdir,
# auxiliary state) is safe to rsync live.
if [[ "$rel" == "var/lib/pve-cluster" || "$rel" == "var/lib/pve-cluster/"* ]]; then
rsync_opts+=(
--exclude "config.db"
--exclude "config.db-wal"
--exclude "config.db-shm"
)
fi
# /root is included by default for easier recovery, but avoid volatile/sensitive noise.
if [[ "$rel" == "root" || "$rel" == "root/"* ]]; then
rsync_opts+=(
@@ -587,7 +625,11 @@ hb_prepare_staging() {
echo "generated_at=$(date -Iseconds)"
echo "hostname=$(hostname)"
echo "kernel=$(uname -r)"
if [[ -f "$meta/run_info.env.tmp" ]]; then
cat "$meta/run_info.env.tmp"
fi
} > "$meta/run_info.env"
rm -f "$meta/run_info.env.tmp"
command -v pveversion >/dev/null 2>&1 && pveversion -v > "$meta/pveversion.txt" 2>&1 || true
command -v lsblk >/dev/null 2>&1 && lsblk -f > "$meta/lsblk.txt" 2>&1 || true
command -v qm >/dev/null 2>&1 && qm list > "$meta/qm-list.txt" 2>&1 || true
@@ -30,9 +30,16 @@ while IFS= read -r pool_json; do
needed_devs="$(printf '%s' "$pool_json" | jq -r '.devices_by_id[]?')"
present=()
missing=()
# devices_by_id entries can be a by-id basename or an absolute path.
dev_path=""
while IFS= read -r dev; do
[[ -z "$dev" ]] && continue
if [[ -e "/dev/disk/by-id/$dev" ]]; then
if [[ "$dev" == /* ]]; then
dev_path="$dev"
else
dev_path="/dev/disk/by-id/$dev"
fi
if [[ -e "$dev_path" ]]; then
present+=("$dev")
else
missing+=("$dev")
+147 -67
View File
@@ -85,87 +85,167 @@ lvm_repair_check() {
cleanup_duplicate_repos_pve9() {
msg_info "$(translate "Cleaning up duplicate repositories...")"
local sources_file="/etc/apt/sources.list"
local temp_file=$(mktemp)
local cleaned_count=0
declare -A seen_repos
if [ ! -s "$sources_file" ]; then
return 0
fi
while IFS= read -r line || [[ -n "$line" ]]; do
if [[ "$line" =~ ^[[:space:]]*# ]] || [[ -z "$line" ]]; then
echo "$line" >> "$temp_file"
continue
fi
if [[ "$line" =~ ^deb ]]; then
read -r _ url dist components <<< "$line"
local key="${url}_${dist}"
if [[ -v "seen_repos[$key]" ]]; then
echo "# $line" >> "$temp_file"
cleaned_count=$((cleaned_count + 1))
msg_info "$(translate "Commented duplicate: $url $dist")"
local sources_file="/etc/apt/sources.list"
local cleaned_count=0
# Helper: extract a DEB822 field's value from a .sources file. Handles
# both `Field: value` and folded continuations. Returns the FIRST value
# only (URIs / Suites / Components with multiple entries are read as a
# single whitespace-separated string that callers split themselves).
_deb822_get() {
local file="$1" field="$2"
[[ -f "$file" ]] || return 1
awk -v F="$field" '
BEGIN{ IGNORECASE=1 }
/^[[:space:]]*$/{ next }
/^[^[:space:]]/{
if (match($0, "^"F"[[:space:]]*:[[:space:]]*")) {
print substr($0, RSTART+RLENGTH)
exit
}
}
' "$file"
}
# Helper: back up a file once before modifying, so an accidental
# comment-out is always recoverable next to the original.
_backup_once() {
local file="$1"
[[ -f "$file" ]] || return 0
local ts backup
ts=$(date +%Y%m%d_%H%M%S)
backup="${file}.proxmenux-backup.${ts}"
[[ -f "$backup" ]] || cp -a "$file" "$backup"
}
# ── Phase 1 — comment intra-file duplicates in sources.list by URL+Suite ──
if [ -s "$sources_file" ]; then
local temp_file
temp_file=$(mktemp)
declare -A seen_repos
local file_changed=0
while IFS= read -r line || [[ -n "$line" ]]; do
if [[ "$line" =~ ^[[:space:]]*# ]] || [[ -z "$line" ]]; then
echo "$line" >> "$temp_file"
continue
fi
if [[ "$line" =~ ^deb ]]; then
read -r _ url dist components <<< "$line"
local key="${url}_${dist}"
# Portable "is this associative-array key set?" — `[[ -v arr[key] ]]`
# is only reliable in bash 4.4+; `${var+set}` works everywhere.
if [[ -n "${seen_repos[$key]+set}" ]]; then
echo "# $line" >> "$temp_file"
cleaned_count=$((cleaned_count + 1))
file_changed=1
msg_info "$(translate "Commented duplicate: $url $dist")"
else
echo "$line" >> "$temp_file"
seen_repos[$key]="$components"
fi
else
echo "$line" >> "$temp_file"
seen_repos[$key]="$components"
fi
done < "$sources_file"
if [[ "$file_changed" -eq 1 ]]; then
_backup_once "$sources_file"
mv "$temp_file" "$sources_file"
chmod 644 "$sources_file"
else
echo "$line" >> "$temp_file"
fi
done < "$sources_file"
mv "$temp_file" "$sources_file"
chmod 644 "$sources_file"
if [ -f "/etc/apt/sources.list.d/proxmox.sources" ]; then
if grep -q "^deb.*download\.proxmox\.com" "$sources_file"; then
sed -i '/^deb.*download\.proxmox\.com/s/^/# /' "$sources_file"
cleaned_count=$((cleaned_count + 1))
fi
for list_file in /etc/apt/sources.list.d/pve-*.list; do
if [ -f "$list_file" ] && [[ "$list_file" != "/etc/apt/sources.list.d/pve-enterprise.list" ]]; then
if grep -q "^deb" "$list_file"; then
sed -i 's/^deb/# deb/g' "$list_file"
cleaned_count=$((cleaned_count + 1))
fi
fi
done
if [ -f "/etc/apt/sources.list.d/debian.sources" ]; then
if grep -q "^deb.*deb\.debian\.org" "$sources_file"; then
sed -i '/^deb.*deb\.debian\.org/s/^/# /' "$sources_file"
cleaned_count=$((cleaned_count + 1))
fi
if grep -q "^deb.*security\.debian\.org" "$sources_file"; then
sed -i '/^deb.*security\.debian\.org/s/^/# /' "$sources_file"
cleaned_count=$((cleaned_count + 1))
fi
rm -f "$temp_file"
fi
fi
# ── Phase 2 — comment lines duplicating what proxmox.sources already declares ──
# Comparison is EXACT on URL + Suite + at least one Component match, so a
# legitimate custom repo the user added under `download.proxmox.com` (e.g.
# /debian/pbs, /debian/ceph-squid, or the same URL pinned to a different
# suite) is preserved untouched.
if [ -f "/etc/apt/sources.list.d/proxmox.sources" ]; then
local pmx_uri pmx_suite pmx_comps
pmx_uri=$(_deb822_get /etc/apt/sources.list.d/proxmox.sources URIs)
pmx_suite=$(_deb822_get /etc/apt/sources.list.d/proxmox.sources Suites)
pmx_comps=$(_deb822_get /etc/apt/sources.list.d/proxmox.sources Components)
_match_and_comment() {
local target_file="$1" uri="$2" suite="$3" comps="$4"
[[ -f "$target_file" ]] || return 0
[[ -n "$uri" && -n "$suite" && -n "$comps" ]] || return 0
local base_uri="${uri#http://}"
base_uri="${base_uri#https://}"
base_uri="${base_uri%/}"
local first_comp
first_comp=$(awk '{print $1}' <<< "$comps")
local matched=0
while IFS= read -r ln; do
[[ "$ln" =~ ^[[:space:]]*# ]] && continue
[[ "$ln" =~ ^deb ]] || continue
read -r _ line_url line_suite line_comps <<< "$ln"
local ln_base="${line_url#http://}"
ln_base="${ln_base#https://}"
ln_base="${ln_base%/}"
[[ "$ln_base" == "$base_uri" ]] || continue
[[ "$line_suite" == "$suite" ]] || continue
[[ " $line_comps " == *" $first_comp "* ]] || continue
matched=1
break
done < "$target_file"
if [[ "$matched" -eq 1 ]]; then
_backup_once "$target_file"
# Anchored sed: reconstruct the exact deb prefix to avoid
# eating unrelated lines. Escape URL for regex safety.
local esc_uri esc_suite esc_comp
esc_uri=$(printf '%s' "$uri" | sed 's/[][\.^$*/]/\\&/g')
esc_suite=$(printf '%s' "$suite" | sed 's/[][\.^$*/]/\\&/g')
esc_comp=$(printf '%s' "$first_comp" | sed 's/[][\.^$*/]/\\&/g')
sed -i -E "/^deb[[:space:]]+${esc_uri}[[:space:]]+${esc_suite}[[:space:]]+.*(^| )${esc_comp}( |$)/s/^/# /" "$target_file"
cleaned_count=$((cleaned_count + 1))
fi
}
_match_and_comment "$sources_file" "$pmx_uri" "$pmx_suite" "$pmx_comps"
# Only walk a fixed allowlist of known-legacy PVE list files. Any
# other pve-*.list on disk is assumed to be user-authored (custom
# mirror, backports, staging) and left alone.
local legacy_pve_lists=(
/etc/apt/sources.list.d/pve-public-repo.list
/etc/apt/sources.list.d/pve-install-repo.list
/etc/apt/sources.list.d/pve-no-subscription.list
)
for legacy in "${legacy_pve_lists[@]}"; do
_match_and_comment "$legacy" "$pmx_uri" "$pmx_suite" "$pmx_comps"
done
# Same exact-match approach for debian.sources vs sources.list.
if [ -f "/etc/apt/sources.list.d/debian.sources" ]; then
local dbn_uri dbn_suite dbn_comps
dbn_uri=$(_deb822_get /etc/apt/sources.list.d/debian.sources URIs)
dbn_suite=$(_deb822_get /etc/apt/sources.list.d/debian.sources Suites)
dbn_comps=$(_deb822_get /etc/apt/sources.list.d/debian.sources Components)
# `Suites` in debian.sources holds multiple ("trixie trixie-updates"),
# walk each so both duplicates get commented if present.
for suite_iter in $dbn_suite; do
_match_and_comment "$sources_file" "$dbn_uri" "$suite_iter" "$dbn_comps"
done
fi
fi
# ── Phase 3 — remove ONLY the well-known legacy files, and only when the
# modern replacement already exists ──
if [ -f "/etc/apt/sources.list.d/proxmox.sources" ]; then
for old_file in /etc/apt/sources.list.d/pve-public-repo.list /etc/apt/sources.list.d/pve-install-repo.list; do
if [ -f "$old_file" ]; then
_backup_once "$old_file"
rm -f "$old_file"
cleaned_count=$((cleaned_count + 1))
fi
done
fi
if [ $cleaned_count -gt 0 ]; then
msg_ok "$(translate "Cleaned up $cleaned_count duplicate/old repositories")"
apt-get update > /dev/null 2>&1 || true
@@ -193,7 +273,7 @@ cleanup_duplicate_repos_pve9_() {
if [[ "$line" =~ ^deb ]]; then
read -r _ url dist components <<< "$line"
local key="${url}_${dist}"
if [[ -v "seen_repos[$key]" ]]; then
if [[ -n "${seen_repos[$key]+set}" ]]; then
echo "# $line" >> "$temp_file"
cleaned_count=$((cleaned_count + 1))
else
@@ -276,7 +356,7 @@ cleanup_duplicate_repos_pve8() {
if [[ "$line" =~ ^[[:space:]]*deb ]]; then
read -r _ url dist components <<< "$line"
local key="${url}_${dist}"
if [[ -v "seen_repos[$key]" ]]; then
if [[ -n "${seen_repos[$key]+set}" ]]; then
echo "# $line" >> "$temp_file"
cleaned_count=$((cleaned_count + 1))
else
+272
View File
@@ -0,0 +1,272 @@
#!/bin/bash
# ==========================================================
# Proxmox VE Update Script — Safe / Non-Invasive Variant
# ==========================================================
# Author : MacRimi
# Copyright : (c) 2024 MacRimi
# License : GPL-3.0
# ==========================================================
# Description:
# Update path intended for a Proxmox host ALREADY in
# production. Unlike scripts/global/update-pve8.sh and
# update-pve9_2.sh (invoked by post_install), this variant
# NEVER modifies the operator's own configuration:
#
# - Does NOT disable Enterprise / Ceph repositories
# - Does NOT delete legacy repo files
# - Does NOT overwrite proxmox.sources / debian.sources
# when they already exist
# - Does NOT purge alternative NTP services
# - Does NOT force-install zfsutils / chrony /
# proxmox-backup-restore-image
# - Does NOT write no-firmware-warnings.conf
#
# What it DOES:
# 1. Sanity checks (disk space, network)
# 2. ensure_repositories() — only when repos are MISSING
# 3. apt-get update, with automatic GPG key import when apt
# reports NO_PUBKEY (any repo, user's or ours)
# 4. cleanup_duplicate_repos() — exact URL+Suite+Component
# match against proxmox.sources / debian.sources; leaves
# unrelated custom `download.proxmox.com/*` and
# user-authored pve-*.list files alone; backs each file
# up before modifying
# 5. Detect pending upgrades + security count
# 6. Confirmation dialog
# 7. apt-get full-upgrade with --force-confdef / --force-confold
# (never overwrites the operator's edited config files)
# 8. lvm_repair_check() — refreshes VG metadata when disks
# passed through to guest VMs (DSM, TrueNAS, …) come back
# with old PV headers
# 9. apt-get autoremove + autoclean
#
# Reboot detection is handled by the caller (utilities/proxmox_update.sh).
# ==========================================================
LOCAL_SCRIPTS="/usr/local/share/proxmenux/scripts"
BASE_DIR="/usr/local/share/proxmenux"
UTILS_FILE="$BASE_DIR/utils.sh"
APT_ENV="env DEBIAN_FRONTEND=noninteractive LC_ALL=C LANG=C"
if [[ -f "$UTILS_FILE" ]]; then
source "$UTILS_FILE"
fi
load_language
initialize_cache
download_common_functions() {
if ! source "$LOCAL_SCRIPTS/global/common-functions.sh"; then
return 1
fi
}
# ensure_repositories() lives with the install helpers.
source_install_functions() {
local f="$LOCAL_SCRIPTS/global/utils-install-functions.sh"
if [[ -f "$f" ]]; then
source "$f"
fi
}
update_pve_safe() {
local pve_version
pve_version=$(pveversion 2>/dev/null | grep -oP 'pve-manager/\K[0-9]+' | head -1)
if [[ -z "$pve_version" ]]; then
msg_error "$(translate "Unable to detect Proxmox version")"
return 1
fi
local start_time
start_time=$(date +%s)
local log_file="/var/log/proxmox-update-$(date +%Y%m%d-%H%M%S).log"
# Screen capture: replay the pre-upgrade context lines after `clear`
# so the operator keeps the visual history around the noisy apt run.
local screen_capture="/tmp/proxmenux_screen_capture_$$.txt"
: > "$screen_capture"
download_common_functions
source_install_functions
{
msg_info2 "$(translate "Detected: Proxmox VE $pve_version — running safe update path")"
} | tee -a "$screen_capture"
# ── 1. Sanity checks ──
local available_space
available_space=$(df /var/cache/apt/archives | awk 'NR==2 {print int($4/1024)}')
if [ "$available_space" -lt 1024 ]; then
msg_error "$(translate "Insufficient disk space. Available: ${available_space}MB")"
echo -e
msg_success "$(translate "Press Enter to return to menu...")"
read -r
rm -f "$screen_capture"
return 1
fi
if ! ping -c 1 download.proxmox.com >/dev/null 2>&1; then
msg_error "$(translate "Cannot reach Proxmox repositories")"
echo -e
msg_success "$(translate "Press Enter to return to menu...")"
read -r
rm -f "$screen_capture"
return 1
fi
# ── 2. ensure_repositories: adds base Proxmox+Debian repos only if
# they don't already exist. On a configured host this is a no-op. ──
if declare -f ensure_repositories >/dev/null 2>&1; then
ensure_repositories
fi
# ── 3. apt-get update with automatic key recovery ──
local update_output update_exit_code
update_output=$(apt-get update 2>&1)
update_exit_code=$?
if [ $update_exit_code -eq 0 ]; then
msg_ok "$(translate "Package lists updated successfully")" | tee -a "$screen_capture"
else
if echo "$update_output" | grep -Eq "NO_PUBKEY|GPG error"; then
local key
key=$(echo "$update_output" | sed -n 's/.*NO_PUBKEY \([0-9A-F]\{8,40\}\).*/\1/p' | head -1)
if [ -n "$key" ]; then
mkdir -p /etc/apt/keyrings
if command -v gpg >/dev/null 2>&1; then
if gpg --batch --keyserver keyserver.ubuntu.com --recv-keys "$key" \
&& gpg --batch --export "$key" | gpg --dearmor -o "/etc/apt/keyrings/${key}.gpg"; then
msg_ok "$(translate "Imported missing GPG key: $key")" | tee -a "$screen_capture"
else
msg_warn "$(translate "Keyrings method failed; trying apt-key fallback")"
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys "$key" >/dev/null 2>&1 || true
fi
else
msg_warn "$(translate "gpg not found; trying apt-key fallback")"
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys "$key" >/dev/null 2>&1 || true
fi
fi
if apt-get update > "$log_file" 2>&1; then
msg_ok "$(translate "Package lists updated after GPG fix")" | tee -a "$screen_capture"
else
msg_error "$(translate "Failed to update package lists. Check log: $log_file")"
rm -f "$screen_capture"
return 1
fi
elif echo "$update_output" | grep -Eq "404|Failed to fetch"; then
msg_warn "$(translate "Some repositories are not available, continuing with available ones...")"
else
msg_error "$(translate "Failed to update package lists. Check log: $log_file")"
echo "Error details: $update_output"
rm -f "$screen_capture"
return 1
fi
fi
# ── 4. Precise duplicate cleanup (exact URL+Suite+Component match,
# backs up files before modifying). Skipped if unavailable. ──
if declare -f cleanup_duplicate_repos >/dev/null 2>&1; then
cleanup_duplicate_repos
fi
# ── 5-6. Detect + confirm ──
local current_pve_version available_pve_version upgradable security_updates
current_pve_version=$(pveversion 2>/dev/null | grep -oP 'pve-manager/\K[0-9]+\.[0-9]+\.[0-9]+' | head -1)
available_pve_version=$(apt-cache policy pve-manager 2>/dev/null | grep -oP 'Candidate: \K[0-9]+\.[0-9]+\.[0-9]+' | head -1)
upgradable=$($APT_ENV apt list --upgradable 2>/dev/null | sed '1d' | sed '/^\s*$/d' | wc -l)
security_updates=$($APT_ENV apt list --upgradable 2>/dev/null | sed '1d' | grep -ci '\-security')
local menu_text
menu_text="$(translate "System Update Information")\n\n"
menu_text+="$(translate "Current PVE Version"): $current_pve_version\n"
if [ -n "$available_pve_version" ] && [ "$available_pve_version" != "$current_pve_version" ]; then
menu_text+="$(translate "Available PVE Version"): $available_pve_version\n"
fi
menu_text+="\n$(translate "Package Updates Available"): $upgradable\n"
menu_text+="$(translate "Security Updates"): $security_updates\n\n"
if [ "$upgradable" -eq 0 ]; then
menu_text+="$(translate "System is already up to date")"
whiptail --title "$(translate "Update Status")" --msgbox "$menu_text" 15 70
apt-get -y autoremove >/dev/null 2>&1 || true
apt-get -y autoclean >/dev/null 2>&1 || true
rm -f "$screen_capture"
return 0
fi
menu_text+="$(translate "Do you want to proceed with the system update?")"
if ! whiptail --title "$(translate "Proxmox Update")" --yesno "$menu_text" 18 70; then
msg_info2 "$(translate "Update cancelled by user")"
apt-get -y autoremove >/dev/null 2>&1 || true
apt-get -y autoclean >/dev/null 2>&1 || true
rm -f "$screen_capture"
return 0
fi
# ── 7. Full upgrade — --force-confdef/confold preserves user-edited configs ──
# Redraw the ProxMenux frame before apt starts printing so the operator
# keeps the visual context around the noisy upgrade output.
clear
show_proxmenux_logo
msg_title "$(translate "$SCRIPT_TITLE")"
cat "$screen_capture"
# apt's own progress bar (Progress: [ %]) prints on stderr and only
# when stdout is a TTY. We pipe stderr through tee to keep a log copy
# while letting apt keep its interactive stdout, so the native bar
# keeps rendering at the bottom of the terminal as the user expects.
DEBIAN_FRONTEND=noninteractive apt -y \
-o Dpkg::Options::='--force-confdef' \
-o Dpkg::Options::='--force-confold' \
full-upgrade 2> >(tee -a "$log_file" >&2)
local upgrade_exit_code=$?
echo -e
# Redraw once more so the wrap-up (LVM check, cleanup, summary) reads
# cleanly instead of scrolling under half-a-screen of apt noise.
clear
show_proxmenux_logo
msg_title "$(translate "$SCRIPT_TITLE")"
cat "$screen_capture"
if [ $upgrade_exit_code -ne 0 ]; then
msg_error "$(translate "System upgrade failed. Check log: $log_file")"
rm -f "$screen_capture"
return 1
fi
msg_ok "$(translate "System upgrade completed")"
# ── 8. LVM header repair (only touches VGs actually flagged as stale) ──
if declare -f lvm_repair_check >/dev/null 2>&1; then
lvm_repair_check
fi
# ── 9. DKMS driver rebuild if a new kernel was staged ──
if declare -f pmx_rebuild_dkms_after_kernel >/dev/null 2>&1; then
pmx_rebuild_dkms_after_kernel
fi
# ── 10. Final cleanup ──
apt-get -y autoremove >/dev/null 2>&1 || true
apt-get -y autoclean >/dev/null 2>&1 || true
msg_ok "$(translate "Cleanup finished")"
local end_time duration minutes seconds
end_time=$(date +%s)
duration=$((end_time - start_time))
minutes=$((duration / 60))
seconds=$((duration % 60))
echo -e "${TAB}${BGN}$(translate "====== PVE UPDATE COMPLETED ======")${CL}"
echo -e "${TAB}${GN}⏱️ $(translate "Duration")${CL}: ${BL}${minutes}m ${seconds}s${CL}"
echo -e "${TAB}${GN}📄 $(translate "Log file")${CL}: ${BL}$log_file${CL}"
echo -e "${TAB}${GN}📦 $(translate "Packages upgraded")${CL}: ${BL}$upgradable${CL}"
echo -e "${TAB}${GN}🖥️ $(translate "Proxmox VE")${CL}: ${BL}${available_pve_version:-$current_pve_version}${CL}"
msg_ok "$(translate "Proxmox VE safe update completed")"
rm -f "$screen_capture"
}
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
update_pve_safe
fi
+303
View File
@@ -153,3 +153,306 @@ install_single_package() {
return 1
fi
}
# ==========================================================
# Persistent NIC naming — shared helpers
# ==========================================================
# ProxMenux-owned .link files carry two identifying marks:
# - filename prefix: 10-proxmenux-<iface>.link
# - first-line marker: `# Managed by ProxMenux — do not edit`
# Both are required for pmx_uninstall_persistent_network to remove a
# file, so user-authored .link files in /etc/systemd/network/ are
# never touched.
readonly PMX_NIC_LINK_DIR="/etc/systemd/network"
readonly PMX_NIC_LINK_MARKER="# Managed by ProxMenux — do not edit"
# Print the MAC address recorded in a .link file, uppercased, or empty
# if the file has no MACAddress= line.
_pmx_link_mac() {
awk -F= '
/^[[:space:]]*MACAddress=/ {
gsub(/[[:space:]]/, "", $2)
print toupper($2)
exit
}
' "$1" 2>/dev/null
}
# Returns 0 if the file exists AND its first line matches the marker.
_pmx_link_is_managed() {
local first
IFS= read -r first < "$1" 2>/dev/null || return 1
[[ "$first" == "$PMX_NIC_LINK_MARKER" ]]
}
# Returns 0 if the file matches the exact template ProxMenux 1.0 used
# to write (no marker, no extra fields). Used once at 1.1 upgrade time
# to reclaim files created by an earlier version and replace them with
# the marked format.
_pmx_link_matches_legacy_10() {
local file="$1" iface mac
iface=$(basename "$file" .link)
iface="${iface#10-}"
mac=$(_pmx_link_mac "$file")
[[ -z "$iface" || -z "$mac" ]] && return 1
local expected
expected="[Match]
MACAddress=$mac
[Link]
Name=$iface"
[[ "$(cat "$file" 2>/dev/null)" == "$expected" ]]
}
# Detect physical NICs and generate 10-proxmenux-<iface>.link for
# each. Idempotent — reruns replace stale ProxMenux entries whose MAC
# is no longer present, migrate 1.0-format files, and leave every
# other .link in the directory alone.
#
# Outputs (via `printf`):
# line "COUNT=<n>" — number of managed files after the run
# line "REMOVED_STALE=<n>" — reconciled entries for missing MACs
# line "REMOVED_LEGACY=<n>" — 1.0-format files replaced
pmx_setup_persistent_network() {
mkdir -p "$PMX_NIC_LINK_DIR"
declare -A current_macs=()
local dev_path iface mac
for dev_path in /sys/class/net/*; do
iface=$(basename "$dev_path")
case "$iface" in
lo|docker*|veth*|br-*|vmbr*|tap*|fwpr*|fwln*|virbr*|bond*|cilium*|zt*|wg*)
continue ;;
esac
if [[ -e "$dev_path/device" || -e "$dev_path/phy80211" ]]; then
mac=$(cat "$dev_path/address" 2>/dev/null | tr '[:lower:]' '[:upper:]')
[[ "$mac" =~ ^([A-F0-9]{2}:){5}[A-F0-9]{2}$ ]] && current_macs["$mac"]=1
fi
done
if compgen -G "$PMX_NIC_LINK_DIR"/*.link >/dev/null; then
local backup_dir
backup_dir="$PMX_NIC_LINK_DIR/backup-$(date +%Y%m%d-%H%M%S)"
mkdir -p "$backup_dir"
cp "$PMX_NIC_LINK_DIR"/*.link "$backup_dir"/ 2>/dev/null || true
fi
local removed_stale=0 removed_legacy=0
local link_file
for link_file in "$PMX_NIC_LINK_DIR"/10-proxmenux-*.link; do
[[ -f "$link_file" ]] || continue
_pmx_link_is_managed "$link_file" || continue
mac=$(_pmx_link_mac "$link_file")
[[ -z "$mac" ]] && continue
if [[ -z "${current_macs[$mac]+x}" ]]; then
rm -f -- "$link_file"
removed_stale=$((removed_stale + 1))
fi
done
for link_file in "$PMX_NIC_LINK_DIR"/10-*.link; do
[[ -f "$link_file" ]] || continue
[[ "$(basename "$link_file")" == 10-proxmenux-*.link ]] && continue
if _pmx_link_matches_legacy_10 "$link_file"; then
rm -f -- "$link_file"
removed_legacy=$((removed_legacy + 1))
fi
done
local count=0
for dev_path in /sys/class/net/*; do
iface=$(basename "$dev_path")
case "$iface" in
lo|docker*|veth*|br-*|vmbr*|tap*|fwpr*|fwln*|virbr*|bond*|cilium*|zt*|wg*)
continue ;;
esac
[[ -e "$dev_path/device" || -e "$dev_path/phy80211" ]] || continue
mac=$(cat "$dev_path/address" 2>/dev/null | tr '[:lower:]' '[:upper:]')
[[ "$mac" =~ ^([A-F0-9]{2}:){5}[A-F0-9]{2}$ ]] || continue
local link_file="$PMX_NIC_LINK_DIR/10-proxmenux-$iface.link"
cat > "$link_file" <<EOF
$PMX_NIC_LINK_MARKER
[Match]
MACAddress=$mac
[Link]
Name=$iface
EOF
chmod 644 "$link_file"
count=$((count + 1))
done
printf 'COUNT=%d\n' "$count"
printf 'REMOVED_STALE=%d\n' "$removed_stale"
printf 'REMOVED_LEGACY=%d\n' "$removed_legacy"
}
# Remove only files that carry BOTH the ProxMenux filename prefix AND
# the marker on the first line. User-authored .link files are left
# intact regardless of their name.
pmx_uninstall_persistent_network() {
local removed=0 link_file
for link_file in "$PMX_NIC_LINK_DIR"/10-proxmenux-*.link; do
[[ -f "$link_file" ]] || continue
_pmx_link_is_managed "$link_file" || continue
rm -f -- "$link_file"
removed=$((removed + 1))
done
printf 'REMOVED=%d\n' "$removed"
}
# ==========================================================
# DKMS driver rebuild after a kernel upgrade
# ==========================================================
# Called from update-pve-safe.sh and proxmox_update.sh after
# apt full-upgrade succeeds. Detects whether a new kernel is
# staged for the next boot and, if so, ensures matching headers
# are installed and rebuilds every DKMS module registered by
# ProxMenux components against that kernel — so drivers keep
# working after the reboot without operator intervention.
readonly PMX_COMPONENTS_STATUS="/usr/local/share/proxmenux/components_status.json"
# component_key : dkms_module_name (empty module = not DKMS)
readonly -a PMX_DKMS_COMPONENTS=(
"nvidia_driver:nvidia"
"coral_driver:gasket"
)
# Print the newest installed pve/proxmox kernel version, or empty.
_pmx_newest_installed_kernel() {
dpkg-query -W -f='${Status}\t${Package}\n' \
'proxmox-kernel-*-pve-signed' 'pve-kernel-*-pve' 2>/dev/null \
| awk -F'\t' '/^install ok installed\t/ { print $2 }' \
| sed -E 's/^(proxmox|pve)-kernel-//; s/-signed$//' \
| sort -V | tail -1
}
# Return the header package name matching a kernel version.
_pmx_header_pkg_for_kernel() {
local kver="$1"
if apt-cache show "proxmox-headers-$kver" >/dev/null 2>&1; then
printf 'proxmox-headers-%s\n' "$kver"
else
printf 'pve-headers-%s\n' "$kver"
fi
}
# Return 0 if the DKMS module is 'installed' for the given kernel.
_pmx_dkms_module_installed_for_kernel() {
local module="$1" kver="$2"
command -v dkms >/dev/null 2>&1 || return 1
dkms status 2>/dev/null | awk -v m="$module" -v k="$kver" '
BEGIN { FS = "[,:]" }
{
gsub(/[[:space:]]/, "")
if (index($0, m "/") == 1 && index($0, k) > 0 && $0 ~ /installed/) {
found = 1; exit
}
}
END { exit found ? 0 : 1 }
'
}
# Best-effort rebuild. Never returns non-zero — the update flow that
# calls this must always complete regardless of driver state.
pmx_rebuild_dkms_after_kernel() {
command -v dkms >/dev/null 2>&1 || return 0
[[ -f "$PMX_COMPONENTS_STATUS" ]] || return 0
command -v jq >/dev/null 2>&1 || return 0
local running_kernel newest_kernel
running_kernel=$(uname -r)
newest_kernel=$(_pmx_newest_installed_kernel)
[[ -z "$newest_kernel" || "$newest_kernel" == "$running_kernel" ]] && return 0
local -a pending_components=() pending_modules=()
local entry key module status
for entry in "${PMX_DKMS_COMPONENTS[@]}"; do
key="${entry%%:*}"
module="${entry##*:}"
status=$(jq -r --arg k "$key" '.[$k].status // ""' "$PMX_COMPONENTS_STATUS" 2>/dev/null)
[[ "$status" == "installed" ]] || continue
pending_components+=("$key")
pending_modules+=("$module")
done
(( ${#pending_components[@]} == 0 )) && return 0
local msg
msg="$(translate 'A new kernel is staged for the next boot:')"$'\n'
msg+=" ${newest_kernel}"$'\n\n'
msg+="$(translate 'The following DKMS-managed drivers will now be rebuilt against it so they keep working after reboot:')"$'\n'
local c
for c in "${pending_components[@]}"; do
msg+="$c"$'\n'
done
msg+=$'\n'"$(translate 'This may take a few minutes. Press OK to proceed.')"
if [[ -t 0 ]] && command -v whiptail >/dev/null 2>&1; then
whiptail --title "$(translate 'DKMS driver rebuild')" --msgbox "$msg" 18 78
else
msg_info "$(translate 'New kernel staged; rebuilding DKMS drivers:') ${pending_components[*]}"
fi
local header_pkg
header_pkg=$(_pmx_header_pkg_for_kernel "$newest_kernel")
if ! dpkg-query -W -f='${Status}' "$header_pkg" 2>/dev/null | grep -q 'install ok installed'; then
msg_info "$(translate 'Installing kernel headers:') $header_pkg"
if DEBIAN_FRONTEND=noninteractive apt-get install -y "$header_pkg" >/dev/null 2>&1; then
msg_ok "$(translate 'Kernel headers installed')"
else
msg_warn "$(translate 'Kernel headers install failed — DKMS rebuild will likely fail:') $header_pkg"
fi
fi
msg_info "$(translate 'Running dkms autoinstall for kernel') ${newest_kernel}..."
dkms autoinstall -k "$newest_kernel" >/dev/null 2>&1 || true
local -a failed_components=() failed_modules=()
local i
for i in "${!pending_components[@]}"; do
if ! _pmx_dkms_module_installed_for_kernel "${pending_modules[$i]}" "$newest_kernel"; then
failed_components+=("${pending_components[$i]}")
failed_modules+=("${pending_modules[$i]}")
fi
done
if (( ${#failed_components[@]} == 0 )); then
msg_ok "$(translate 'DKMS drivers rebuilt for kernel') ${newest_kernel}: ${pending_components[*]}"
return 0
fi
msg_warn "$(translate 'dkms autoinstall did not activate:') ${failed_components[*]}"
msg_info "$(translate 'Falling back to each installer with --auto-reinstall...')"
declare -A installer_for=(
[nvidia_driver]="/usr/local/share/proxmenux/scripts/gpu_tpu/nvidia_installer.sh"
[coral_driver]="/usr/local/share/proxmenux/scripts/gpu_tpu/install_coral.sh"
)
local comp installer still_failing=()
for comp in "${failed_components[@]}"; do
installer="${installer_for[$comp]:-}"
[[ -n "$installer" && -x "$installer" ]] || {
still_failing+=("$comp")
continue
}
msg_info "$(translate 'Reinstalling') $comp..."
if bash "$installer" --auto-reinstall >/dev/null 2>&1; then
msg_ok "$(translate 'Reinstalled') $comp"
else
still_failing+=("$comp")
fi
done
if (( ${#still_failing[@]} > 0 )); then
msg_warn "$(translate 'The following drivers could not be rebuilt for the new kernel — run their installer manually after reboot:') ${still_failing[*]}"
else
msg_ok "$(translate 'DKMS drivers reinstalled for kernel') ${newest_kernel}"
fi
return 0
}
+29 -1
View File
@@ -243,9 +243,37 @@ EOF
apply_release_channel() {
local target_channel="$1"
local current_channel installer_file installer_status
local current_channel installer_file installer_status installer_url
current_channel=$(get_release_channel)
if [ "$target_channel" = "beta" ]; then
installer_url="$BETA_INSTALLER_URL"
else
installer_url="$STABLE_INSTALLER_URL"
fi
# Running inside the Monitor's WebSocket terminal: the installer stops
# the Monitor service, which kills this shell mid-install and leaves
# the channel switch broken. Inform and route to SSH / host console.
if [[ "${PROXMENUX_TERMINAL:-}" == "monitor" ]]; then
show_proxmenux_logo
msg_title "$(translate "Changing Release Channel")"
whiptail --title "$(translate "Release Channel")" --msgbox "\
$(translate "Switching to") $(release_channel_label "$target_channel") $(translate "requires running the official installer, which restarts the Monitor service.")
$(translate "This session is running in the Monitor terminal. Running it from here would cut the connection mid-install and leave the switch in a broken state.")
$(translate "Run the release-channel switch from an SSH session or the Proxmox host console with:")
bash -c \"\$(wget -qLO - ${installer_url})\"
$(translate "You can keep using ProxMenux from this terminal.")" 22 78
msg_success "$(translate "Press Enter to return to menu...")"
read -r
exec bash "$LOCAL_SCRIPTS/menus/config_menu.sh"
fi
installer_file=$(mktemp /tmp/proxmenux-${target_channel}-installer.XXXXXX) || return 1
show_proxmenux_logo
+26 -38
View File
@@ -751,57 +751,45 @@ guided_configuration_cleanup() {
setup_persistent_network() {
local LINK_DIR="/etc/systemd/network"
local BACKUP_DIR="/etc/systemd/network/backup-$(date +%Y%m%d-%H%M%S)"
if ! dialog --title "$(translate "Network Interface Setup")" \
--yesno "\n$(translate "Create persistent network interface names?")" 8 60; then
return 1
fi
show_proxmenux_logo
show_proxmenux_logo
msg_info "$(translate "Setting up persistent network interfaces")"
sleep 2
# Create directory
mkdir -p "$LINK_DIR"
# Backup existing files
if ls "$LINK_DIR"/*.link >/dev/null 2>&1; then
mkdir -p "$BACKUP_DIR"
cp "$LINK_DIR"/*.link "$BACKUP_DIR"/ 2>/dev/null || true
fi
# Process physical interfaces
local count=0
for iface in $(ls /sys/class/net/ | grep -vE "lo|docker|veth|br-|vmbr|tap|fwpr|fwln|virbr|bond|cilium|zt|wg"); do
if [[ -e "/sys/class/net/$iface/device" ]] || [[ -e "/sys/class/net/$iface/phy80211" ]]; then
local MAC=$(cat /sys/class/net/$iface/address 2>/dev/null)
if [[ "$MAC" =~ ^([a-fA-F0-9]{2}:){5}[a-fA-F0-9]{2}$ ]]; then
local LINK_FILE="$LINK_DIR/10-$iface.link"
cat > "$LINK_FILE" <<EOF
[Match]
MACAddress=$MAC
[Link]
Name=$iface
EOF
chmod 644 "$LINK_FILE"
((count++))
fi
if ! type pmx_setup_persistent_network &>/dev/null; then
if [[ -f "$LOCAL_SCRIPTS/global/utils-install-functions.sh" ]]; then
source "$LOCAL_SCRIPTS/global/utils-install-functions.sh"
fi
done
if [[ $count -gt 0 ]]; then
fi
local count=0 removed_stale=0 removed_legacy=0
while IFS='=' read -r key value; do
case "$key" in
COUNT) count="$value" ;;
REMOVED_STALE) removed_stale="$value" ;;
REMOVED_LEGACY) removed_legacy="$value" ;;
esac
done < <(pmx_setup_persistent_network)
if (( removed_legacy > 0 )); then
msg_ok "$(translate "Migrated") $removed_legacy $(translate "legacy .link file(s) to the ProxMenux-managed format")"
fi
if (( removed_stale > 0 )); then
msg_ok "$(translate "Reconciled") $removed_stale $(translate "stale entry/entries for interfaces no longer present")"
fi
if (( count > 0 )); then
msg_ok "$(translate "Created persistent names for") $count $(translate "interfaces")"
msg_ok "$(translate "Changes will apply after reboot.")"
else
msg_warn "$(translate "No physical interfaces found")"
fi
register_tool "persistent_network" true
echo -e
msg_success "$(translate "Press ENTER to continue...")"
read -r
register_tool "persistent_network" true
echo -e
msg_success "$(translate "Press ENTER to continue...")"
read -r
}
+116 -54
View File
@@ -497,7 +497,7 @@ force_apt_ipv4() {
# ==========================================================
apply_network_optimizations() {
local FUNC_VERSION="1.0"
local FUNC_VERSION="1.1"
# description: Tune TCP buffers, somaxconn, IPv4 hardening and disable rp_filter on fw bridges (PVE 9 compatible).
msg_info "$(translate "Optimizing network settings...")"
NECESSARY_REBOOT=1
@@ -548,8 +548,38 @@ EOF
sysctl --system > /dev/null 2>&1
cat > /usr/local/sbin/proxmenux-fwbr-tune <<'EOF'
#!/usr/bin/env bash
# Set rp_filter=0 and log_martians=0 on Proxmox fw bridge interfaces.
# No arg → sweep every interface currently under /proc/sys/net/ipv4/conf/.
# One arg → tune only that interface (used by the udev rule).
set -u
cat >/etc/systemd/system/proxmenux-fwbr-tune.service <<'EOF'
tune_interface() {
local iface="$1"
local sysctl_path="/proc/sys/net/ipv4/conf/${iface}"
case "$iface" in
fwbr*|fwln*|fwpr*|tap*)
[[ -d "$sysctl_path" ]] || return 0
[[ -w "$sysctl_path/rp_filter" ]] && printf '0\n' > "$sysctl_path/rp_filter"
[[ -w "$sysctl_path/log_martians" ]] && printf '0\n' > "$sysctl_path/log_martians"
;;
esac
}
if [[ $# -gt 0 ]]; then
tune_interface "$1"
else
for sysctl_path in /proc/sys/net/ipv4/conf/*; do
[[ -d "$sysctl_path" ]] || continue
tune_interface "${sysctl_path##*/}"
done
fi
EOF
chmod 0755 /usr/local/sbin/proxmenux-fwbr-tune
chown root:root /usr/local/sbin/proxmenux-fwbr-tune
cat > /etc/systemd/system/proxmenux-fwbr-tune.service <<'EOF'
[Unit]
Description=ProxMenux - Tune rp_filter/log_martians on virtual fw bridges
After=network-online.target
@@ -557,14 +587,26 @@ Wants=network-online.target
[Service]
Type=oneshot
ExecStart=/bin/bash -c 'for i in /proc/sys/net/ipv4/conf/*; do n=${i##*/}; case "$n" in fwbr*|fwln*|fwpr*|tap*) echo 0 > /proc/sys/net/ipv4/conf/$n/rp_filter; echo 0 > /proc/sys/net/ipv4/conf/$n/log_martians; esac; done'
ExecStart=/usr/local/sbin/proxmenux-fwbr-tune
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
cat > /etc/udev/rules.d/99-proxmenux-fwbr-tune.rules <<'EOF'
ACTION=="add", SUBSYSTEM=="net", KERNEL=="fwbr*", RUN+="/usr/local/sbin/proxmenux-fwbr-tune %k"
ACTION=="add", SUBSYSTEM=="net", KERNEL=="fwln*", RUN+="/usr/local/sbin/proxmenux-fwbr-tune %k"
ACTION=="add", SUBSYSTEM=="net", KERNEL=="fwpr*", RUN+="/usr/local/sbin/proxmenux-fwbr-tune %k"
ACTION=="add", SUBSYSTEM=="net", KERNEL=="tap*", RUN+="/usr/local/sbin/proxmenux-fwbr-tune %k"
EOF
chmod 0644 /etc/udev/rules.d/99-proxmenux-fwbr-tune.rules
chown root:root /etc/udev/rules.d/99-proxmenux-fwbr-tune.rules
systemctl daemon-reload >/dev/null 2>&1 || true
udevadm control --reload-rules >/dev/null 2>&1 || true
systemctl enable --now proxmenux-fwbr-tune.service >/dev/null 2>&1 || true
/usr/local/sbin/proxmenux-fwbr-tune >/dev/null 2>&1 || true
local interfaces_file="/etc/network/interfaces"
@@ -638,7 +680,7 @@ EOF
install_log2ram_auto() {
local FUNC_VERSION="1.2"
local FUNC_VERSION="1.3"
# description: Install Log2RAM with size auto-tuned to host RAM (128M/256M/512M); SSD/M.2 detection skips on rotational disks.
@@ -727,6 +769,52 @@ install_log2ram_auto() {
return 1
fi
# Drop ACL preservation from the upstream rsync call: some
# /var/log.hdd filesystems reject POSIX ACLs and log2ram write
# exits 23 with `set_acl: Operation not supported`. xattrs stay.
local _l2r_bin
for _l2r_bin in \
"$(command -v log2ram 2>/dev/null)" \
/usr/local/bin/log2ram \
/usr/sbin/log2ram \
/usr/bin/log2ram
do
[[ -n "$_l2r_bin" && -f "$_l2r_bin" ]] || continue
if grep -q 'rsync -aAXv ' "$_l2r_bin" 2>/dev/null; then
cp -a "$_l2r_bin" "${_l2r_bin}.proxmenux.bak"
sed -i 's/rsync -aAXv /rsync -aXv --no-acls /g' "$_l2r_bin"
fi
break
done
# Size-based rotation for the PBS API logs — the upstream package
# ships no logrotate rule and pvestatd's local-datastore poll fills
# them fast enough to saturate a tmpfs /var/log.
if dpkg-query -W -f='${Status}' proxmox-backup-server 2>/dev/null \
| grep -q 'install ok installed'; then
mkdir -p /var/log/proxmox-backup/api 2>/dev/null || true
cat > /etc/logrotate.d/proxmox-backup-api <<'EOF'
/var/log/proxmox-backup/api/access.log /var/log/proxmox-backup/api/auth.log {
size 20M
rotate 3
compress
delaycompress
missingok
notifempty
copytruncate
}
EOF
chmod 0644 /etc/logrotate.d/proxmox-backup-api
chown root:root /etc/logrotate.d/proxmox-backup-api
cat > /etc/cron.hourly/proxmox-backup-logrotate <<'EOF'
#!/bin/sh
/usr/sbin/logrotate /etc/logrotate.d/proxmox-backup-api >/dev/null 2>&1
EOF
chmod 0755 /etc/cron.hourly/proxmox-backup-logrotate
chown root:root /etc/cron.hourly/proxmox-backup-logrotate
msg_ok "$(translate "PBS API log rotation configured (hourly, size-based)")"
fi
systemctl enable --now log2ram >/dev/null 2>&1 || true
systemctl daemon-reload >/dev/null 2>&1 || true
@@ -766,13 +854,10 @@ EOF
cat > /usr/local/bin/log2ram-check.sh <<'EOF'
#!/usr/bin/env bash
# v1.2 — `log2ram write` only copies tmpfs→disk; it does NOT shrink
# the tmpfs. When journald or pveproxy/access.log grow past their
# limits the tmpfs hit 100% and PVE crashed with "No space left on
# device" on Shell open (community-reported: JC Miñarro, Nicolás P.
# de A., 17-18/05). We now vacuum the journal and truncate the
# non-rotating logs that actually consume the tmpfs before calling
# `log2ram write`.
# Watch /var/log usage on Log2RAM's tmpfs and act at two thresholds:
# > 80% → vacuum journald down to ~30% of SIZE, then log2ram write
# > 92% → aggressive: journal to ~5%, rotate PBS API logs if present,
# truncate pveproxy/pveam, then log2ram write
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
CONF_FILE="/etc/log2ram.conf"
@@ -793,15 +878,13 @@ LOCK="/run/log2ram-check.lock"
exec 9>"$LOCK" 2>/dev/null || exit 0
flock -n 9 || exit 0
# `log2ram write` alone leaves the tmpfs full. Real recovery requires:
# (a) journal vacuum — journald respects --vacuum-size unconditionally,
# unlike SystemMaxUse which only enforces on rotation boundaries;
# (b) truncating logs that aren't rotated by logrotate (pveproxy, pveam);
# (c) THEN syncing to disk so the persistent copy reflects reality.
if (( USED_BYTES > EMERGENCY_BYTES )); then
SAFE_JOURNAL_MB=$(( SIZE_MiB * 5 / 100 ))
[[ "$SAFE_JOURNAL_MB" -lt 16 ]] && SAFE_JOURNAL_MB=16
journalctl --vacuum-size="${SAFE_JOURNAL_MB}M" >/dev/null 2>&1 || true
if [[ -x /usr/sbin/logrotate && -f /etc/logrotate.d/proxmox-backup-api ]]; then
/usr/sbin/logrotate -f /etc/logrotate.d/proxmox-backup-api >/dev/null 2>&1 || true
fi
: > /var/log/pveproxy/access.log 2>/dev/null || true
: > /var/log/pveproxy/error.log 2>/dev/null || true
: > /var/log/pveam.log 2>/dev/null || true
@@ -820,8 +903,7 @@ EOF
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
MAILTO=""
# Runs every 10 min starting at :03 to avoid overlap with debian-sa1 (:00/:10/:20...)
# nice -n 19 + ionice -c 3 ensures minimum CPU/IO priority (no visible spikes)
# nice/ionice keep the check off the priority queue for scheduled tasks.
3-59/10 * * * * root nice -n 19 ionice -c 3 /usr/local/bin/log2ram-check.sh >/dev/null 2>&1
EOF
chmod 0644 /etc/cron.d/log2ram-auto-sync
@@ -1011,57 +1093,37 @@ enable_zfs_autotrim() {
setup_persistent_network() {
local FUNC_VERSION="1.0"
local FUNC_VERSION="1.1"
# description: Pin NIC names to MAC addresses via systemd .link files so kernel updates don't shuffle interface names.
local LINK_DIR="/etc/systemd/network"
local BACKUP_DIR="/etc/systemd/network/backup-$(date +%Y%m%d-%H%M%S)"
local pve_version
pve_version=$(pveversion 2>/dev/null | grep -oP 'pve-manager/\K[0-9]+' | head -1)
msg_info "$(translate "Setting up persistent network interfaces")"
sleep 2
# Same legacy-conflict warning as in customizable_post_install.sh.
if [[ -f /etc/network/interfaces ]]; then
if grep -qE '^[[:space:]]*allow-hotplug[[:space:]]' /etc/network/interfaces 2>/dev/null; then
msg_warn "$(translate '/etc/network/interfaces uses allow-hotplug. Renaming interfaces via systemd .link can break that flow — review the file after reboot.')"
fi
fi
mkdir -p "$LINK_DIR"
local count=0 removed_stale=0 removed_legacy=0
while IFS='=' read -r key value; do
case "$key" in
COUNT) count="$value" ;;
REMOVED_STALE) removed_stale="$value" ;;
REMOVED_LEGACY) removed_legacy="$value" ;;
esac
done < <(pmx_setup_persistent_network)
if ls "$LINK_DIR"/*.link >/dev/null 2>&1; then
mkdir -p "$BACKUP_DIR"
cp "$LINK_DIR"/*.link "$BACKUP_DIR"/ 2>/dev/null || true
if (( removed_legacy > 0 )); then
msg_ok "$(translate "Migrated") $removed_legacy $(translate "legacy .link file(s) to the ProxMenux-managed format")"
fi
local count=0
for iface in $(ls /sys/class/net/ | grep -vE "lo|docker|veth|br-|vmbr|tap|fwpr|fwln|virbr|bond|cilium|zt|wg"); do
if [[ -e "/sys/class/net/$iface/device" ]] || [[ -e "/sys/class/net/$iface/phy80211" ]]; then
local MAC=$(cat /sys/class/net/$iface/address 2>/dev/null)
if [[ "$MAC" =~ ^([a-fA-F0-9]{2}:){5}[a-fA-F0-9]{2}$ ]]; then
local LINK_FILE="$LINK_DIR/10-$iface.link"
cat > "$LINK_FILE" <<EOF
[Match]
MACAddress=$MAC
[Link]
Name=$iface
EOF
chmod 644 "$LINK_FILE"
((count++))
fi
fi
done
if [[ $count -gt 0 ]]; then
if (( removed_stale > 0 )); then
msg_ok "$(translate "Reconciled") $removed_stale $(translate "stale entry/entries for interfaces no longer present")"
fi
if (( count > 0 )); then
msg_ok "$(translate "Created persistent names for") $count $(translate "interfaces")"
# In PVE9, systemd-networkd is the native network backend and udev processes
# .link files directly. Reloading udev rules makes the new .link files effective
# immediately for any interface added later (hotplug, new NICs) without waiting
# for a full reboot. On PVE8 (ifupdown2), names are resolved at boot anyway.
if [[ "$pve_version" -ge 9 ]]; then
udevadm control --reload-rules 2>/dev/null || true
msg_ok "$(translate "PVE9: udev rules reloaded — new interfaces will get correct names without reboot")"
+168 -122
View File
@@ -778,7 +778,7 @@ force_apt_ipv4() {
apply_network_optimizations() {
local FUNC_VERSION="1.0"
local FUNC_VERSION="1.1"
# description: Tune TCP buffers, somaxconn, IPv4 hardening and disable rp_filter on fw bridges (PVE 9 compatible).
msg_info "$(translate "Optimizing network settings...")"
NECESSARY_REBOOT=1
@@ -839,6 +839,66 @@ EOF
sysctl --system > /dev/null 2>&1
cat > /usr/local/sbin/proxmenux-fwbr-tune <<'EOF'
#!/usr/bin/env bash
# Set rp_filter=0 and log_martians=0 on Proxmox fw bridge interfaces.
# No arg → sweep every interface currently under /proc/sys/net/ipv4/conf/.
# One arg → tune only that interface (used by the udev rule).
set -u
tune_interface() {
local iface="$1"
local sysctl_path="/proc/sys/net/ipv4/conf/${iface}"
case "$iface" in
fwbr*|fwln*|fwpr*|tap*)
[[ -d "$sysctl_path" ]] || return 0
[[ -w "$sysctl_path/rp_filter" ]] && printf '0\n' > "$sysctl_path/rp_filter"
[[ -w "$sysctl_path/log_martians" ]] && printf '0\n' > "$sysctl_path/log_martians"
;;
esac
}
if [[ $# -gt 0 ]]; then
tune_interface "$1"
else
for sysctl_path in /proc/sys/net/ipv4/conf/*; do
[[ -d "$sysctl_path" ]] || continue
tune_interface "${sysctl_path##*/}"
done
fi
EOF
chmod 0755 /usr/local/sbin/proxmenux-fwbr-tune
chown root:root /usr/local/sbin/proxmenux-fwbr-tune
cat > /etc/systemd/system/proxmenux-fwbr-tune.service <<'EOF'
[Unit]
Description=ProxMenux - Tune rp_filter/log_martians on virtual fw bridges
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/proxmenux-fwbr-tune
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
EOF
cat > /etc/udev/rules.d/99-proxmenux-fwbr-tune.rules <<'EOF'
ACTION=="add", SUBSYSTEM=="net", KERNEL=="fwbr*", RUN+="/usr/local/sbin/proxmenux-fwbr-tune %k"
ACTION=="add", SUBSYSTEM=="net", KERNEL=="fwln*", RUN+="/usr/local/sbin/proxmenux-fwbr-tune %k"
ACTION=="add", SUBSYSTEM=="net", KERNEL=="fwpr*", RUN+="/usr/local/sbin/proxmenux-fwbr-tune %k"
ACTION=="add", SUBSYSTEM=="net", KERNEL=="tap*", RUN+="/usr/local/sbin/proxmenux-fwbr-tune %k"
EOF
chmod 0644 /etc/udev/rules.d/99-proxmenux-fwbr-tune.rules
chown root:root /etc/udev/rules.d/99-proxmenux-fwbr-tune.rules
systemctl daemon-reload >/dev/null 2>&1 || true
udevadm control --reload-rules >/dev/null 2>&1 || true
systemctl enable --now proxmenux-fwbr-tune.service >/dev/null 2>&1 || true
/usr/local/sbin/proxmenux-fwbr-tune >/dev/null 2>&1 || true
local interfaces_file="/etc/network/interfaces"
if ! grep -q 'source /etc/network/interfaces.d/*' "$interfaces_file"; then
@@ -1147,86 +1207,58 @@ EOF
optimize_zfs_arc() {
local FUNC_VERSION="1.0"
# description: Cap ZFS ARC to a sensible fraction of host RAM so VMs don't fight the kernel for memory.
msg_info2 "$(translate "Optimizing ZFS ARC size according to available memory...")"
local FUNC_VERSION="1.1"
# description: Cap ZFS ARC max to a sensible fraction of host RAM so VMs don't fight the kernel for memory. Only sets zfs_arc_max; other OpenZFS tunables stay at their defaults.
local zfs_conf="/etc/modprobe.d/99-zfsarc.conf"
local ram_bytes arc_max
# Check if ZFS is installed
if ! command -v zfs > /dev/null; then
msg_info2 "$(translate "Optimizing ZFS ARC maximum size...")"
if ! command -v zpool >/dev/null 2>&1; then
msg_warn "$(translate "ZFS not detected. Skipping ZFS ARC optimization.")"
return 0
fi
# Ensure RAM_SIZE_GB is set
if [ -z "$RAM_SIZE_GB" ]; then
RAM_SIZE_GB=$(free -g | awk '/^Mem:/{print $2}')
if [ -z "$RAM_SIZE_GB" ] || [ "$RAM_SIZE_GB" -eq 0 ]; then
msg_warn "$(translate "Failed to detect RAM size. Using default value of 16GB for ZFS ARC optimization.")"
RAM_SIZE_GB=16 # Default to 16GB if detection fails
fi
if ! zpool list -H -o name 2>/dev/null | grep -q .; then
msg_warn "$(translate "No ZFS pools detected. Skipping ZFS ARC optimization.")"
return 0
fi
msg_ok "$(translate "Detected RAM size: ${RAM_SIZE_GB} GB")"
ram_bytes=$(awk '/MemTotal:/ { print $2 * 1024 }' /proc/meminfo)
if [[ -z "$ram_bytes" || "$ram_bytes" -le 0 ]]; then
msg_error "$(translate "Unable to determine the installed memory.")"
return 1
fi
# Calculate ZFS ARC sizes
if [[ "$RAM_SIZE_GB" -le 16 ]]; then
MY_ZFS_ARC_MIN=536870911 # 512MB
MY_ZFS_ARC_MAX=536870912 # 512MB
elif [[ "$RAM_SIZE_GB" -le 32 ]]; then
MY_ZFS_ARC_MIN=1073741823 # 1GB
MY_ZFS_ARC_MAX=1073741824 # 1GB
if (( ram_bytes <= 16 * 1024 * 1024 * 1024 )); then
arc_max=$((512 * 1024 * 1024))
elif (( ram_bytes <= 32 * 1024 * 1024 * 1024 )); then
arc_max=$((1024 * 1024 * 1024))
else
# Use 1/16 of RAM for min and 1/8 for max
MY_ZFS_ARC_MIN=$((RAM_SIZE_GB * 1073741824 / 16))
MY_ZFS_ARC_MAX=$((RAM_SIZE_GB * 1073741824 / 8))
arc_max=$((ram_bytes / 8))
fi
(( arc_max < 512 * 1024 * 1024 )) && arc_max=$((512 * 1024 * 1024))
if [[ -f "$zfs_conf" && ! -f "${zfs_conf}.bak" ]]; then
cp -p "$zfs_conf" "${zfs_conf}.bak"
fi
# Enforce the minimum values
MY_ZFS_ARC_MIN=$((MY_ZFS_ARC_MIN > 536870911 ? MY_ZFS_ARC_MIN : 536870911))
MY_ZFS_ARC_MAX=$((MY_ZFS_ARC_MAX > 536870912 ? MY_ZFS_ARC_MAX : 536870912))
# Apply ZFS tuning parameters
local zfs_conf="/etc/modprobe.d/99-zfsarc.conf"
local config_changed=false
if [ -f "$zfs_conf" ]; then
msg_info "$(translate "Checking existing ZFS ARC configuration...")"
if ! grep -q "zfs_arc_min=$MY_ZFS_ARC_MIN" "$zfs_conf" || \
! grep -q "zfs_arc_max=$MY_ZFS_ARC_MAX" "$zfs_conf"; then
msg_ok "$(translate "Changes detected. Updating ZFS ARC configuration...")"
cp "$zfs_conf" "${zfs_conf}.bak"
config_changed=true
else
msg_ok "$(translate "ZFS ARC configuration is up to date")"
fi
else
msg_info "$(translate "Creating new ZFS ARC configuration...")"
config_changed=true
fi
if $config_changed; then
cat <<EOF > "$zfs_conf"
# ZFS tuning
# Use 1/8 RAM for MAX cache, 1/16 RAM for MIN cache, or 512MB/1GB for systems with <= 32GB RAM
options zfs zfs_arc_min=$MY_ZFS_ARC_MIN
options zfs zfs_arc_max=$MY_ZFS_ARC_MAX
# Enable prefetch method
options zfs l2arc_noprefetch=0
# Set max write speed to L2ARC (500MB)
options zfs l2arc_write_max=524288000
options zfs zfs_txg_timeout=60
cat > "$zfs_conf" <<EOF
# ProxMenux ZFS ARC configuration
# Only zfs_arc_max is set; zfs_arc_min stays at the OpenZFS default (auto).
options zfs zfs_arc_max=$arc_max
EOF
if [ $? -eq 0 ]; then
msg_ok "$(translate "ZFS ARC configuration file created/updated successfully")"
NECESSARY_REBOOT=1
else
msg_error "$(translate "Failed to create/update ZFS ARC configuration file")"
fi
msg_info "$(translate "Updating initramfs so the ARC cap applies at next boot...")"
if ! update-initramfs -u -k all >/dev/null 2>&1; then
msg_error "$(translate "Failed to update initramfs.")"
return 1
fi
if command -v proxmox-boot-tool >/dev/null 2>&1; then
proxmox-boot-tool refresh >/dev/null 2>&1 || true
fi
NECESSARY_REBOOT=1
msg_ok "$(translate "ZFS ARC maximum configured:") $arc_max $(translate "bytes")"
msg_success "$(translate "ZFS ARC optimization completed")"
register_tool "zfs_arc" true "$FUNC_VERSION"
}
@@ -2493,7 +2525,7 @@ update_pve_appliance_manager() {
configure_log2ram() {
local FUNC_VERSION="1.2"
local FUNC_VERSION="1.3"
# description: Install Log2RAM with user-chosen RAM size; prompts for size and SSD/M.2 awareness before applying.
msg_info2 "$(translate "Preparing Log2RAM configuration")"
sleep 1
@@ -2591,6 +2623,52 @@ configure_log2ram() {
return 1
fi
# Drop ACL preservation from the upstream rsync call: some
# /var/log.hdd filesystems reject POSIX ACLs and log2ram write
# exits 23 with `set_acl: Operation not supported`. xattrs stay.
local _l2r_bin
for _l2r_bin in \
"$(command -v log2ram 2>/dev/null)" \
/usr/local/bin/log2ram \
/usr/sbin/log2ram \
/usr/bin/log2ram
do
[[ -n "$_l2r_bin" && -f "$_l2r_bin" ]] || continue
if grep -q 'rsync -aAXv ' "$_l2r_bin" 2>/dev/null; then
cp -a "$_l2r_bin" "${_l2r_bin}.proxmenux.bak"
sed -i 's/rsync -aAXv /rsync -aXv --no-acls /g' "$_l2r_bin"
fi
break
done
# Size-based rotation for the PBS API logs — the upstream package
# ships no logrotate rule and pvestatd's local-datastore poll fills
# them fast enough to saturate a tmpfs /var/log.
if dpkg-query -W -f='${Status}' proxmox-backup-server 2>/dev/null \
| grep -q 'install ok installed'; then
mkdir -p /var/log/proxmox-backup/api 2>/dev/null || true
cat > /etc/logrotate.d/proxmox-backup-api <<'EOF'
/var/log/proxmox-backup/api/access.log /var/log/proxmox-backup/api/auth.log {
size 20M
rotate 3
compress
delaycompress
missingok
notifempty
copytruncate
}
EOF
chmod 0644 /etc/logrotate.d/proxmox-backup-api
chown root:root /etc/logrotate.d/proxmox-backup-api
cat > /etc/cron.hourly/proxmox-backup-logrotate <<'EOF'
#!/bin/sh
/usr/sbin/logrotate /etc/logrotate.d/proxmox-backup-api >/dev/null 2>&1
EOF
chmod 0755 /etc/cron.hourly/proxmox-backup-logrotate
chown root:root /etc/cron.hourly/proxmox-backup-logrotate
msg_ok "$(translate "PBS API log rotation configured (hourly, size-based)")"
fi
systemctl daemon-reload >/dev/null 2>&1 || true
systemctl enable --now log2ram >/dev/null 2>&1 || true
@@ -2620,13 +2698,10 @@ EOF
if [[ "$ENABLE_AUTOSYNC" == true ]]; then
cat > /usr/local/bin/log2ram-check.sh <<'EOF'
#!/usr/bin/env bash
# v1.2 — `log2ram write` only copies tmpfs→disk; it does NOT shrink
# the tmpfs. When journald or pveproxy/access.log grow past their
# limits the tmpfs hit 100% and PVE crashed with "No space left on
# device" on Shell open (community-reported: JC Miñarro, Nicolás P.
# de A., 17-18/05). We now vacuum the journal and truncate the
# non-rotating logs that actually consume the tmpfs before calling
# `log2ram write`.
# Watch /var/log usage on Log2RAM's tmpfs and act at two thresholds:
# > 80% → vacuum journald down to ~30% of SIZE, then log2ram write
# > 92% → aggressive: journal to ~5%, rotate PBS API logs if present,
# truncate pveproxy/pveam, then log2ram write
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
CONF_FILE="/etc/log2ram.conf"
L2R_BIN="$(command -v log2ram || true)"
@@ -2646,15 +2721,13 @@ LOCK="/run/log2ram-check.lock"
exec 9>"$LOCK" 2>/dev/null || exit 0
flock -n 9 || exit 0
# `log2ram write` alone leaves the tmpfs full. Real recovery requires:
# (a) journal vacuum — journald respects --vacuum-size unconditionally,
# unlike SystemMaxUse which only enforces on rotation boundaries;
# (b) truncating logs that aren't rotated by logrotate (pveproxy, pveam);
# (c) THEN syncing to disk so the persistent copy reflects reality.
if (( USED_BYTES > EMERGENCY_BYTES )); then
SAFE_JOURNAL_MB=$(( SIZE_MiB * 5 / 100 ))
[[ "$SAFE_JOURNAL_MB" -lt 16 ]] && SAFE_JOURNAL_MB=16
journalctl --vacuum-size="${SAFE_JOURNAL_MB}M" >/dev/null 2>&1 || true
if [[ -x /usr/sbin/logrotate && -f /etc/logrotate.d/proxmox-backup-api ]]; then
/usr/sbin/logrotate -f /etc/logrotate.d/proxmox-backup-api >/dev/null 2>&1 || true
fi
: > /var/log/pveproxy/access.log 2>/dev/null || true
: > /var/log/pveproxy/error.log 2>/dev/null || true
: > /var/log/pveam.log 2>/dev/null || true
@@ -2763,64 +2836,37 @@ EOF
setup_persistent_network() {
local FUNC_VERSION="1.0"
local FUNC_VERSION="1.1"
# description: Pin NIC names to MAC addresses via systemd .link files so kernel updates don't shuffle interface names.
local LINK_DIR="/etc/systemd/network"
local BACKUP_DIR="/etc/systemd/network/backup-$(date +%Y%m%d-%H%M%S)"
local pve_version
pve_version=$(pveversion 2>/dev/null | grep -oP 'pve-manager/\K[0-9]+' | head -1)
msg_info "$(translate "Setting up persistent network interfaces")"
sleep 2
# Detect legacy `/etc/network/interfaces` setups that depend on the
# default udev naming. If the file references `allow-hotplug` rules
# or uses physical interface names directly, the .link rules below
# could rename interfaces and break network on reboot. Warn loudly so
# the user can review before continuing. Audit Tier 6 —
# `setup_persistent_network` no detecta conflicto con legacy.
if [[ -f /etc/network/interfaces ]]; then
if grep -qE '^[[:space:]]*allow-hotplug[[:space:]]' /etc/network/interfaces 2>/dev/null; then
msg_warn "$(translate '/etc/network/interfaces uses allow-hotplug. Renaming interfaces via systemd .link can break that flow — review the file after reboot.')"
fi
fi
mkdir -p "$LINK_DIR"
local count=0 removed_stale=0 removed_legacy=0
while IFS='=' read -r key value; do
case "$key" in
COUNT) count="$value" ;;
REMOVED_STALE) removed_stale="$value" ;;
REMOVED_LEGACY) removed_legacy="$value" ;;
esac
done < <(pmx_setup_persistent_network)
if ls "$LINK_DIR"/*.link >/dev/null 2>&1; then
mkdir -p "$BACKUP_DIR"
cp "$LINK_DIR"/*.link "$BACKUP_DIR"/ 2>/dev/null || true
if (( removed_legacy > 0 )); then
msg_ok "$(translate "Migrated") $removed_legacy $(translate "legacy .link file(s) to the ProxMenux-managed format")"
fi
# Process physical interfaces
local count=0
for iface in $(ls /sys/class/net/ | grep -vE "lo|docker|veth|br-|vmbr|tap|fwpr|fwln|virbr|bond|cilium|zt|wg"); do
if [[ -e "/sys/class/net/$iface/device" ]] || [[ -e "/sys/class/net/$iface/phy80211" ]]; then
local MAC=$(cat /sys/class/net/$iface/address 2>/dev/null)
if [[ "$MAC" =~ ^([a-fA-F0-9]{2}:){5}[a-fA-F0-9]{2}$ ]]; then
local LINK_FILE="$LINK_DIR/10-$iface.link"
cat > "$LINK_FILE" <<EOF
[Match]
MACAddress=$MAC
[Link]
Name=$iface
EOF
chmod 644 "$LINK_FILE"
((count++))
fi
fi
done
if [[ $count -gt 0 ]]; then
if (( removed_stale > 0 )); then
msg_ok "$(translate "Reconciled") $removed_stale $(translate "stale entry/entries for interfaces no longer present")"
fi
if (( count > 0 )); then
msg_ok "$(translate "Created persistent names for") $count $(translate "interfaces")"
# In PVE9, systemd-networkd is the native network backend and udev processes
# .link files directly. Reloading udev rules makes the new .link files effective
# immediately for any interface added later (hotplug, new NICs) without waiting
# for a full reboot. On PVE8 (ifupdown2), names are resolved at boot anyway.
if [[ "$pve_version" -ge 9 ]]; then
udevadm control --reload-rules 2>/dev/null || true
msg_ok "$(translate "PVE9: udev rules reloaded — new interfaces will get correct names without reboot")"
+26 -13
View File
@@ -455,11 +455,14 @@ uninstall_network_optimization() {
systemctl disable --now proxmenux-fwbr-tune.service >/dev/null 2>&1 || true
rm -f /etc/systemd/system/proxmenux-fwbr-tune.service
rm -f /usr/local/sbin/proxmenux-fwbr-tune
rm -f /etc/udev/rules.d/99-proxmenux-fwbr-tune.rules
udevadm control --reload-rules >/dev/null 2>&1 || true
systemctl daemon-reload >/dev/null 2>&1 || true
sysctl --system >/dev/null 2>&1 || true
msg_ok "$(translate "Network optimizations removed")"
register_tool "network_optimization" false
}
@@ -573,22 +576,28 @@ uninstall_log2ram() {
################################################################
uninstall_persistent_network() {
local LINK_DIR="/etc/systemd/network"
msg_info "$(translate "Removing all .link files from") $LINK_DIR"
sleep 2
if ! ls "$LINK_DIR"/*.link >/dev/null 2>&1; then
msg_warn "$(translate "No .link files found in") $LINK_DIR"
return 0
msg_info "$(translate "Removing ProxMenux persistent NIC .link files...")"
sleep 1
if ! type pmx_uninstall_persistent_network &>/dev/null; then
if [[ -f "$LOCAL_SCRIPTS/global/utils-install-functions.sh" ]]; then
source "$LOCAL_SCRIPTS/global/utils-install-functions.sh"
fi
fi
rm -f "$LINK_DIR"/*.link
local removed=0
while IFS='=' read -r key value; do
[[ "$key" == "REMOVED" ]] && removed="$value"
done < <(pmx_uninstall_persistent_network)
msg_ok "$(translate "Removed all .link files from") $LINK_DIR"
msg_info "$(translate "Interface names will return to default systemd behavior.")"
if (( removed > 0 )); then
msg_ok "$(translate "Removed") $removed $(translate "ProxMenux-managed .link file(s). User-authored .link files were left in place.")"
msg_info "$(translate "Interface names will return to default systemd behavior.")"
NECESSARY_REBOOT=1
else
msg_warn "$(translate "No ProxMenux-managed .link files found — nothing to remove.")"
fi
register_tool "persistent_network" false
NECESSARY_REBOOT=1
}
@@ -906,6 +915,10 @@ uninstall_zfs_arc() {
rm -f /etc/modprobe.d/99-zfsarc.conf
msg_ok "$(translate 'ZFS ARC config removed (kernel defaults will apply on reboot)')"
fi
update-initramfs -u -k all >/dev/null 2>&1 || true
if command -v proxmox-boot-tool >/dev/null 2>&1; then
proxmox-boot-tool refresh >/dev/null 2>&1 || true
fi
register_tool "zfs_arc" false
}
+57 -34
View File
@@ -6,32 +6,26 @@
# Copyright : (c) 2024 MacRimi
# License : GPL-3.0
# https://github.com/MacRimi/ProxMenux/blob/main/LICENSE
# Version : 1.0
# Version : 1.1
# ==========================================================
# Description:
# Wrapper that detects the running Proxmox major version and
# delegates to the matching worker script:
# - PVE 8 -> scripts/global/update-pve8.sh
# - PVE 9 -> scripts/global/update-pve9_2.sh
# After the worker finishes, runs the post-update cleanup
# (apt-get autoremove + autoclean) and prompts for an immediate
# reboot if the kernel was updated or /var/run/reboot-required
# was created.
# Update wrapper for a Proxmox host ALREADY in production.
# Delegates to `scripts/global/update-pve-safe.sh`, a non-
# invasive worker that only performs operations safe for a
# configured host — no repo overwriting, no service purging,
# no forced package installs. See the header of the worker
# for the full list of what it does and what it deliberately
# refuses to do.
#
# Features (delegated to worker scripts):
# - APT repository hygiene (Proxmox + Debian)
# - Removal of duplicate / conflicting sources
# - Switch to the no-subscription Proxmox repository
# - Full apt update + dist-upgrade
# - Installs essential packages if missing (zfsutils, chrony, ...)
# - LVM / storage sanity checks and header repair
# - Removes conflicting time-sync packages
# - Post-update system cleanup
# - Reboot prompt when kernel changed
# ==========================================================
# After the worker finishes this script:
# - Runs apt-get autoremove + autoclean (final cleanup)
# - Prompts for an immediate reboot when the kernel was
# updated or /var/run/reboot-required was created
#
# The goal of this script is to simplify and secure the update process for Proxmox,
# reduce manual intervention, and prevent common repository and package errors.
# NOTE: For a fresh Proxmox install use post_install
# (scripts/post_install/{auto,customizable}_post_install.sh),
# which invokes the aggressive `update-pve{8,9_2}.sh` workers
# that set up repositories and essentials from scratch.
# ==========================================================
BASE_DIR="/usr/local/share/proxmenux"
@@ -51,6 +45,23 @@ export SCRIPT_TITLE="Proxmox system update"
NECESSARY_REBOOT=1
# Suppress the Monitor's `service_fail` notifications while apt is running.
# PVE services (pve-cluster, pveproxy, pvedaemon, corosync…) get killed and
# restarted as part of the upgrade — those events are expected, not real
# failures. `notification_events.is_apt_active_on_host()` reads these
# markers; keep the names in sync between the two files.
_PROXMENUX_UPDATE_MARKER="/var/run/proxmenux-update-in-progress"
_PROXMENUX_UPDATE_FINISHED_MARKER="/var/run/proxmenux-update-just-finished"
_proxmenux_update_cleanup() {
rm -f "$_PROXMENUX_UPDATE_MARKER"
# Grace-window marker: journal events landing shortly after apt exits
# (e.g. the pve-cluster restart) are still gated on this file's mtime.
touch "$_PROXMENUX_UPDATE_FINISHED_MARKER"
}
trap _proxmenux_update_cleanup EXIT
touch "$_PROXMENUX_UPDATE_MARKER"
apt_upgrade() {
local pve_version pve_raw
# Capture both stdout and the rc so a failure is visible in the
@@ -70,17 +81,11 @@ apt_upgrade() {
return 1
fi
if [[ "$pve_version" -ge 9 ]]; then
show_proxmenux_logo
msg_title "$(translate "$SCRIPT_TITLE")"
bash "$LOCAL_SCRIPTS/global/update-pve9_2.sh"
else
show_proxmenux_logo
msg_title "$(translate "Proxmox system update")"
bash "$LOCAL_SCRIPTS/global/update-pve8.sh"
fi
show_proxmenux_logo
msg_title "$(translate "$SCRIPT_TITLE")"
# Single worker for both PVE 8 and 9 — it detects the version itself
# and only performs operations safe on a production host.
bash "$LOCAL_SCRIPTS/global/update-pve-safe.sh"
}
@@ -90,10 +95,28 @@ apt_upgrade() {
check_reboot() {
NECESSARY_REBOOT=0
# Standard Debian mechanism — needs `needrestart` (or a package that
# explicitly creates it in its postinst) to be present. Not shipped
# by default on many PVE installs, so we complement with the kernel
# fallback below.
if [ -f /var/run/reboot-required ]; then
NECESSARY_REBOOT=1
fi
if grep -q "linux-image" "$log_file" 2>/dev/null; then
# Fallback: compare the running kernel with the most recent
# proxmox-kernel-* / pve-kernel-* installed. If a newer kernel package
# is on disk but the box is still on the old one, a reboot is required
# regardless of whether needrestart flagged it.
local running_kernel newest_kernel
running_kernel=$(uname -r)
newest_kernel=$(
dpkg-query -W -f='${Status}\t${Package}\n' 'proxmox-kernel-*-pve-signed' 'pve-kernel-*-pve' 2>/dev/null \
| awk -F'\t' '/^install ok installed\t/ { print $2 }' \
| sed -E 's/^(proxmox|pve)-kernel-//; s/-signed$//' \
| sort -V \
| tail -1
)
if [ -n "$newest_kernel" ] && [ "$newest_kernel" != "$running_kernel" ]; then
NECESSARY_REBOOT=1
fi
@@ -85,7 +85,7 @@ export default async function ShowVersionInformationPage({
</div>
<h2 className="text-2xl font-semibold mt-10 mb-4 text-gray-900">{t("sampleHeading")}</h2>
<pre className="rounded-md bg-gray-100 p-4 overflow-x-auto text-xs font-mono text-gray-800 leading-relaxed border border-gray-200">{`Current ProxMenux version: 1.2.3
<pre className="rounded-md bg-gray-100 p-4 overflow-x-auto text-xs font-mono text-gray-800 leading-relaxed border border-gray-200">{`Current ProxMenux version: 1.2.4
Installation type:
Translation Version (Multi-language support)
+138 -8
View File
@@ -1,4 +1,134 @@
## 2026-07-17
### Nueva versión ProxMenux v1.2.4
Esta versión suma dos mejoras visibles en el propio dashboard — un botón para lanzar la actualización de Proxmox desde el Monitor de Salud, y un prompt para invitar a los usuarios móviles a instalar el Monitor como PWA — extiende el flujo de restauración de Backups con snapshots consistentes de pmxcfs (`config.db`) e importación automática de pools ZFS de datos, refina el comportamiento de Log2RAM en hosts que corren Proxmox Backup Server como servicio, refuerza el ajuste de sysctl de los bridges del firewall en todo el ciclo de vida de VMs, ajusta la optimización de ZFS ARC a su propio ámbito, hace idempotentes los nombres persistentes de NIC en re-ejecuciones, recompila automáticamente los drivers DKMS cuando entra un kernel nuevo, mantiene intacta la sesión de la terminal del Monitor cuando hay update de ProxMenux disponible, y afina cinco plantillas de notificaciones y tres chequeos del panel de salud.
---
## 🩺 Botón Update Now en el Monitor de Salud
- Nuevo botón **Update Now** dentro del modal del Monitor de Salud, en la sección **System Updates**.
- Ejecuta el flujo estándar de actualización de Proxmox (`apt update` + `dist-upgrade` + limpieza posterior) en una terminal dentro del dashboard — sin necesidad de abrir una shell.
- Solo aparece cuando hay actualizaciones pendientes; si el sistema está al día, el botón queda oculto.
- Al cerrar, el Monitor de Salud fuerza un refresh con invalidación de caché (`/api/health/full?refresh=1`) para que el contador de actualizaciones pendientes y la fila del kernel reflejen el estado post-actualización al instante, en lugar del valor pre-actualización que la caché de fondo había guardado justo antes de la ejecución.
- El script subyacente distingue el contexto: si se ejecuta sobre un host ya en producción, respeta los repositorios personalizados del usuario (no desactiva enterprise/ceph, no borra sources legacy, no purga NTP alternativos ni fuerza instalar zfsutils/chrony); si detecta un servidor virgen crea los repos base necesarios. Detecta también si hay un kernel nuevo instalado distinto del que está corriendo y pide el reboot al terminar.
- Durante la actualización se suprimen las notificaciones de `service_fail` de servicios PVE (pve-cluster, pveproxy, corosync…) porque su reinicio es parte normal del upgrade; volvían a notificarse hasta 60 s después de que apt termine.
---
## 📱 Prompt de instalación en la app para móvil
- Los visitantes por primera vez en **Android** (Chrome / Brave) e **iOS Safari** ven ahora una bottom-sheet con instrucciones claras para añadir el Monitor a su pantalla de inicio como PWA.
- La instalación se hace a través de la propia entrada del menú del navegador ("Añadir a pantalla de inicio"), que produce una PWA real instalada que se lanza en modo standalone. La bottom-sheet no intercepta el evento `beforeinstallprompt` del navegador — interceptarlo sin llegar a llamar a `prompt()` degrada la ruta manual del menú a un acceso directo simple, que es lo que apareció en las pruebas de campo.
- Dos niveles de descarte: **Not now** (temporal, reaparece a los 30 días) y **Don't show again** (permanente, almacenado en `localStorage`).
- No se muestra en escritorio, ni cuando el Monitor ya se está ejecutando como aplicación instalada.
---
## 🔔 Contenido de notificaciones — cinco refinamientos de renderizado
- **Destino de backup en título y cuerpo.** Los correos y mensajes de Telegram de backup de VM/CT llevan el storage / destino PBS, de modo que en instalaciones con varios destinos se identifica de un vistazo qué backup produjo el evento.
- **Los cuerpos de migración llevan el nodo destino real** — extraído del log de la tarea PVE para eventos `qmigrate` / `vzmigrate`.
- **Los cuerpos de snapshot llevan el nombre real del snapshot** — extraído del log de la tarea PVE para eventos `qmsnapshot` / `vzsnapshot`.
- **Las notificaciones genéricas `system_problem` incluyen la razón real** — el cuerpo del payload de PVE se surface como cuerpo de la notificación.
- **Los correos de actualización de driver NVIDIA / Coral renderizan correctamente la fila *New Version*** — el placeholder del template está ahora alineado con el campo que lee el renderer.
---
## 🩹 Panel de salud — tres chequeos reforzados
- **Dismiss silencia ahora las alertas de storage.** El flujo de acknowledge cubre `storage_unavailable`, `mount_stale`, `mount_readonly`, `lxc_disk_low`, `lxc_mount_low`, `pve_storage_full` y `zfs_pool_full` bajo la categoría `storage`, y el caché de storage se invalida al pulsar Dismiss para que el panel se refresque inmediatamente.
- **El chequeo de VMs & Containers tolera errores persistidos con la columna `details` a NULL** (#255). `_check_vms_cts_with_persistence` coalesciona un `details` ausente a un dict vacío antes de leer claves anidadas, de modo que una fila sparse suelta ya no deja el chequeo completo fuera de servicio.
- **La notificación `system_startup` se dispara una sola vez por boot.** `_check_startup_aggregation` marca la agregación como completada justo después de encolar el evento, así que el resumen de arranque llega una única vez independientemente de cuántos ticks de polling entren en la sesión.
---
## 🛠 Móvil y webhook
- **El polling del dashboard en móvil se mantiene vivo sobre HTTPS + reverse proxy.** `pwa-register.tsx` auto-desregistra cualquier Service Worker al cargar, así que la limitación en segundo plano de los navegadores móviles deja de interferir con los fetch de polling, y la instalabilidad PWA se gestiona ahora desde el nuevo prompt in-app descrito arriba.
- **La autenticación del webhook confía en todas las IPs locales del host.** El webhook interno (`/api/notifications/webhook`) acepta peticiones desde cualquier IP de una interfaz que el host posee (Tailscale, WireGuard, LAN, IPv6, más la forma IPv4 mapeada en IPv6 `::ffff:x.x.x.x` que Flask reporta en bindings dual-stack), de modo que los botones Test de PVE funcionan a través de cualquiera de ellas.
---
## 🛡 Flujo de actualización — Monitor-terminal-aware para el update y para el cambio de canal
- **La terminal WebSocket del Monitor expone ahora `PROXMENUX_TERMINAL=monitor`** en el entorno de cada shell que abre, y cada proceso hijo lo hereda. Esto le da a `menu` (y a cualquier otro flow que le importe) una vía fiable y determinista para saber que la sesión actual vive dentro del proceso del Monitor — una sesión que quedaría cortada a mitad de instalación si el servicio del Monitor se reiniciara.
- **Prompt de update de `menu`** — cuando hay una nueva versión de ProxMenux disponible y la sesión corre dentro de la terminal del Monitor, el clásico prompt yes/no se sustituye por un msgbox informativo. El msgbox nombra la nueva versión y muestra el comando canónico de una línea (`bash -c "$(wget -qLO - …)"`) para ejecutar el update desde SSH o la consola del host Proxmox. Como el flow ya ha decidido que el path in-terminal es inseguro, hay un solo botón OK — no hay yes/no que pudiera disparar el update destructivo por accidente.
- **Ajustes → Canal de release** — el mismo guard se aplica en `apply_release_channel()` de `config_menu.sh`. Seleccionar Stable ↔ Beta desde la terminal del Monitor muestra un msgbox informativo con el comando `wget` exacto para el canal destino (usando la misma URL que el flow habría descargado por su cuenta) y vuelve al menú en lugar de ejecutar el installer en el sitio.
- **Tras pulsar OK, ambos flows continúan con normalidad.** El usuario sigue usando ProxMenux desde la misma terminal sin restricciones; solo el paso destructivo queda enrutado a otro sitio. No hay bloqueo ni acción forzada.
- **Las sesiones SSH, la consola del host Proxmox y cualquier entorno donde `PROXMENUX_TERMINAL` no sea `monitor` mantienen el comportamiento anterior** y pueden actualizar o cambiar de canal como siempre. El cambio solo afecta al caso en el que hacerlo en el sitio rompería la sesión activa.
- **Nota de bootstrap**: como `PROXMENUX_TERMINAL=monitor` lo añade el AppImage que ship-ea esta release, el guard solo empieza a proteger sesiones una vez que el host está en 1.2.4 o posterior. La primera actualización a 1.2.4, si se dispara desde la terminal del Monitor, aún puede caer en el comportamiento antiguo — a partir de 1.2.4 el guard queda en su sitio.
---
## 🔧 Flujo de actualización — Drivers DKMS recompilados cuando entra un kernel nuevo
- **Cuando `apt full-upgrade` deja preparado un kernel más nuevo que el que está en marcha, `update-pve-safe.sh` recompila ahora los drivers DKMS instalados por ProxMenux contra ese nuevo kernel.** El botón Update Now del Monitor de Salud y la utilidad CLI `utilities/proxmox_update.sh` delegan ambos en `update-pve-safe.sh`, así que las dos vías ganan el comportamiento. El paso lee `components_status.json`, cruza los componentes DKMS que ProxMenux gestiona (`nvidia_driver`, `coral_driver`), instala las cabeceras de kernel correspondientes (`proxmox-headers-<newkver>` o `pve-headers-<newkver>`) si no están ya presentes, y ejecuta `dkms autoinstall -k <newkver>`. Después verifica vía `dkms status` que cada módulo esperado (`gasket` para Coral, `nvidia` para el driver NVIDIA) haya alcanzado el estado `installed` para el nuevo kernel — si alguno no lo hizo, cae al camino `--auto-reinstall` de cada instalador.
- **Un whiptail msgbox anuncia la recompilación antes de ejecutarla.** Un solo botón OK — no hay yes/no. Nombra el kernel entrante y lista los componentes DKMS que se van a recompilar, de modo que el usuario ve exactamente qué está a punto de suceder. Dado que dejar los drivers DKMS sin recompilar dejaría el sistema con un kernel funcional pero TPU / GPU no operativos al arrancar, se trata de transparencia, no de decisión — al pulsar OK se reconoce el trabajo posterior y el flujo procede. Las invocaciones no interactivas (cron, batch sin terminal, sin whiptail) omiten el msgbox y registran la misma información.
- **Solo se consideran componentes registrados como `installed` en `components_status.json`.** Un host sin drivers DKMS gestionados por ProxMenux no ve msgbox ni paso de recompilación. Los hosts que nunca ejecutaron el instalador de Coral o NVIDIA no se ven afectados.
- **Si la recompilación falla, no se aborta la actualización.** Si un módulo DKMS no puede recompilarse contra el nuevo kernel (ruptura de API upstream, dependencia faltante), el flujo de actualización termina normalmente, se nombran los componentes concretos que fallaron en el resumen, y el usuario puede re-ejecutar su instalador a mano tras el reboot. El paso es best-effort por diseño — un desajuste kernel/driver es un problema upstream, no algo que el flujo de actualización deba tratar como fallo.
- El helper compartido `pmx_rebuild_dkms_after_kernel` vive en `scripts/global/utils-install-functions.sh`, de modo que futuros actualizadores o utilidades CLI pueden reutilizarlo con una única llamada.
---
## 🔌 Post-install — Nombres persistentes de NIC ahora idempotentes
- **Los ficheros `.link` gestionados por ProxMenux llevan ahora un prefijo distintivo y un marcador interno.** Se escriben como `10-proxmenux-<iface>.link` y la primera línea de cada fichero es `# Managed by ProxMenux — do not edit`. Ambos se comprueban en los pasos de reconciliación y desinstalación antes de tocar un fichero, de modo que cualquier cosa que el usuario haya escrito a mano o que venga de otro paquete queda a salvo.
- **Cada re-ejecución de `setup_persistent_network` reconcilia las entradas de ProxMenux.** En cada invocación se recorren los ficheros `10-proxmenux-*.link` existentes, se extrae el valor `MACAddress=`, se compara con las MAC actualmente presentes en `/sys/class/net/`, y se eliminan únicamente las entradas propiedad de ProxMenux cuya MAC ya no está. Reemplazos de tarjetas, cambios de NIC y migraciones de hardware dejan de acumular mapeos huérfanos en cada re-ejecución.
- **Los ficheros con formato 1.0 (`10-<iface>.link`, escritos por la versión anterior) se migran automáticamente en la primera ejecución de la nueva función.** Si el fichero coincide con la plantilla exacta que usaba el código 1.0 (dos secciones, `MACAddress=` + `Name=`, nada más), se elimina y se reemplaza por el `10-proxmenux-<iface>.link` en un solo paso. Cualquier fichero que no coincida con esa plantilla exacta se deja intacto.
- **El desinstalador (`uninstall_persistent_network`) elimina ahora solamente ficheros que cumplan a la vez el prefijo `10-proxmenux-` y el marcador en la primera línea.** El anterior barrido `rm -f /etc/systemd/network/*.link` desaparece — los `.link` escritos por el usuario permanecen en su sitio con independencia de su nombre.
- **Implementación única compartida.** Las tres copias de `setup_persistent_network` (`auto_post_install.sh`, `customizable_post_install.sh`, `network_menu.sh`) y el desinstalador delegan ahora en `pmx_setup_persistent_network` / `pmx_uninstall_persistent_network` en `scripts/global/utils-install-functions.sh`. Los futuros arreglos no pueden dejar una copia atrás.
- Se sube `FUNC_VERSION` de 1.0 → 1.1 en las tres llamadas, de modo que el detector de actualizaciones de ProxMenux vuelve a ejecutar la función en hosts que ya tenían la 1.0. Esa primera re-ejecución hace la migración legacy y la reconciliación en un solo paso.
---
## 🧮 Post-install — Optimización de ZFS ARC ajustada a su ámbito
- **`optimize_zfs_arc` establece ahora únicamente `zfs_arc_max`.** La función escribe una sola línea en `/etc/modprobe.d/99-zfsarc.conf`: `options zfs zfs_arc_max=<cap>`. `zfs_arc_min` queda en el valor por defecto de OpenZFS (auto-calculado como el mayor entre 32 MiB y ~1/32 de la RAM), y los tunables de L2ARC (`l2arc_noprefetch`, `l2arc_write_max`) y de TXG (`zfs_txg_timeout`) — que quedan fuera del ámbito de una optimización del ARC — se dejan en los valores por defecto de OpenZFS salvo que el usuario los configure explícitamente.
- **Se regenera ahora el initramfs tras escribir el fichero.** En sistemas con ZFS-on-root el módulo ZFS se carga desde el initramfs antes de que el sistema en marcha lea `/etc/modprobe.d/`, así que un simple reboot no bastaba para que el nuevo cap surtiera efecto. `update-initramfs -u -k all` se ejecuta justo después de escribir el fichero, más `proxmox-boot-tool refresh` en hosts con systemd-boot, de modo que el valor se aplica en el siguiente arranque en lugar de quedar sombreado por la copia obsoleta del initramfs.
- **La función se protege con la presencia de un pool ZFS vivo** (chequeo `zpool list`), de manera que se convierte en no-op en hosts que no usan ZFS.
- **Los valores del cap usan tamaños binarios limpios**: 512 MiB hasta 16 GB de RAM, 1 GiB hasta 32 GB, RAM/8 por encima — con un suelo de 512 MiB para que una lectura defectuosa de memoria no deje un ARC inutilizablemente pequeño.
- Se sube `FUNC_VERSION` de 1.0 → 1.1, de modo que el detector de actualizaciones de ProxMenux vuelve a ejecutar la función en hosts que ya tenían la 1.0. Como la escritura es una reescritura completa de `99-zfsarc.conf`, ejecutar la función actualizada una vez reemplaza el fichero entero limpiamente. El desinstalador ejecuta ahora también `update-initramfs` + `proxmox-boot-tool refresh` tras restaurar o eliminar la configuración, de modo que la reversión se propaga al initramfs de la misma manera.
---
## 🔥 Post-install — Ajuste de sysctl en los bridges del firewall reforzado
- **El ajuste de `rp_filter=0` y `log_martians=0` sobre las interfaces del firewall bridge (`fwbr*`, `fwln*`, `fwpr*`, `tap*`) se aplica ahora también a las interfaces que Proxmox crea al arrancar, parar, reiniciar o migrar una VM.** Se añade `/etc/udev/rules.d/99-proxmenux-fwbr-tune.rules`, que dispara un helper por cada evento `net`/`add` matcheando esos prefijos, de modo que cada interfaz nueva obtiene el valor correcto inmediatamente sin necesidad de reboot ni de reejecutar el post-install.
- **La lógica de ajuste se ha reorganizado en un helper independiente** en `/usr/local/sbin/proxmenux-fwbr-tune`, compartido por el barrido inicial (servicio `proxmenux-fwbr-tune.service`, tipo oneshot) y por la regla udev. Al instalar se hace además un barrido explícito para que la sesión actual vea el cambio sin esperar al siguiente ciclo de VM.
- **El flujo configurable (`customizable_post_install.sh`) incorpora ahora el mismo helper + servicio + regla udev + barrido inicial que el flujo automático**, para que ambos variantes de post-install dejen el sistema en el mismo estado.
- Ambas funciones `apply_network_optimizations` suben `FUNC_VERSION` de 1.0 → 1.1, de modo que el detector de actualizaciones de ProxMenux vuelve a ejecutar la función en hosts que ya tenían la 1.0. El desinstalador (`uninstall_network_optimization`) se amplía para borrar el nuevo helper y la regla udev, y recargar udev.
---
## 🧰 Post-install — Log2RAM + PBS
- **Rotación automática de los logs de la API de PBS cuando `proxmox-backup-server` corre como servicio en el host.** Ambos instaladores de Log2RAM (`install_log2ram_auto` y el configurable `configure_log2ram`) detectan PBS mediante `dpkg-query` y dejan `/etc/logrotate.d/proxmox-backup-api` con una regla de rotación por tamaño (20MB × 3 copias) más `/etc/cron.hourly/proxmox-backup-logrotate`. En un host PVE que además ejecuta PBS como servicio, `pvestatd` sondea el datastore local cada pocos segundos y cada sondeo escribe en `/var/log/proxmox-backup/api/access.log` y `auth.log` — el paquete upstream de PBS no incluye regla de logrotate para esos ficheros, y esta regla los mantiene acotados de modo que un `/var/log` respaldado por tmpfs permanece cómodamente dentro del presupuesto. En hosts sin PBS como servicio no se crea nada.
- **Script upstream `log2ram` parcheado a `rsync -aXv --no-acls` justo después de `install.sh`.** Ambos instaladores reescriben la llamada en el sitio con un `sed` guardado por `grep -q` (deja backup en `.proxmenux.bak`, no-op si una futura release upstream ya no incluye `-A`). Los atributos extendidos (`-X`) se preservan. Resultado: `log2ram write` finaliza limpio en sistemas de ficheros de `/var/log.hdd` que no aceptan ACLs POSIX (ZFS con `acltype=off`, ext4 montado sin la opción `acl`) — sin mensajes de `set_acl: Operation not supported` / salida 23.
- **El bloque de emergencia de `log2ram-check.sh` rota los logs de PBS antes de truncar.** Cuando `/var/log` cruza el umbral del 92%, el script de auto-sync ejecuta ahora `logrotate -f /etc/logrotate.d/proxmox-backup-api` (solo si el fichero de regla existe) *antes* de truncar `pveproxy/access.log`, `pveproxy/error.log` y `pveam.log`. El historial reciente de accesos/autenticación de PBS se conserva en los `.gz` rotados en lugar de perderse. Se subió `FUNC_VERSION` de 1.2 → 1.3 en `install_log2ram_auto` y `configure_log2ram`, y el comentario de cabecera del `log2ram-check.sh` embebido de v1.2 → v1.3.
## 🗄 Restauración de backup — snapshot de pmxcfs + pools ZFS de datos
- **`/var/lib/pve-cluster/config.db` se captura ahora con `sqlite3 .backup`.** pmxcfs (`/etc/pve`) es servido por `pve-cluster` desde ese almacén SQLite, así que un rsync directo del fichero raw con el servicio en marcha puede pillarlo a mitad de checkpoint WAL y aterrizar en el archivo como una copia inconsistente. `hb_prepare_staging` ejecuta ahora `sqlite3 /var/lib/pve-cluster/config.db ".backup '$staging/…/config.db'"` antes del rsync general — la vía canónica (documentada por Proxmox) para snapshotear el almacén de forma consistente mientras `pve-cluster` sigue sirviendo tráfico, con downtime cero para el cluster. El rsync general de `/var/lib/pve-cluster` excluye ahora `config.db`, `config.db-wal` y `config.db-shm` para que nada sobrescriba el dump consistente. Los hosts sin `sqlite3` recurren a una copia raw llamada `config.db.raw-fallback`, que el helper de recuperación promociona a `config.db` antes de arrancar `pve-cluster`. Los metadatos registran qué vía se usó vía `pmxcfs_config_db=sqlite_backup|raw_fallback` en `metadata/run_info.env` para trazabilidad. La ruta de restauración sigue usando el patrón canónico `systemctl stop pve-cluster → cp → systemctl start pve-cluster` (`apply_pending_restore.sh` y el helper de recuperación standalone que se escribe junto a cada directorio de cluster extraído), así que la DB que el usuario trae de vuelta es ahora consistente garantizada en lugar de una copia raw de estado en vuelo.
- **Los pools ZFS de datos listados en el backup ahora se importan automáticamente durante la restauración.** El nuevo paso `_rs_import_data_pools` corre tras el apply de configs, recorre `storage_inventory.zfs_pools[]`, excluye el pool raíz (ya montado por el sistema) y lanza `zpool import <nombre>` para cada pool no-raíz cuyos discos estén todos presentes en este host. Cuando ZFS rechaza el import por *foreign* — el caso típico tras una instalación fresh que regraba la etiqueta on-disk con un `hostid` nuevo — el paso reintenta con `-f` y reporta el pool como forzado para dejar trazabilidad. Los pools a los que les falta algún disco se omiten con un aviso claro en lugar de importarse en modo degradado. Todo esto cierra el caso habitual en el que `zfs-import-scan.service` fallaba al boot tras una instalación fresh y dejaba el pool de datos separado indisponible hasta ejecutar `zpool import -f` a mano.
- **El resultado del auto-import persiste en la tarjeta post-restauración.** El paso escribe una sección `data_pools_import` en `/var/lib/proxmenux/restore-state.json` (el mismo JSON que la tarjeta de la pestaña Backups consulta) y un log crudo en `/var/log/proxmenux/restore-datapools-<timestamp>.log`. La tarjeta muestra dentro de Detalles un bloque dedicado con cinco filas coloreadas (Importados / Forzados / Omitidos parcial / Omitidos ausentes / Fallidos), así que el resumen queda consultable después de cerrar el terminal de restauración, y la entrada se preserva en el historial del run para consulta posterior.
- **Pools ZFS creados con `by-partuuid` o `/dev/sdX` en bruto reconocidos por el chequeo de presencia de discos.** El paso de auto-import y `validate_storage.sh` tratan como absolutas las entradas de `devices_by_id` que empiezan por `/` y solo prependen `/dev/disk/by-id/` a los basenames desnudos, de modo que los pools construidos contra partition UUIDs o dispositivos de bloque en bruto se detectan como presentes cuando sus discos están en el host.
- **`/etc/systemd/network` añadido a las rutas de backup por defecto.** Ese directorio contiene los ficheros `.link` de systemd que fijan los nombres de las NICs a su MAC a través de actualizaciones de kernel y reinstalaciones — el `setup_persistent_network` del post_install los escribe para cada interfaz física, y los usuarios pueden dejar los suyos también para renombrar una NIC a algo significativo. Preservarlos a través de una restauración sobre instalación fresh mantiene intacta en el destino la política de nombres de NIC del host origen, de modo que las entradas de `/etc/network/interfaces` que referencian nombres custom siguen resolviendo tras la restauración.
---
## 🙏 Agradecimientos
- **@pepenai** — dashboard en móvil sobre HTTPS + reverse proxy.
- **Pepo** — autenticación de webhook desde FQDN Tailscale.
- **@ash34** (#255) — chequeo VM/CT con la columna `details` a NULL.
- **@f3rs3n** (#256, #257, #258) — ajuste de sysctl en bridges del firewall, ámbito de la optimización de ZFS ARC y reconciliación de nombres persistentes de NIC.
- **Juan C.** — auto-import de pools ZFS de datos tras instalación fresh.
- **David Barbero (@sikete)** — recompilación de drivers DKMS al actualizar el kernel.
## 2026-07-14
### Nueva versión ProxMenux v1.2.3
@@ -106,13 +236,13 @@ Y a cada usuario que abrió una issue, comentó en [GitHub Discussions](https://
### Nueva versión ProxMenux v1.2.2 — *Consolidación estable del ciclo v1.2.1.x*
Release estable que lleva al canal principal las cuatro prereleases del ciclo **v1.2.1.x** en un solo movimiento. El trabajo a lo largo de esas cuatro betas se centró en tres temas: hacer del Health Monitor algo realmente configurable en lugar de solo observable (thresholds por categoría, duraciones de dismiss por evento, un audit log de supresiones activas), expandir el stack de notificaciones para cubrir alrededor de 80 servicios a través de Apprise mientras se persisten eventos durante las Quiet Hours, y convertir el propio proceso del Monitor en un ciudadano del sistema más silencioso y predecible en hosts idle. Por encima de eso, esta release entrega detección automática de updates en contenedores LXC, una reescritura end-to-end del instalador de Coral TPU con los últimos drivers upstream, y una larga lista de fixes visibles para el operador — handshake del terminal HTTPS, detección de kernel updates en PVE 9.x, flujo del instalador NVIDIA en Alpine LXC, gestión del audio acompañante en passthrough de GPU mixta, y varias optimizaciones runtime en los bucles de scan del Monitor. Cinco contribuciones de código directas de la comunidad shipean junto con esta release ([@jcastro](https://github.com/jcastro) ×5, [@pespinel](https://github.com/pespinel) ×1) y el trabajo de GPU passthrough lo impulsaron los reports detallados de campo de [@ghosthvj](https://github.com/ghosthvj) — ver los Acknowledgments al final.
Release estable que lleva al canal principal las cuatro prereleases del ciclo **v1.2.1.x** en un solo movimiento. El trabajo a lo largo de esas cuatro betas se centró en tres temas: hacer del Health Monitor algo realmente configurable en lugar de solo observable (thresholds por categoría, duraciones de dismiss por evento, un audit log de supresiones activas), expandir el stack de notificaciones para cubrir alrededor de 80 servicios a través de Apprise mientras se persisten eventos durante las Quiet Hours, y convertir el propio proceso del Monitor en un ciudadano del sistema más silencioso y predecible en hosts idle. Por encima de eso, esta release entrega detección automática de updates en contenedores LXC, una reescritura end-to-end del instalador de Coral TPU con los últimos drivers upstream, y una larga lista de fixes visibles para el usuario — handshake del terminal HTTPS, detección de kernel updates en PVE 9.x, flujo del instalador NVIDIA en Alpine LXC, gestión del audio acompañante en passthrough de GPU mixta, y varias optimizaciones runtime en los bucles de scan del Monitor. Cinco contribuciones de código directas de la comunidad shipean junto con esta release ([@jcastro](https://github.com/jcastro) ×5, [@pespinel](https://github.com/pespinel) ×1) y el trabajo de GPU passthrough lo impulsaron los reports detallados de campo de [@ghosthvj](https://github.com/ghosthvj) — ver los Acknowledgments al final.
---
## 🩺 Health Monitor — Configurable, granular, auditable
Tres piezas acopladas que juntas permiten al operador ajustar el Health Monitor a la envoltura real de su host en lugar de trabajar alrededor de sus defaults, y gestionar dismisses con el mismo control fino que ya tienen sobre el resto del dashboard.
Tres piezas acopladas que juntas permiten al usuario ajustar el Health Monitor a la envoltura real de su host en lugar de trabajar alrededor de sus defaults, y gestionar dismisses con el mismo control fino que ya tienen sobre el resto del dashboard.
### Thresholds Warning / Critical por categoría
@@ -132,7 +262,7 @@ El botón *Dismiss* en cada alerta del Health Monitor abre ahora un pequeño dro
- **7 days** — útil para una condición temporal de la que no quieres oír durante una migración de una semana
- **Permanently** — silencia este `error_key` concreto indefinidamente
Los dismisses permanentes se persisten con `suppression_hours = -1` en la base de datos de persistencia, nunca re-emiten, nunca re-notifican y se marcan con un badge ámbar **Permanent** distinto en el Health Monitor para que el operador siempre sepa qué alertas están silenciadas intencionadamente. La infraestructura backend para el centinela permanente ya existía — solo le faltaba a la UI una forma de fijarlo. El contrato de API es pequeño y backwards-compatible: `POST /api/health/acknowledge` acepta un campo body opcional `suppression_hours` (entero positivo para horas, `-1` para permanente); omitirlo preserva el comportamiento previo y usa la supresión configurada de la categoría. Un segundo endpoint nuevo `POST /api/health/un-acknowledge {error_key}` limpia un acknowledgment previamente registrado para que la alerta vuelva a ser elegible para dispararse — usado por el panel Active Suppressions abajo.
Los dismisses permanentes se persisten con `suppression_hours = -1` en la base de datos de persistencia, nunca re-emiten, nunca re-notifican y se marcan con un badge ámbar **Permanent** distinto en el Health Monitor para que el usuario siempre sepa qué alertas están silenciadas intencionadamente. La infraestructura backend para el centinela permanente ya existía — solo le faltaba a la UI una forma de fijarlo. El contrato de API es pequeño y backwards-compatible: `POST /api/health/acknowledge` acepta un campo body opcional `suppression_hours` (entero positivo para horas, `-1` para permanente); omitirlo preserva el comportamiento previo y usa la supresión configurada de la categoría. Un segundo endpoint nuevo `POST /api/health/un-acknowledge {error_key}` limpia un acknowledgment previamente registrado para que la alerta vuelva a ser elegible para dispararse — usado por el panel Active Suppressions abajo.
### Panel Active Suppressions en Settings
@@ -160,7 +290,7 @@ Tres fixes de fiabilidad shipean junto, todos surfaceados después del rollout b
2. **Regresión del whitelist backend** que rechazaba Apprise con HTTP 400. El conjunto de canales hardcodeado del validador de notifications-test (`{telegram, gotify, discord, email, all}`) tenía a `apprise` ausente, por lo que cada test o send de Apprise devolvía `400 Invalid channel` antes de que la librería fuera siquiera invocada. El whitelist se deriva ahora en vivo desde `notification_channels.CHANNEL_TYPES`, de forma que añadir una nueva implementación de canal en el futuro no puede regresionar silenciosamente este validador otra vez.
3. **Error reporting opaco** cuando el destino devolvía una respuesta no-2xx. Cuando un destino (`jsons://`, `ntfy://`, `slack://`, …) rechazaba el payload, el operador solo veía un mensaje genérico *"Apprise rejected the notification (transport failure)"*. El canal captura ahora el logger interno de Apprise durante `notify()` y surfacea el HTTP status code real más el response body del destino (capado a 300 caracteres) — de forma que un beta tester debuggeando un webhook custom puede ver inmediatamente si el servidor upstream está rechazando su schema de payload.
3. **Error reporting opaco** cuando el destino devolvía una respuesta no-2xx. Cuando un destino (`jsons://`, `ntfy://`, `slack://`, …) rechazaba el payload, el usuario solo veía un mensaje genérico *"Apprise rejected the notification (transport failure)"*. El canal captura ahora el logger interno de Apprise durante `notify()` y surfacea el HTTP status code real más el response body del destino (capado a 300 caracteres) — de forma que un beta tester debuggeando un webhook custom puede ver inmediatamente si el servidor upstream está rechazando su schema de payload.
---
@@ -197,7 +327,7 @@ El mount monitor solía llamar `lxc-info -n <vmid> -p` por cada CT corriendo sol
## 🔌 Handshake del terminal HTTPS
Cada modal de terminal en el Monitor (terminal del dashboard, terminal LXC, terminal de scripts) solía fallar con *WebSocket connection error* en hosts donde HTTPS estaba habilitado. La root cause era específica al path `gevent + SSL`: el `WebSocketHandler` de gevent-websocket estaba apilado sobre la implementación de protocolo de flask-sock, por lo que el servidor emitía **dos** cabeceras `HTTP/1.1 101 Switching Protocols` consecutivas y el navegador cerraba la conexión como un frame corrupto. Quitar el argumento explícito `handler_class=WebSocketHandler` restaura una única respuesta 101 y el handshake completa con normalidad. El fix es invisible para operadores corriendo en HTTP plano — no estaban afectados — pero desbloquea cada install fronteada por HTTPS (reverse proxies, deployments con certificate-managed, cualquier cosa detrás de nginx/Traefik).
Cada modal de terminal en el Monitor (terminal del dashboard, terminal LXC, terminal de scripts) solía fallar con *WebSocket connection error* en hosts donde HTTPS estaba habilitado. La root cause era específica al path `gevent + SSL`: el `WebSocketHandler` de gevent-websocket estaba apilado sobre la implementación de protocolo de flask-sock, por lo que el servidor emitía **dos** cabeceras `HTTP/1.1 101 Switching Protocols` consecutivas y el navegador cerraba la conexión como un frame corrupto. Quitar el argumento explícito `handler_class=WebSocketHandler` restaura una única respuesta 101 y el handshake completa con normalidad. El fix es invisible para usuarios corriendo en HTTP plano — no estaban afectados — pero desbloquea cada install fronteada por HTTPS (reverse proxies, deployments con certificate-managed, cualquier cosa detrás de nginx/Traefik).
Adicionalmente, el panel de terminal solía perder su conexión WebSocket cuando el usuario activaba la feature de auto-traducción del navegador (los prompts "translate this page" de Chrome / Edge / Safari). El traductor mueve nodos del DOM que React aún mantiene como refs, y el componente WebSocket React se rompe porque su ref de contenedor apunta a un nodo movido. Añadido `translate="no"` en los divs contenedores del terminal para que el traductor salte el tty embebido por completo — las traducciones en el resto de la página siguen funcionando.
@@ -211,7 +341,7 @@ En hosts Proxmox VE 9.x, la fila *System Updates → Kernel / PVE* reportaba "Ke
2. **El dry-run cambió de `apt-get upgrade --dry-run` a `apt-get dist-upgrade --dry-run`**. PVE 9 shipea kernel updates empaquetados como instalaciones nuevas (no como upgrades directas de un paquete existente), y el `upgrade --dry-run` plano no considera nuevas instalaciones en absoluto. `dist-upgrade --dry-run` sí.
3. **La detección del kernel corriendo** lee ahora `uname -r` y flaguea un update como *running-kernel update* cuando el paquete matchea la release corriendo exactamente o su meta-package de branch (p. ej. `proxmox-kernel-6.14` para un host en `6.14.11-4-pve`). El texto de la fila distingue *"Running kernel update available (reboot required)"* de *"N kernel update(s) available (none for running kernel)"* para que el operador sepa si necesita reboot o solo instalar.
3. **La detección del kernel corriendo** lee ahora `uname -r` y flaguea un update como *running-kernel update* cuando el paquete matchea la release corriendo exactamente o su meta-package de branch (p. ej. `proxmox-kernel-6.14` para un host en `6.14.11-4-pve`). El texto de la fila distingue *"Running kernel update available (reboot required)"* de *"N kernel update(s) available (none for running kernel)"* para que el usuario sepa si necesita reboot o solo instalar.
---
@@ -241,9 +371,9 @@ Nuevas páginas de documentación cubren la sección **Active Suppressions** en
- **Detección de updates de funciones post-install** — el Monitor trackea optimizaciones ProxMenux instaladas (Log2Ram, Memory Settings, System Limits, Logrotate, …) y notifica cuando hay una versión más nueva disponible, con apply one-click desde Settings.
- **Flujo de update de Secure Gateway (Tailscale)** — update one-click de Tailscale desde Settings con indicadores Last-checked / Installed / Latest y notificación cuando se publica una nueva versión.
- **Menú Helper-Scripts** — context más rico e información útil para cada entrada, haciendo más fácil saber qué hace cada script antes de ejecutarlo.
- **Wording de agregación burst** — los resúmenes burst reportan ahora solo los eventos *adicionales* que llegaron después de la alerta individual inicial, de forma que el operador ya no ve el primer evento contado dos veces.
- **Wording de agregación burst** — los resúmenes burst reportan ahora solo los eventos *adicionales* que llegaron después de la alerta individual inicial, de forma que el usuario ya no ve el primer evento contado dos veces.
- **Clasificador de errores conocidos** — regex con word-boundary en patrones ATA / UNC para que mensajes de kernel como `nvidia_uvm:FatalError` ya no se clasifiquen mal como problemas de cable ATA.
- **Errores de control de VM / CT** — start / stop / restart fallido surfacea ahora el stderr real de `pvesh` (p. ej. *"no space left on device"*) en el toast de la UI y dispara una notificación `vm_fail` / `ct_fail`, en lugar del bare 500 INTERNAL SERVER ERROR que el operador solía ver.
- **Errores de control de VM / CT** — start / stop / restart fallido surfacea ahora el stderr real de `pvesh` (p. ej. *"no space left on device"*) en el toast de la UI y dispara una notificación `vm_fail` / `ct_fail`, en lugar del bare 500 INTERNAL SERVER ERROR que el usuario solía ver.
- **Path de apply de log2ram** — el flujo auto / update reinicia ahora log2ram después de escribir el nuevo size, de forma que un `512M` configurado realmente surte efecto en el tmpfs corriendo sin restart manual.
- **PVE webhook URL** — el webhook de notificación sigue ahora automáticamente el estado SSL activo, cambiando entre `http://` y `https://` cuando toggleas HTTPS en el panel.
- **Cascada de 401 frontend** — la login screen ya no se traga un 401 para siempre tras un estado breve de token rancio; la flag de dedup se limpia al mount y al login exitoso.