mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2026-10-01 19:17:10 +00:00
ProxMenux 1.2.6.2-beta: OCI containers in the Monitor, docs and fixes
OCI manager Apps - App tab: containers installed from an OCI image are identified from their installation record; the application and image versions are shown and an update is detected by image digest; repository link; Refresh data. - Updates tab for OCI containers: Update and Recreate run the same flow as the OCI menu in the Monitor terminal; the pre-update backup can be kept in a backup storage; scheduled image updates with an optional minimum age. - Logs tab: console output of the application, kept on the host (lxc.console.logfile + logrotate) and followed live. - The Proxmox console opens a shell (cmode: shell) when the image has one. - A damaged image download is fetched again before failing. - Multi-container applications open at their LAN address; volume mount points on block storage report their usage. Monitor - Proxmox notifications are delivered to a loopback-only HTTP listener when HTTPS is enabled, so they no longer fail certificate verification. - Log persistence counts recurring patterns only; an ended burst is not reported as persistent and its warning clears on its own (#386). - Proxmox notification config backups are deduplicated and capped at three. - The update icon on the Apps page opens the container on its Updates tab. - Version 1.2.6.2-beta and its release notes in every Monitor language. Docs - OCI manager Apps and Audit & Report rebuilt as per-page message files, with a new page for OCI containers in the Monitor. - Seven pages fixed where rich-text tags were missing from t.rich. Translations - Spanish fixes across the OCI engine, the Monitor and the TUI menus. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
386d33df6e
commit
4437a671d2
@@ -157,12 +157,12 @@
|
||||
{
|
||||
"endpoint": "/api/vms/<vmid>/control",
|
||||
"method": "POST",
|
||||
"use": "Start / stop / shutdown / reboot a VM or LXC container (the same operations the Monitor's VM & LXC modal exposes). Body: {\"action\": \"start|stop|shutdown|reboot\"}. Synchronous — returns the outcome directly with no polling needed."
|
||||
"use": "Start / stop / shutdown / reboot a VM or LXC container (the same operations the Monitor's VM & LXC modal exposes). The body carries <code>action</code>: <code>start</code>, <code>stop</code>, <code>shutdown</code> or <code>reboot</code>. Synchronous — returns the outcome directly with no polling needed."
|
||||
},
|
||||
{
|
||||
"endpoint": "/api/vms/<vmid>/backup",
|
||||
"method": "POST",
|
||||
"use": "Create a vzdump backup of a VM or LXC. Body (all optional except when the defaults don't match your storage layout): {\"storage\": \"<pve-storage>\", \"mode\": \"snapshot|suspend|stop\", \"compress\": \"zstd|lzo|gz|none\", \"protected\": true, \"notes\": \"…\", \"notification\": \"auto|always|failure|never\", \"pbs_change_detection\": \"default|legacy|data\"}. Returns the PVE task UPID."
|
||||
"use": "Create a vzdump backup of a VM or LXC. Body fields, all optional unless the defaults do not match the storage layout: <code>storage</code> (a PVE storage), <code>mode</code> (<code>snapshot</code>, <code>suspend</code> or <code>stop</code>), <code>compress</code> (<code>zstd</code>, <code>lzo</code>, <code>gz</code> or <code>none</code>), <code>protected</code> (<code>true</code> or <code>false</code>), <code>notes</code>, <code>notification</code> (<code>auto</code>, <code>always</code>, <code>failure</code> or <code>never</code>) and <code>pbs_change_detection</code> (<code>default</code>, <code>legacy</code> or <code>data</code>). Returns the PVE task UPID."
|
||||
},
|
||||
{
|
||||
"endpoint": "/api/vms/<vmid>/backups",
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
{
|
||||
"meta": {
|
||||
"title": "Assessment and inventory | Audit & Report",
|
||||
"description": "The Audit & Report assessment: report profiles, how results are classified, accepted risks, unreadable sources, Lynis and the inventory of the node."
|
||||
},
|
||||
"header": {
|
||||
"title": "Assessment and inventory",
|
||||
"description": "The assessment inspects the node, keeps its findings and answers different questions depending on the report profile.",
|
||||
"section": "Audit & Report"
|
||||
},
|
||||
"sections": [
|
||||
{
|
||||
"id": "read-only",
|
||||
"blocks": [
|
||||
{
|
||||
"calloutInfo": {
|
||||
"title": "An assessment inspects; it does not change the configuration",
|
||||
"body": "It reads the configuration and state of the host. It writes its results, reports and logs, and the boot checks can mount EFI system partitions for a moment. The configuration it assesses is not modified."
|
||||
}
|
||||
},
|
||||
{
|
||||
"p": "<strong>Run assessment</strong> starts a run with the profile selected in <strong>Report</strong>. The view shows the date of the last run and how long ago it was."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "profiles",
|
||||
"title": "Report profiles",
|
||||
"intro": "Each profile answers a different question. The checks and sections are selected before the document is composed.",
|
||||
"blocks": [
|
||||
{
|
||||
"table": {
|
||||
"headers": ["Profile", "What it covers"],
|
||||
"rows": [
|
||||
["Full audit", "Every check and all the structure available."],
|
||||
["Quick diagnosis", "Every check; the result opens with the critical findings, the warnings and the readings that could not be verified."],
|
||||
["Inventory", "A description of the node, with no checks and no classification."],
|
||||
["Security review", "Exposure, access, privileges, certificates, updates, repositories and Lynis."],
|
||||
["Backup assurance", "Coverage, age, results, verification, retention and recovery of the backups."],
|
||||
["Capacity and wear", "Growth margin, memory, space usage and the service life of the disks."]
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"p": "The contents of each document are described in <reportsLink>Reports and comparisons</reportsLink>."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "results",
|
||||
"title": "How results are classified",
|
||||
"blocks": [
|
||||
{
|
||||
"table": {
|
||||
"headers": ["Classification", "Meaning"],
|
||||
"rows": [
|
||||
["Critical", "A failed condition that has priority."],
|
||||
["Warning", "A condition that needs a review, given the evidence or the policy."],
|
||||
["Observation", "Information about the node that is not reported as a failure."],
|
||||
["Unverified", "The source the check needs could not be read. It does not mean the problem is absent."],
|
||||
["Conformant", "The condition meets the criterion applied."],
|
||||
["Not applicable", "Nothing within the scope of the check applies."],
|
||||
["Accepted risk", "The finding exists and a decision about it has been recorded."],
|
||||
["Excluded by policy", "The policy declares that the element is left out of the count."]
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"p": "Findings can be filtered by area: System, Storage, Network, Security, Backup, Guests and Hardware. Each finding keeps its evidence, with the source it was read from."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "unverified",
|
||||
"title": "When a source cannot be read",
|
||||
"blocks": [
|
||||
{
|
||||
"table": {
|
||||
"headers": ["Case", "Behaviour"],
|
||||
"rows": [
|
||||
["Unverified", "The check keeps its identity, states in its evidence which source failed and does not turn missing data into a conformant result."],
|
||||
["Incomplete evidence", "The report names the source and the time of collection, so a real problem can be told apart from an insufficient reading."],
|
||||
["A new run", "Once the access, package or service is corrected, the same profile runs again and the comparison shows whether the result could be verified."]
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "lynis",
|
||||
"title": "Lynis",
|
||||
"blocks": [
|
||||
{
|
||||
"p": "The security review uses the Lynis report of the host. When Lynis has not been run yet, or its report is older than the threshold of the policy, a dialog offers <strong>Run with Lynis</strong>, which takes a few minutes longer, or <strong>Run without Lynis</strong>, which uses the existing report."
|
||||
},
|
||||
{
|
||||
"figure": {
|
||||
"src": "/monitor/audit/lynis-dialog.png",
|
||||
"alt": "Dialog that offers to run the assessment with or without Lynis",
|
||||
"caption": "The Lynis dialog before a security review."
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "accept",
|
||||
"title": "Accepting a risk",
|
||||
"blocks": [
|
||||
{
|
||||
"p": "<strong>Accept risk</strong> records a decision on a finding. The reason is required and is stored with the author and the date."
|
||||
},
|
||||
{
|
||||
"table": {
|
||||
"headers": ["Field", "Options"],
|
||||
"rows": [
|
||||
["Reason", "Free text, required."],
|
||||
["Stops applying after", "90 days, 180 days, 1 year or does not expire. When the period ends the finding becomes active again."],
|
||||
["Remind me to review", "A reminder that brings the decision back to attention while it stays in force."]
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"p": "An accepted finding stays visible with its decision, and <strong>Return to active</strong> revokes it. An accepted risk is not a correction: the comparison reports it as accepted, not as resolved."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "inventory",
|
||||
"title": "Inventory",
|
||||
"blocks": [
|
||||
{
|
||||
"list": {
|
||||
"items": [
|
||||
"Identity, Proxmox VE version, kernel, subscription and cluster.",
|
||||
"CPU, memory, board, BIOS, controllers and IOMMU.",
|
||||
"Disks, SMART, power-on hours and recorded events.",
|
||||
"Adapters, bonds, bridges, latency and connections.",
|
||||
"Storage, guests with their disks and interfaces, and backups.",
|
||||
"PCI passthrough and the software ProxMenux manages."
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
{
|
||||
"meta": {
|
||||
"title": "Changes | Audit & Report",
|
||||
"description": "The change journal of Audit & Report: what ProxMenux changed on the host, what was there before, the difference and whether it can be undone."
|
||||
},
|
||||
"header": {
|
||||
"title": "Changes",
|
||||
"description": "The journal of the operations ProxMenux performs on the host and, where it was captured, the state before and after each one.",
|
||||
"section": "Audit & Report"
|
||||
},
|
||||
"sections": [
|
||||
{
|
||||
"id": "purpose",
|
||||
"blocks": [
|
||||
{
|
||||
"calloutInfo": {
|
||||
"title": "From the script that ran to the change it made",
|
||||
"body": "A long function may change only two lines. The journal keeps each concrete operation with the script, function and version responsible, the resource affected, the difference and whether it can be undone."
|
||||
}
|
||||
},
|
||||
{
|
||||
"flow": {
|
||||
"nodes": [
|
||||
{ "label": "Script", "detail": "function + version" },
|
||||
{ "label": "Capture", "detail": "content before" },
|
||||
{ "label": "Operation", "detail": "file · package · service" },
|
||||
{ "label": "Journal", "detail": "attribution + difference" }
|
||||
],
|
||||
"caption": "The capture is taken when the operation runs and is consolidated when the Monitor reads the journal."
|
||||
}
|
||||
},
|
||||
{
|
||||
"figure": {
|
||||
"src": "/monitor/audit/changes-view.png",
|
||||
"alt": "Changes view of Audit & Report with the entries grouped by post-install option and script",
|
||||
"caption": "The Changes view, with the difference of a file edited by ProxMenux."
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "types",
|
||||
"title": "Kinds of entry",
|
||||
"intro": "The filter at the top separates the entries by kind.",
|
||||
"blocks": [
|
||||
{
|
||||
"table": {
|
||||
"headers": ["Kind", "What it records"],
|
||||
"rows": [
|
||||
["Configuration", "ProxMenux wrote, edited or removed a file, changed a setting or altered a service."],
|
||||
["Installations", "ProxMenux added a package or component, and the packages that actually appeared are recorded."],
|
||||
["Executions", "ProxMenux ran a command; what it changed is up to the command itself."],
|
||||
["Applied", "A function was applied before the journal existed; the state before it was not captured."]
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "groups",
|
||||
"title": "How the view is organised",
|
||||
"blocks": [
|
||||
{
|
||||
"table": {
|
||||
"headers": ["Section", "Contents"],
|
||||
"rows": [
|
||||
["Post-install optimizations", "Grouped by the post-install option the user selected."],
|
||||
["ProxMenux scripts", "GPU, Coral, network, storage, security, utilities and the other instrumented scripts, including the ProxMenux installer and ProxMenux Monitor."],
|
||||
["Installed packages and utilities", "Software ProxMenux installed on the host."]
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"p": "Within each section, <strong>By function</strong> groups the entries under the function that made them."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "entry",
|
||||
"title": "What each entry shows",
|
||||
"blocks": [
|
||||
{
|
||||
"list": {
|
||||
"items": [
|
||||
"The script, function and version responsible.",
|
||||
"The date and the affected resource.",
|
||||
"The known state before and after the change.",
|
||||
"The lines added and removed.",
|
||||
"The packages that were actually added.",
|
||||
"The state transition of a service.",
|
||||
"<strong>Undoing this</strong>: <em>Restores exactly what was there</em>, <em>Deletes the file (there was none before)</em>, <em>The package can be uninstalled</em>, a partial undo, or <em>Cannot be undone from the journal</em>."
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "before",
|
||||
"title": "What the state before means",
|
||||
"blocks": [
|
||||
{
|
||||
"p": "The state before can be captured content, a file created for the first time or an unknown state. Changes made before the journal existed cannot be reconstructed; running the function again captures first the state found at that moment."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "limits",
|
||||
"title": "Scope of the journal",
|
||||
"blocks": [
|
||||
{
|
||||
"list": {
|
||||
"items": [
|
||||
"Manual changes and changes made by other software are not recorded.",
|
||||
"Only operations that go through the ProxMenux audit primitives are covered.",
|
||||
"Recording never blocks the operation it describes: if the entry cannot be written, the operation goes on.",
|
||||
"Successive changes to one resource are shown as the known origin against the current state."
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "retention",
|
||||
"title": "Stored evidence",
|
||||
"blocks": [
|
||||
{
|
||||
"table": {
|
||||
"headers": ["Element", "Behaviour"],
|
||||
"rows": [
|
||||
["Entries", "They are kept on the host; nothing is removed from the journal automatically."],
|
||||
["Captured content", "A captured object is kept while an entry refers to it."],
|
||||
["Size of a capture", "Content above 1 MiB is not stored whole; the entry records that the capture was skipped because of its size."],
|
||||
["Reading", "The Monitor does not load stored objects above 2 MiB."],
|
||||
["Difference", "At most 400 lines are shown, and a longer difference is marked as truncated."],
|
||||
["Listing", "The API returns 200 entries per request by default and up to 1000."]
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"calloutWarning": {
|
||||
"title": "Kept content is not an automatic undo",
|
||||
"body": "The content before a change can be inspected and restored by hand, but the Changes view does not revert operations. An execution entry records the command without knowing every effect of the tool it ran."
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
{
|
||||
"meta": {
|
||||
"title": "Audit & Report | ProxMenux Monitor",
|
||||
"description": "Assess a Proxmox VE node, record what ProxMenux changed on it and declare what is expected from its guests and storage, with printable reports."
|
||||
},
|
||||
"header": {
|
||||
"title": "Audit & Report",
|
||||
"description": "An assessment of the node, the journal of what ProxMenux changed on it and the declaration of what is expected from it, with documents that can be printed or saved as PDF.",
|
||||
"section": "ProxMenux Monitor"
|
||||
},
|
||||
"sections": [
|
||||
{
|
||||
"id": "views",
|
||||
"blocks": [
|
||||
{
|
||||
"calloutInfo": {
|
||||
"title": "Three views, three questions",
|
||||
"body": "Audit & Report keeps apart the facts of the node, the interventions of ProxMenux and the expectations declared for it. A configuration the assessment cannot place against a declared purpose is described, not reported as a failure."
|
||||
}
|
||||
},
|
||||
{
|
||||
"cards": {
|
||||
"items": [
|
||||
{ "icon": "shield", "title": "Assessment — how is the node?", "body": "Runs the checks of the chosen profile, composes the inventory and classifies what needs attention, with the evidence of each result." },
|
||||
{ "icon": "refresh", "title": "Changes — what did ProxMenux do?", "body": "Lists the files, packages, services and commands the instrumented ProxMenux scripts changed, with what was there before when it was captured." },
|
||||
{ "icon": "fileText", "title": "Policy — what is expected?", "body": "Declares which guests need a backup or must start with the host, which storage is essential and the thresholds of the checks." }
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"flow": {
|
||||
"nodes": [
|
||||
{ "label": "Assessment", "detail": "facts" },
|
||||
{ "label": "Policy", "detail": "context" },
|
||||
{ "label": "Changes", "detail": "interventions" }
|
||||
],
|
||||
"caption": "The assessment provides the facts, the policy gives them context and the journal records what ProxMenux did."
|
||||
}
|
||||
},
|
||||
{
|
||||
"figure": {
|
||||
"src": "/monitor/audit/assessment-view.png",
|
||||
"alt": "Audit & Report in ProxMenux Monitor with the Assessment, Changes and Policy views",
|
||||
"caption": "Audit & Report, with the Assessment view open."
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "boundaries",
|
||||
"title": "Three different functions",
|
||||
"blocks": [
|
||||
{
|
||||
"table": {
|
||||
"headers": ["Function", "What it does"],
|
||||
"rows": [
|
||||
["<healthLink>Health Monitor</healthLink>", "Observes metrics and events continuously and can raise notifications."],
|
||||
["Audit & Report", "Runs an assessment when it is asked for, documents the node and compares runs with each other."],
|
||||
["Change journal", "Records the operations ProxMenux performs through its audit primitives."]
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "pages",
|
||||
"title": "Pages of this section",
|
||||
"blocks": [
|
||||
{
|
||||
"next": {
|
||||
"items": [
|
||||
{ "label": "Assessment and inventory", "href": "/docs/monitor/audit-report/assessment", "tail": "profiles, results, accepted risks and inventory." },
|
||||
{ "label": "Changes", "href": "/docs/monitor/audit-report/changes", "tail": "the journal of what ProxMenux changed on the host." },
|
||||
{ "label": "Policy", "href": "/docs/monitor/audit-report/policy", "tail": "guests, storage and thresholds." },
|
||||
{ "label": "Reports and comparisons", "href": "/docs/monitor/audit-report/reports", "tail": "the six documents and the reference run." },
|
||||
{ "label": "Scope and guarantees", "href": "/docs/monitor/audit-report/scope", "tail": "sources, limits and stored data." }
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
{
|
||||
"meta": {
|
||||
"title": "Policy | Audit & Report",
|
||||
"description": "The node policy of Audit & Report: backup, autostart and recovery objective of each guest, the role of each storage and the thresholds of the checks."
|
||||
},
|
||||
"header": {
|
||||
"title": "Policy",
|
||||
"description": "The policy declares what no inspection can deduce: what each guest and storage is for and the thresholds the checks apply.",
|
||||
"section": "Audit & Report"
|
||||
},
|
||||
"sections": [
|
||||
{
|
||||
"id": "principle",
|
||||
"blocks": [
|
||||
{
|
||||
"calloutInfo": {
|
||||
"title": "With no declaration, the report describes; with one, it assesses",
|
||||
"body": "An assessment sees what the host does, not what it is for. A guest without a backup whose purpose is not declared is reported as an observation. If its backup is declared required, the same absence is reported as a warning. Nothing has to be declared."
|
||||
}
|
||||
},
|
||||
{
|
||||
"figure": {
|
||||
"src": "/monitor/audit/policy-view.png",
|
||||
"alt": "Policy view with the guests, the storage and the thresholds of the node",
|
||||
"caption": "The Policy view."
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "guests",
|
||||
"title": "Guests",
|
||||
"intro": "Each VM and LXC of the node has three fields. A value left as default takes the general value, shown next to it.",
|
||||
"blocks": [
|
||||
{
|
||||
"table": {
|
||||
"headers": [
|
||||
"Field",
|
||||
"Values",
|
||||
"Effect on the assessment"
|
||||
],
|
||||
"rows": [
|
||||
[
|
||||
"Backup",
|
||||
"Required, Not required, Not stated",
|
||||
"A missing backup is a warning when it is required, an observation when it is not stated, and it is left out of the count when it is not required."
|
||||
],
|
||||
[
|
||||
"Autostart",
|
||||
"Required, Not required, Not stated",
|
||||
"Whether the guest has to start with the host."
|
||||
],
|
||||
[
|
||||
"Recovery objective",
|
||||
"Hours",
|
||||
"The maximum acceptable age of the last backup."
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "storage",
|
||||
"title": "Storage",
|
||||
"blocks": [
|
||||
{
|
||||
"p": "An unreachable storage is reported as critical when it is declared essential or serves a running guest, as a warning when its role is not stated, and as an observation when it is declared optional."
|
||||
}
|
||||
],
|
||||
"intro": "Each storage of the node is declared Essential, Optional or Not stated."
|
||||
},
|
||||
{
|
||||
"id": "thresholds",
|
||||
"title": "Thresholds",
|
||||
"intro": "An empty threshold uses the shipped value, shown as its placeholder.",
|
||||
"blocks": [
|
||||
{
|
||||
"list": {
|
||||
"items": [
|
||||
"Storage capacity review (%) and thin pool fill review (%).",
|
||||
"Thin overprovisioning ratio and memory overcommit ratio.",
|
||||
"ZFS scrub interval (days).",
|
||||
"Backup age fallback (days) and schedule grace (ratio).",
|
||||
"Certificate expiry notice (days).",
|
||||
"Disk service life (hours) and recent disk error window (days).",
|
||||
"Lynis report age (days) and package index age (days).",
|
||||
"Journal against its cap (%).",
|
||||
"Filesystem space review (%) and filesystem inode review (%)."
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "save",
|
||||
"title": "How the policy is saved",
|
||||
"blocks": [
|
||||
{
|
||||
"p": "The declaration is validated and saved atomically in <code>/usr/local/share/proxmenux/audit_policy.json</code>. Each save carries a revision: if the declaration changed in another session, the draft is not saved and the view offers to reload the saved declaration."
|
||||
},
|
||||
{
|
||||
"calloutWarning": {
|
||||
"title": "The policy is never inferred",
|
||||
"body": "ProxMenux adds no requirement on its own. An empty field keeps the shipped value or stays not stated, and a partial declaration only affects the elements it names."
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
{
|
||||
"meta": {
|
||||
"title": "Reports and comparisons | Audit & Report",
|
||||
"description": "The six Audit & Report documents, how they are printed or saved as PDF, and how runs are compared with a reference run."
|
||||
},
|
||||
"header": {
|
||||
"title": "Reports and comparisons",
|
||||
"description": "Six documents, each composed for a different question, and the comparison of every run with a reference run.",
|
||||
"section": "Audit & Report"
|
||||
},
|
||||
"sections": [
|
||||
{
|
||||
"id": "intro",
|
||||
"blocks": [
|
||||
{
|
||||
"calloutInfo": {
|
||||
"title": "Six documents, not six styles",
|
||||
"body": "The engine selects checks and sections before composing the document. A quick diagnosis is not a full audit with fewer pages, and an inventory presents no assessment results. The sample documents below use fictitious data."
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "documents",
|
||||
"title": "The six documents",
|
||||
"blocks": [
|
||||
{
|
||||
"downloads": {
|
||||
"items": [
|
||||
{
|
||||
"title": "Full audit",
|
||||
"body": "Documents the node end to end as a technical record.",
|
||||
"href": "/monitor/audit/sample-audit-full-report.pdf",
|
||||
"facts": [
|
||||
{ "label": "Checks", "value": "Every available check." },
|
||||
{ "label": "Contents", "value": "Executive summary; identity and cluster; hardware; network and latency; storage; guests; passthrough; applications; findings with evidence; sources and scope." }
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Quick diagnosis",
|
||||
"body": "Shows what needs attention without the complete inventory.",
|
||||
"href": "/monitor/audit/sample-audit-diagnostic-report.pdf",
|
||||
"facts": [
|
||||
{ "label": "Checks", "value": "The same checks as the full audit." },
|
||||
{ "label": "Contents", "value": "Minimal identity; critical findings; warnings; relevant observations; unverified readings and priority actions. Diagrams, inventory and long annexes are left out." }
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Inventory",
|
||||
"body": "Describes what exists on the node without assessing it.",
|
||||
"href": "/monitor/audit/sample-audit-inventory-report.pdf",
|
||||
"facts": [
|
||||
{ "label": "Checks", "value": "None; no finding is classified." },
|
||||
{ "label": "Contents", "value": "Identity; cluster; CPU and memory; board, BIOS and controllers; network; storage; VMs and LXCs; passthrough; applications and elements managed by ProxMenux." }
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Security review",
|
||||
"body": "Covers exposure and the controls over access to the node.",
|
||||
"href": "/monitor/audit/sample-audit-security-report.pdf",
|
||||
"facts": [
|
||||
{ "label": "Checks", "value": "The security area, plus container privileges, updates, repositories and the APT chain." },
|
||||
{ "label": "Contents", "value": "Identity and cluster; network and latency; access; firewall; 2FA; certificates; privileges; updates; repositories; state and age of Lynis." }
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Backup assurance",
|
||||
"body": "Checks that the declared protection exists and that its backups are usable.",
|
||||
"href": "/monitor/audit/sample-audit-backup-report.pdf",
|
||||
"facts": [
|
||||
{ "label": "Checks", "value": "The backup area, plus storage connectivity and notification delivery." },
|
||||
{ "label": "Contents", "value": "Coverage per guest; declared recovery objective; age and result; destination; retention; verification; failed jobs; recovery and incomplete sources." }
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Capacity and wear",
|
||||
"body": "Measures growth margin and signs of exhaustion or ageing.",
|
||||
"href": "/monitor/audit/sample-audit-capacity-report.pdf",
|
||||
"facts": [
|
||||
{ "label": "Checks", "value": "The storage and hardware areas, plus memory, swap, journal and the host filesystem." },
|
||||
{ "label": "Contents", "value": "Usage and thresholds; thin pools; overprovisioning; memory; inodes; ZFS; temperatures; power-on hours; SMART errors and NVMe/SSD service life." }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"figure": {
|
||||
"src": "/monitor/audit/audit-report-preview.png",
|
||||
"alt": "First page of a full audit report",
|
||||
"caption": "Every document shares the report identifier, numbered sections, date, node, profile and footer."
|
||||
}
|
||||
},
|
||||
{
|
||||
"p": "A document contains the identity of the node and the date of the assessment, the executive result, a summary by area, the structure of hardware, network and storage, the protection of the guests, the findings with their evidence, the sources that could not be read, and the scope and policy applied, in the measure each profile includes them."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "print",
|
||||
"title": "Printing or saving as PDF",
|
||||
"blocks": [
|
||||
{
|
||||
"steps": {
|
||||
"items": [
|
||||
{ "title": "Run", "body": "An assessment runs with the profile selected in <strong>Report</strong>." },
|
||||
{ "title": "Review", "body": "The finished run shows its findings, the sources it could not read and the policy it applied." },
|
||||
{ "title": "Generate report", "body": "<strong>Generate report</strong> opens the view prepared as a document." },
|
||||
{ "title": "Print", "body": "<strong>Print or save as PDF</strong> opens the print dialog of the browser, where a printer or <em>Save as PDF</em> is selected." }
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"calloutTip": {
|
||||
"title": "The printout is a document, not a screenshot",
|
||||
"body": "The action bar is hidden, a table that continues on the next page repeats its header, blocks avoid unnecessary breaks and every page keeps the identification and numbering."
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "compare",
|
||||
"title": "Comparing with a reference run",
|
||||
"intro": "<strong>Use as reference</strong> marks a finished run as the reference. Later runs are compared with it and their findings are separated into:",
|
||||
"blocks": [
|
||||
{
|
||||
"table": {
|
||||
"headers": ["Group", "Meaning"],
|
||||
"rows": [
|
||||
["New", "Reported now and not before."],
|
||||
["Worse", "Still reported, and graver or reaching further than before."],
|
||||
["Better", "Still reported, but less grave or reaching less far than before."],
|
||||
["Resolved", "No longer reported, and nobody accepted them."],
|
||||
["Accepted", "No longer counted because a risk was accepted, not because the host changed."],
|
||||
["No longer assessed", "Present before and absent from this run; nothing verified that they stopped."]
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"table": {
|
||||
"headers": ["Reference run", "Behaviour"],
|
||||
"rows": [
|
||||
["Setting it", "Any finished run can be marked; none is marked automatically."],
|
||||
["Changing it", "Marking another run moves the reference without deleting the history."],
|
||||
["Comparing", "When no two runs are selected, the comparison uses the reference and the selected or most recent run."],
|
||||
["Keeping runs", "The 30 most recent runs are kept, and the reference run is never removed."],
|
||||
["Without a reference", "The view states that no reference run has been chosen yet, so there is nothing to compare against."]
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"calloutWarning": {
|
||||
"title": "A report describes one moment",
|
||||
"body": "An assessment does not certify a whole period. Older results stop describing the current state; the view shows the age of the last run and each source keeps the time it was collected."
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
{
|
||||
"meta": {
|
||||
"title": "Scope and guarantees | Audit & Report",
|
||||
"description": "Where the data of Audit & Report comes from, what is outside its view, the guarantees of its design and where its data is stored."
|
||||
},
|
||||
"header": {
|
||||
"title": "Scope and guarantees",
|
||||
"description": "Where the data of an assessment comes from, what stays outside its view and where Audit & Report keeps its data.",
|
||||
"section": "Audit & Report"
|
||||
},
|
||||
"sections": [
|
||||
{
|
||||
"id": "sees",
|
||||
"title": "What it observes",
|
||||
"blocks": [
|
||||
{
|
||||
"list": {
|
||||
"items": [
|
||||
"The configuration and state of the local node.",
|
||||
"The declared configuration of the VMs and LXCs.",
|
||||
"The state of the storage as Proxmox VE knows it.",
|
||||
"The available history of backups and verifications.",
|
||||
"SMART data and the events the Monitor has collected.",
|
||||
"The state of services, cluster, HA and local sources."
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "outside",
|
||||
"title": "What is outside its view",
|
||||
"blocks": [
|
||||
{
|
||||
"list": {
|
||||
"items": [
|
||||
"The interior of the guests, beyond what they declare to Proxmox VE.",
|
||||
"Network equipment outside the host.",
|
||||
"Remote dependencies the node cannot observe.",
|
||||
"Physical state the hardware does not expose.",
|
||||
"Manual actions or actions of other software, in the change journal."
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"calloutWarning": {
|
||||
"title": "Missing evidence is not evidence of absence",
|
||||
"body": "A source that cannot be read gives an unverified or incomplete result, never a conformant one. The report lists the sources that were not available."
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "guarantees",
|
||||
"title": "Guarantees of the design",
|
||||
"blocks": [
|
||||
{
|
||||
"list": {
|
||||
"items": [
|
||||
"Stable identifiers for the checks.",
|
||||
"History of runs and findings.",
|
||||
"A validated policy, saved atomically.",
|
||||
"Accepted risks with a reason, visible.",
|
||||
"Attribution of changes to script and function.",
|
||||
"Capture before and after, where it is available.",
|
||||
"An explicit result when something cannot be measured or captured."
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "storage",
|
||||
"title": "Where the data is stored",
|
||||
"blocks": [
|
||||
{
|
||||
"table": {
|
||||
"headers": ["Data", "Location"],
|
||||
"rows": [
|
||||
["Policy", "<code>/usr/local/share/proxmenux/audit_policy.json</code>"],
|
||||
["Assessments", "The audit database of the Monitor"],
|
||||
["Captured objects", "<code>/usr/local/share/proxmenux/changes/objects/</code>"],
|
||||
["Pending entries", "<code>/usr/local/share/proxmenux/changes/spool/</code>"],
|
||||
["Consolidated journal", "<code>/usr/local/share/proxmenux/changes.db</code>"]
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"calloutInfo": {
|
||||
"title": "A tool to review, not a certification",
|
||||
"body": "Audit & Report reviews, compares and documents the node. Its results are read together with the purpose of the system, the declared policy and the sources available."
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -96,7 +96,8 @@
|
||||
{ "method": "docker label / docker exec", "use": "Reads an OCI version label or runs a version command inside a Docker container." },
|
||||
{ "method": "python distribution", "use": "Uses importlib.metadata through the selected Python interpreter." },
|
||||
{ "method": "command", "use": "Runs an advanced argv-style command without a shell and extracts the version from its output." },
|
||||
{ "method": "manual", "use": "Stores a version entered manually; it must be changed after upgrading the app." }
|
||||
{ "method": "manual", "use": "Stores a version entered manually; it must be changed after upgrading the app." },
|
||||
{ "method": "OCI image", "use": "For containers installed by OCI manager Apps. Reads the application and image versions from the installation record and compares the installed digest with the one the registry publishes; it needs no configuration." }
|
||||
],
|
||||
"sourcesHeading": "Available-version sources",
|
||||
"sources": [
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
},
|
||||
"drillIn": {
|
||||
"heading": "Per-guest drill-in modal",
|
||||
"intro": "The modal opens with the guest name, VMID, type, state and uptime. Its navigation adapts to the guest: <strong>Status</strong>, <strong>App</strong> and <strong>Updates</strong> for LXC application management, <strong>Mounts</strong> when an LXC has mount points, plus <strong>Backups</strong> and <strong>Firewall</strong>. The fixed action bar keeps the lifecycle controls and the LXC terminal available from every tab.",
|
||||
"intro": "The modal opens with the guest name, VMID, type, state and uptime. Its navigation adapts to the guest: <strong>Status</strong>, <strong>App</strong> and <strong>Updates</strong> for LXC application management, <strong>Mounts</strong> when an LXC has mount points, <strong>Logs</strong> for containers installed by OCI manager Apps, plus <strong>Backups</strong> and <strong>Firewall</strong>. The fixed action bar keeps the lifecycle controls and the LXC terminal available from every tab.",
|
||||
"statusTitle": "Tab 1 — Status",
|
||||
"statusImageAlt": "Per-guest drill-in modal — Status tab with CPU / Memory / Disk live cards, Disk and Network I/O totals, the OS distro logo, and the Resources / IP Addresses block",
|
||||
"statusImageCaption": "Status tab — live CPU / Memory / Disk with progress bars at the top, accumulated I/O totals (disk read/write, network down/up) below, then the static Resources block with Notes and + Info expansions and the IP Addresses pill list.",
|
||||
@@ -106,7 +106,9 @@
|
||||
],
|
||||
"mountsCalloutTitle": "What this gives you over the native UI",
|
||||
"mountsCalloutBody": "A truthful, capacity-aware view of every place the container reads or writes. NFS or CIFS shares mounted from inside the CT — invisible to the Proxmox web UI — appear here with the same look and the same health probe as any configured mount point. Stale remote mounts and zombie binds are flagged before they bite during a backup.",
|
||||
"backupsTitle": "Tab 5 — Backups",
|
||||
"logsTitle": "Tab 5 — Logs (OCI containers only)",
|
||||
"logsBody": "Appears only for containers installed by OCI manager Apps. It shows the console output of the main process of the image, kept on the host in <code>/var/log/proxmenux/oci/VMID.console.log</code>: the last 100, 500 or 1000 lines, followed live while the container runs, with a filter and a download. It reads the file on every open, so it also works with the container stopped. The details are in <ociLink>OCI containers in ProxMenux Monitor</ociLink>.",
|
||||
"backupsTitle": "Tab 6 — Backups",
|
||||
"backupsImageAlt": "Per-guest drill-in modal — Backups tab with the available backups list, destination tag, sizes and the Create Backup button",
|
||||
"backupsImageCaption": "Backups tab — every backup stored on configured Proxmox storages for this guest, sorted newest first. The tab header carries the count badge.",
|
||||
"backupsIntro": "Lists every backup stored across configured Proxmox storages for this guest, sorted newest first. The tab title carries a count badge so you see at a glance whether the guest is backed up. Per row:",
|
||||
@@ -116,7 +118,7 @@
|
||||
"<strong>Size</strong> — final on-disk size of the backup."
|
||||
],
|
||||
"backupsOutro": "The <strong>+ Create Backup</strong> button at the top right kicks off a new run on the storage marked as \"Backup target\" in the Proxmox storage config. Restore lives in the Proxmox web UI — the Monitor exposes the \"is this guest backed up recently?\" view, not the recovery flow.",
|
||||
"firewallTitle": "Tab 6 — Firewall",
|
||||
"firewallTitle": "Tab 7 — Firewall",
|
||||
"firewallIntro": "Reads the per-guest Proxmox firewall log straight from the host (no extra service, no polling). The tab is always present in the navigation strip; the panel decides what to render depending on whether the firewall is enabled for that guest and whether any rule is actually logging:",
|
||||
"firewallItems": [
|
||||
"<strong>Firewall disabled</strong> — an amber notice explains exactly where to enable it in the Proxmox UI (<em><Container|VM> → Firewall → Options</em>) and reminds you that at least one rule needs <code>log: info</code> (or higher) before packets show up.",
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
{
|
||||
"meta": {
|
||||
"title": "How an OCI image is translated | ProxMenux",
|
||||
"description": "From the image repository and its Compose file to a reviewable template, a deployment plan and a native Proxmox VE LXC, without Docker inside."
|
||||
},
|
||||
"header": {
|
||||
"title": "How an OCI image is translated",
|
||||
"description": "From the image repository and its Compose file to a reviewable template, a deployment plan and a native LXC, without installing Docker inside.",
|
||||
"section": "OCI manager Apps"
|
||||
},
|
||||
"sections": [
|
||||
{
|
||||
"id": "pipeline",
|
||||
"title": "The translation pipeline",
|
||||
"blocks": [
|
||||
{
|
||||
"mermaid": {
|
||||
"chartCode": "flowchart LR\n A[\"{{repo}}\"] --> B[\"Compose + README\"]\n B --> C[\"{{converter}}\"]\n C --> D[\"{{template}}\"]\n D --> E{\"{{blockers}}\"}\n E -- \"{{no}}\" --> F[\"{{review}}\"]\n F --> D\n E -- \"{{yes}}\" --> G[\"{{plan}}\"]\n G --> H[\"pct create\"]\n H --> I[\"{{lxc}}\"]",
|
||||
"labels": {
|
||||
"repo": "Image repository",
|
||||
"converter": "Converter",
|
||||
"template": "JSON template",
|
||||
"blockers": "No blockers?",
|
||||
"no": "No",
|
||||
"yes": "Yes",
|
||||
"review": "Review / overlay",
|
||||
"plan": "Deployment plan",
|
||||
"lxc": "Native LXC"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"p": "The converter reads the image and the Compose file its project publishes and writes a JSON template. A template with untranslated blockers goes through review, where a curated overlay resolves them, before it is published in the catalog. Only templates without blockers are offered for installation."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "template",
|
||||
"title": "What the template keeps",
|
||||
"blocks": [
|
||||
{
|
||||
"cards": {
|
||||
"items": [
|
||||
{ "icon": "archive", "title": "Image identity", "body": "Repository, rolling tag, architecture, resolved digest and source revision." },
|
||||
{ "icon": "braces", "title": "Container contract", "body": "Entrypoint, Cmd, environment, user, working directory, stop signal, ports and volumes." },
|
||||
{ "icon": "layers", "title": "Proxmox VE translation", "body": "Resources, security, mount points, devices, sysctls, healthchecks and the adaptations each one needs, with their reason." },
|
||||
{ "icon": "shield", "title": "Compatibility", "body": "Supported keys, untranslated blockers and the state of each validation." }
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "sources",
|
||||
"title": "OCI provides the process; Compose provides the environment",
|
||||
"blocks": [
|
||||
{
|
||||
"table": {
|
||||
"headers": ["Source", "Example", "Native result"],
|
||||
"rows": [
|
||||
["OCI metadata", "<code>Entrypoint</code>, <code>Cmd</code>, <code>User</code>", "Proxmox VE imports them when the CT is created"],
|
||||
["Docker Compose", "<code>environment</code>, <code>volumes</code>, <code>devices</code>", "LXC environment entries, <code>mpN</code> and <code>devN</code>"],
|
||||
["ProxMenux profile", "GPU, healthcheck, credentials", "questions and reviewed adaptations"],
|
||||
["User", "VMID, storage, network", "the instance contract"]
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "install",
|
||||
"title": "What happens during an installation",
|
||||
"blocks": [
|
||||
{
|
||||
"steps": {
|
||||
"items": [
|
||||
{ "title": "Resolve", "body": "The registry is queried, the host architecture is selected and the effective digest of the rolling tag is fixed." },
|
||||
{ "title": "Download and verify", "body": "Skopeo downloads the image as an OCI archive, and every layer is checked against its digest and decompressed before anything is created. A damaged download is fetched a second time before the installation stops." },
|
||||
{ "title": "Build", "body": "<code>pct create</code> builds the rootfs from the archive and keeps the official process metadata of the image." },
|
||||
{ "title": "Connect", "body": "The declared volumes, network, environment, devices and security profiles are attached." },
|
||||
{ "title": "Console", "body": "The console output of the container is kept on the host, and the Proxmox VE console opens a shell when the image ships one." },
|
||||
{ "title": "Check", "body": "The first start waits for an address and for the service to answer; a failure is not reported as a successful installation." },
|
||||
{ "title": "Register", "body": "The effective configuration is written to the instance contract that updates and recreations use." }
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "example",
|
||||
"title": "Example: an image with /config and /downloads",
|
||||
"intro": "A common Compose definition and the Proxmox VE configuration it becomes. The paths the application expects do not change.",
|
||||
"blocks": [
|
||||
{
|
||||
"codeGrid": {
|
||||
"items": [
|
||||
{
|
||||
"title": "Docker Compose",
|
||||
"code": "image: lscr.io/linuxserver/example:latest\nenvironment:\n - PUID=1000\n - PGID=1000\nvolumes:\n - config:/config\n - /srv/downloads:/downloads\nports:\n - 8080:8080"
|
||||
},
|
||||
{
|
||||
"title": "/etc/pve/lxc/VMID.conf (excerpt)",
|
||||
"code": "entrypoint: /init\nmp0: local-lvm:vm-VMID-disk-1,mp=/config,backup=1,size=8G\nmp1: /srv/downloads,mp=/downloads\nnet0: name=eth0,bridge=vmbr0,ip=dhcp,type=veth\nlxc.environment.runtime: PUID=1000\nlxc.environment.runtime: PGID=1000"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"p": "<code>mp0</code> is a second disk that belongs to the container, named <code>vm-VMID-disk-N</code> on the selected storage. It is mounted at <code>/config</code> and, with <code>backup=1</code>, it is part of the container backup. <code>mp1</code> creates no disk: it binds the host directory <code>/srv/downloads</code> to <code>/downloads</code> inside the LXC. Port 8080 is not mapped: the LXC has an address of its own and the service answers on it."
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
{
|
||||
"meta": {
|
||||
"title": "Install an image that is not in the catalog | ProxMenux",
|
||||
"description": "Translate an OCI image from a Compose file, a URL, a docker run command or an image reference, and review what ProxMenux can reproduce before installing it."
|
||||
},
|
||||
"header": {
|
||||
"title": "Install an image that is not in the catalog",
|
||||
"description": "An image of your own is translated from its Compose file, a docker run command or its reference alone, and the result is shown for review before any LXC is created.",
|
||||
"section": "OCI manager Apps"
|
||||
},
|
||||
"sections": [
|
||||
{
|
||||
"id": "intro",
|
||||
"blocks": [
|
||||
{
|
||||
"calloutInfo": {
|
||||
"title": "Same converter, same installer",
|
||||
"body": "The option <strong>Install an image that is not in the catalog</strong> uses the converter and the installer of the catalog templates. The difference is that the contract is generated at that moment from the definition given, and validated before any LXC is created."
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "sources",
|
||||
"title": "How the image is described",
|
||||
"intro": "The first screen asks how the image is described. There are four ways to give a complete definition and a fifth that uses only the image name.",
|
||||
"blocks": [
|
||||
{
|
||||
"table": {
|
||||
"headers": ["Option", "What it reads"],
|
||||
"rows": [
|
||||
["Paste its Compose file in the terminal", "The YAML is pasted in the terminal and ends with Ctrl+D on an empty line."],
|
||||
["Read its Compose file from a file of this host", "A path on the node, <code>/root/docker-compose.yml</code> by default. The file must exist and be at most 256 KiB."],
|
||||
["Download its Compose file from an address", "An <code>http://</code> or <code>https://</code> address that serves the raw YAML. At most 256 KiB are downloaded."],
|
||||
["Paste its docker run command in the terminal", "The command published by the project. Ports, environment, volumes, devices, capabilities, user, shared memory and the other supported options are translated."],
|
||||
["Only the image reference, with no Compose file", "A reference such as <code>ghcr.io/user/application:latest</code>. The registry is queried and the OCI metadata of the image is kept."]
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"figure": {
|
||||
"src": "/oci-manager/custom-source-menu.png",
|
||||
"alt": "Menu that asks how an image that is not in the catalog is described",
|
||||
"caption": "The five ways to describe an image that is not in the catalog."
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "reference-only",
|
||||
"title": "What an image reference alone provides",
|
||||
"intro": "An image carries its process metadata, but not everything a Compose file usually adds around it.",
|
||||
"blocks": [
|
||||
{
|
||||
"table": {
|
||||
"headers": ["Read from the image", "Not in the image", "Consequence"],
|
||||
"rows": [
|
||||
["<code>Entrypoint</code>, <code>Cmd</code>, <code>User</code>, <code>WorkingDir</code> and embedded environment", "Variables that only appear in the documentation", "They are added during the installation or a recreation"],
|
||||
["Volumes declared by the image", "Host directories that only a Compose file names", "Additional paths are added before installing"],
|
||||
["<code>EXPOSE</code> ports", "The URL, protocol or functional healthcheck", "The port and the way the service is checked are confirmed"],
|
||||
["Architectures and digest in the registry", "Devices, privileges or external dependencies", "None of them is enabled without a definition that asks for it"]
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "review",
|
||||
"title": "Review before installing",
|
||||
"intro": "After translating the definition, ProxMenux shows what it understood before asking for VMID, resources or storage.",
|
||||
"blocks": [
|
||||
{
|
||||
"steps": {
|
||||
"items": [
|
||||
{ "title": "Analyse", "body": "Services, image, ports, paths, environment, devices, security and healthcheck are read." },
|
||||
{ "title": "Query the registry", "body": "The image must exist in a public registry, and its published architectures are listed." },
|
||||
{ "title": "List what is not applied", "body": "Labels, Docker networks, Swarm settings and other keys that have no effect on an LXC are listed." },
|
||||
{ "title": "Block what cannot be translated", "body": "A key with no safe equivalent is shown under <strong>What cannot be translated</strong> and the installation does not continue. Nothing is dropped silently." },
|
||||
{ "title": "Ask for secrets", "body": "Variables whose names read as a password, token or key become sensitive questions of the installation." }
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"figure": {
|
||||
"src": "/oci-manager/custom-review.png",
|
||||
"alt": "Summary of what ProxMenux understood from a Compose file",
|
||||
"caption": "The summary shown before the installation asks for VMID and resources."
|
||||
}
|
||||
},
|
||||
{
|
||||
"p": "After accepting the summary, the flow is the one of the catalog: name, default or advanced mode, VMID, CPU, memory, network, start with the node, persistent paths, additional paths, compatible devices and a final summary."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "examples",
|
||||
"title": "Input examples",
|
||||
"blocks": [
|
||||
{
|
||||
"codeGrid": {
|
||||
"items": [
|
||||
{
|
||||
"title": "Compose",
|
||||
"code": "services:\n app:\n image: ghcr.io/example/app:latest\n ports:\n - \"8080:8080\"\n volumes:\n - ./config:/config\n - /srv/media:/media\n environment:\n TZ: Europe/Madrid"
|
||||
},
|
||||
{
|
||||
"title": "docker run",
|
||||
"code": "docker run -d \\\n --name app \\\n -p 8080:8080 \\\n -e TZ=Europe/Madrid \\\n -v app-config:/config \\\n -v /srv/media:/media \\\n ghcr.io/example/app:latest"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "limits",
|
||||
"title": "Definitions that are not installed",
|
||||
"blocks": [
|
||||
{
|
||||
"list": {
|
||||
"items": [
|
||||
"Images in private registries: registry credentials are not requested.",
|
||||
"A Dockerfile without a published image: the image has to be built and published in an OCI registry first.",
|
||||
"A Compose file with several services: it is blocked because it describes more than one image. This option installs a single container.",
|
||||
"<code>configs</code>, external secrets or device formats that cannot be translated unambiguously.",
|
||||
"Shell substitutions such as <code>$(command)</code>: the resulting value has to be written instead.",
|
||||
"<code>docker run</code> options that are not recognised, and <code>--env-file</code>: an explicit Compose file is read instead."
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,232 @@
|
||||
{
|
||||
"meta": {
|
||||
"title": "Devices and acceleration | ProxMenux",
|
||||
"description": "How OCI manager Apps passes GPU, NVIDIA, Coral, USB, FUSE and other devices to an OCI container as validated native Proxmox VE resources."
|
||||
},
|
||||
"header": {
|
||||
"title": "Devices and acceleration",
|
||||
"description": "GPU, NVIDIA, Coral, USB, FUSE and block devices become validated native Proxmox VE resources of the container.",
|
||||
"section": "OCI manager Apps"
|
||||
},
|
||||
"sections": [
|
||||
{
|
||||
"id": "principle",
|
||||
"title": "The device the application needs, not the whole host",
|
||||
"blocks": [
|
||||
{
|
||||
"p": "A device requested by the Compose file or by the application profile becomes a concrete <code>devN</code> entry or LXC mount. Asking for a GPU, a USB device or a Coral does not make the container privileged."
|
||||
},
|
||||
{
|
||||
"flow": {
|
||||
"nodes": [
|
||||
{ "label": "Host inventory", "detail": "/dev/dri/renderD128\nGID 993 · Intel" },
|
||||
{ "label": "ProxMenux", "detail": "vendor and\npermissions checked" },
|
||||
{ "label": "LXC", "detail": "same device\neffective GID" }
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "origin",
|
||||
"title": "Where the device request comes from",
|
||||
"blocks": [
|
||||
{
|
||||
"table": {
|
||||
"headers": ["Source", "What is read", "What the installer does"],
|
||||
"rows": [
|
||||
["Docker Compose", "<code>devices</code>, <code>group_add</code>, <code>deploy.resources</code> and NVIDIA requests", "Each requirement becomes a device request shown for review"],
|
||||
["Catalog profile", "The GPU, Coral, OpenCL, USB or FUSE support the application actually has", "Only the options validated for that image are offered"],
|
||||
["Image metadata and documentation", "VA-API, Selkies, LinuxServer mods or the NVIDIA runtime", "The documented variables and preparation are added"],
|
||||
["User selection", "CPU only, Intel/AMD, OpenCL, NVIDIA or an optional device", "The selection is stored in the instance contract"]
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"calloutWarning": {
|
||||
"title": "Detected devices are not attached on their own",
|
||||
"body": "The host is inventoried, but only devices declared by the Compose file or by a compatible profile are offered and attached. A GPU, USB dongle or Coral present on the host is not exposed to every LXC."
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "identify",
|
||||
"title": "How the host device is identified",
|
||||
"intro": "Before the LXC is modified, the device is read on the host and matched against the chosen profile.",
|
||||
"blocks": [
|
||||
{
|
||||
"table": {
|
||||
"headers": ["Type", "Identity", "Validation"],
|
||||
"rows": [
|
||||
["Intel/AMD DRM", "<code>/dev/dri/renderD*</code> and <code>/sys/class/drm/NODE/device/vendor</code>", "A character device with vendor <code>0x8086</code> (Intel) or <code>0x1002</code> (AMD)"],
|
||||
["AMD OpenCL", "The render node, plus <code>/dev/kfd</code> when the profile needs it", "Existence, type, vendor, permissions and declared compatibility"],
|
||||
["NVIDIA", "<code>nvidia-smi</code> and <code>nvidia-container-cli</code>", "GPU, UUID, PCI bus, driver version, Toolkit, <code>/dev/nvidia*</code> nodes, binaries and libraries"],
|
||||
["Coral PCIe/M.2", "<code>/dev/apex_N</code> and its link in <code>/sys/dev/char/MAJOR:MINOR</code>", "Character node, major/minor, owner, GID and permissions"],
|
||||
["USB and serial", "<code>/dev/ttyUSB*</code>, <code>/dev/ttyACM*</code> or <code>/dev/bus/usb/BBB/DDD</code>", "Character node; for USB also vendor, product and serial when sysfs publishes them"],
|
||||
["KVM, TUN, FUSE, video and generic SCSI", "<code>/dev/kvm</code>, <code>/dev/net/tun</code>, <code>/dev/fuse</code>, <code>/dev/videoN</code> or <code>/dev/sgN</code>", "Supported path, node type and effective permissions"],
|
||||
["Optical drive", "<code>/dev/srN</code>", "A block device"]
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "install",
|
||||
"title": "What happens during the installation",
|
||||
"blocks": [
|
||||
{
|
||||
"steps": {
|
||||
"items": [
|
||||
{ "title": "The template offers its profiles", "body": "For example CPU only, Intel/AMD VA-API, AMD OpenCL, Intel OpenCL or NVIDIA. The options belong to the image, not to a common menu." },
|
||||
{ "title": "A profile is chosen", "body": "It defines the device nodes, environment, mods or runtime the application needs." },
|
||||
{ "title": "A path is proposed", "body": "For DRM, <code>/dev/dri/renderD128</code>, which can be changed on a host with several render nodes. For USB or serial, the concrete node is selected." },
|
||||
{ "title": "Validation", "body": "Existence, type, allowed vendor, permissions and GID are checked. A mismatch stops the operation." },
|
||||
{ "title": "The contract is written", "body": "Path, mode, GID, write access and profile are recorded for updates and recreations." },
|
||||
{ "title": "Attach and test", "body": "<code>pct set</code> adds the <code>devN</code> entry and access is then checked inside the LXC. LinuxServer images are also checked as user <code>abc</code>." }
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "config",
|
||||
"title": "How it appears in the LXC configuration",
|
||||
"intro": "Illustrative values: <code>dev0</code> and <code>dev1</code> are the free slots Proxmox VE assigns, and <code>renderD128</code>, <code>apex_0</code> and the GID depend on the hardware of the node.",
|
||||
"blocks": [
|
||||
{
|
||||
"codeGrid": {
|
||||
"items": [
|
||||
{ "title": "Intel/AMD VA-API", "code": "dev0: path=/dev/dri/renderD128,mode=0660,gid=993,deny-write=0" },
|
||||
{ "title": "Coral PCIe/M.2", "code": "dev0: path=/dev/apex_0,mode=0660,gid=GID,deny-write=0" },
|
||||
{ "title": "A specific USB device", "code": "dev0: path=/dev/bus/usb/003/004,mode=0660,gid=GID,deny-write=0" },
|
||||
{ "title": "AMD OpenCL", "code": "dev0: path=/dev/dri/renderD128,mode=0660,gid=GID,deny-write=0\ndev1: path=/dev/kfd,mode=0660,gid=GID,deny-write=0" }
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"p": "The GID is read with <code>stat</code> on the host and written to the <code>devN</code> entry; the <code>render</code> and <code>video</code> groups are not assumed to have a fixed number. The device keeps the same <code>/dev</code> path inside the LXC, where the application's own mechanisms look for it."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "profiles",
|
||||
"title": "Profiles an image can offer",
|
||||
"blocks": [
|
||||
{
|
||||
"table": {
|
||||
"headers": ["Profile", "Translation", "Offered when"],
|
||||
"rows": [
|
||||
["Intel/AMD VA-API", "<code>/dev/dri</code> render node", "The application supports video acceleration"],
|
||||
["OpenCL", "Render node, <code>/dev/kfd</code> when needed and the official mod", "The image or profile documents it"],
|
||||
["NVIDIA", "Driver devices and libraries of the host", "The host has a working driver and the NVIDIA Container Toolkit"],
|
||||
["Coral", "<code>/dev/apex_0</code> or the USB bus", "The profile declares Coral support (Frigate)"],
|
||||
["USB, serial, FUSE", "A single device, a validated tree or an LXC feature", "The contract asks for it"]
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "nvidia",
|
||||
"title": "NVIDIA",
|
||||
"blocks": [
|
||||
{
|
||||
"calloutWarning": {
|
||||
"title": "Node requirement: NVIDIA Container Toolkit",
|
||||
"body": "A working driver on Proxmox VE is not enough to give an NVIDIA GPU to an OCI image. OCI manager Apps uses <code>nvidia-container-cli</code>, from the NVIDIA Container Toolkit, to identify the devices and to obtain the binaries and libraries that match the loaded driver."
|
||||
}
|
||||
},
|
||||
{
|
||||
"p": "The <nvidiaLink>ProxMenux NVIDIA installer</nvidiaLink> installs the NVIDIA Container Toolkit from the official NVIDIA repository together with the driver. It checks its four packages, validates <code>nvidia-container-cli</code> and records the result in the change journal of <auditLink>Audit & Report</auditLink>."
|
||||
},
|
||||
{
|
||||
"p": "These two commands on the host show whether the driver and the Toolkit are available:"
|
||||
},
|
||||
{
|
||||
"shell": { "code": "nvidia-smi -L\nnvidia-container-cli --version" }
|
||||
},
|
||||
{
|
||||
"p": "On a host where the driver was installed by other means, the Toolkit is installed from the official stable repository:"
|
||||
},
|
||||
{
|
||||
"shell": {
|
||||
"code": "apt-get update\napt-get install -y --no-install-recommends ca-certificates curl gnupg2\n\ncurl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey \\\n | gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg\n\ncurl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list \\\n | sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' \\\n > /etc/apt/sources.list.d/nvidia-container-toolkit.list\n\napt-get update\napt-get install -y nvidia-container-toolkit libnvidia-container-tools"
|
||||
}
|
||||
},
|
||||
{
|
||||
"p": "The inventory OCI manager Apps uses is the output of:"
|
||||
},
|
||||
{
|
||||
"shell": { "code": "nvidia-container-cli list --device all --libraries --binaries --firmwares --ipcs" }
|
||||
},
|
||||
{
|
||||
"calloutInfo": {
|
||||
"title": "Docker runtime configuration is not involved",
|
||||
"body": "The containers are native LXCs and no Docker daemon is used, so <code>nvidia-ctk runtime configure --runtime=docker</code> plays no part: ProxMenux queries <code>nvidia-container-cli</code> directly and writes the LXC devices and mounts. The commands and supported platforms are maintained in the <toolkitLink>NVIDIA Container Toolkit installation guide</toolkitLink>."
|
||||
}
|
||||
},
|
||||
{
|
||||
"cards": {
|
||||
"items": [
|
||||
{ "icon": "cpu", "title": "Inventory from the driver", "body": "<code>nvidia-container-cli</code> lists the device nodes, binaries, firmware and libraries of the installed driver." },
|
||||
{ "icon": "refresh", "title": "No fixed version", "body": "The template does not name library files. The profile is generated from the current host." },
|
||||
{ "icon": "shield", "title": "Read-only mounts", "body": "The host libraries are mounted read-only instead of being copied into the container." },
|
||||
{ "icon": "hardDrive", "title": "Driver changes", "body": "After a driver change the inventory is generated again before the affected LXCs start." }
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"code": {
|
||||
"title": "NVIDIA result (simplified)",
|
||||
"code": "devN: path=/dev/nvidia0,...\ndevN: path=/dev/nvidiactl,...\ndevN: path=/dev/nvidia-uvm,...\nlxc.mount.entry: HOST_LIBRARY CONTAINER_LIBRARY none ro,bind,create=file 0 0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"p": "Passing only <code>/dev/nvidia0</code> is not enough. The user-space components of the loaded driver are mounted read-only, and <code>nvidia-smi</code> then runs inside the LXC to compare GPU, UUID, PCI bus and version with the host inventory."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "usb",
|
||||
"title": "USB, serial and USB Coral",
|
||||
"blocks": [
|
||||
{
|
||||
"calloutWarning": {
|
||||
"title": "USB numbering can change",
|
||||
"body": "A path such as <code>/dev/bus/usb/003/004</code> can change when the device is reconnected or the host restarts. The profile records vendor, product and serial when they are available, but a new bus address is not remapped automatically."
|
||||
}
|
||||
},
|
||||
{
|
||||
"p": "A peripheral is given by its concrete node: <code>/dev/ttyUSB0</code>, <code>/dev/ttyACM0</code>, <code>/dev/apex_0</code> or <code>/dev/bus/usb/BBB/DDD</code>. Passing the whole of <code>/dev</code> is not accepted. Coral is offered only to applications whose profile declares it."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "trees",
|
||||
"title": "Device trees and LXC features",
|
||||
"blocks": [
|
||||
{
|
||||
"table": {
|
||||
"headers": ["Request", "Translation", "Scope"],
|
||||
"rows": [
|
||||
["<code>/dev/dvb</code>, <code>/dev/snd</code> or <code>/dev/bus/usb</code>", "Each character node of the tree gets its own <code>devN</code> entry with the host mode and GID", "Only the requested tree, not the rest of <code>/dev</code>"],
|
||||
["<code>/dev/fuse</code>", "The node and, when the profile needs it, the <code>fuse=1</code> feature", "FUSE alone does not publish mounts to other LXCs"],
|
||||
["<code>/dev/net/tun</code>", "A <code>devN</code> entry at the same path inside the LXC", "The VPN or network configuration stays in the application"],
|
||||
["<code>/dev/kvm</code>", "A validated <code>devN</code> entry", "Offered only when the contract asks for it"]
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "security",
|
||||
"title": "Confirmations by level of risk",
|
||||
"blocks": [
|
||||
{
|
||||
"p": "Concrete devices, optional privilege, required privilege, AppArmor or seccomp relaxation and access to the host PID namespace are treated as separate cases, not under one generic privileged label. Each option with a risk is explained and confirmed during the installation."
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
{
|
||||
"meta": {
|
||||
"title": "OCI manager Apps | ProxMenux",
|
||||
"description": "Run official OCI images as native Proxmox VE LXC containers, with persistent data, devices, multi-container applications and transactional updates."
|
||||
},
|
||||
"header": {
|
||||
"title": "OCI manager Apps",
|
||||
"description": "Official OCI images run as native Proxmox VE LXC containers. The image stays the one its maintainer publishes; ProxMenux reproduces the environment Docker Compose would have created around it.",
|
||||
"section": "OCI manager Apps"
|
||||
},
|
||||
"sections": [
|
||||
{
|
||||
"id": "model",
|
||||
"blocks": [
|
||||
{
|
||||
"calloutInfo": {
|
||||
"title": "One OCI image, one native LXC",
|
||||
"body": "No Docker engine runs inside the containers. Proxmox VE imports the image's filesystem and its OCI metadata, and the application becomes the main process of a native LXC, managed with the same tools as any other container of the node."
|
||||
}
|
||||
},
|
||||
{
|
||||
"flow": {
|
||||
"nodes": [
|
||||
{ "label": "Source", "detail": "OCI image\nCompose and documentation" },
|
||||
{ "label": "ProxMenux", "detail": "JSON contract\nvalidation and plan" },
|
||||
{ "label": "Proxmox VE", "detail": "native LXC\nvolumes and devices" }
|
||||
],
|
||||
"caption": "The application is not rebuilt: its environment is reproduced declaratively."
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "where",
|
||||
"title": "Where it is",
|
||||
"intro": "OCI manager Apps opens from option <strong>OCI manager Apps (beta)</strong> of the ProxMenux main menu, on the Proxmox node and as root. Its first screen offers:",
|
||||
"blocks": [
|
||||
{
|
||||
"list": {
|
||||
"items": [
|
||||
"<strong>Search applications</strong> — search by name across the catalog.",
|
||||
"<strong>All applications</strong> — the complete catalog, with the number of applications.",
|
||||
"<strong>Manage installed OCI applications</strong> — update, recreate, remove or recover what was installed.",
|
||||
"<strong>Install an image that is not in the catalog</strong> — translate a Compose file, a <code>docker run</code> command or an image reference.",
|
||||
"The catalog categories, each with its number of applications."
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"figure": {
|
||||
"src": "/oci-manager/main-menu.png",
|
||||
"alt": "Main screen of OCI manager Apps with search, all applications, management, custom image and the catalog categories",
|
||||
"caption": "Main screen of OCI manager Apps."
|
||||
}
|
||||
},
|
||||
{
|
||||
"p": "Selecting an application shows its description, the image it runs and two installation modes: <strong>Install with default settings</strong>, which asks almost nothing, and <strong>Install with advanced settings</strong>, which offers the real storage, bridge and resource selectors of the node."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "translation",
|
||||
"title": "What Docker expresses and what OCI manager Apps turns it into",
|
||||
"blocks": [
|
||||
{
|
||||
"table": {
|
||||
"headers": ["Requirement", "Docker expresses it as", "OCI manager Apps turns it into"],
|
||||
"rows": [
|
||||
["Run the application", "<code>image</code>, <code>entrypoint</code>, <code>command</code>", "OCI rootfs and the main process of the LXC"],
|
||||
["Keep configuration", "<code>volumes: /config</code>", "a persistent <code>mpN</code> disk included in the backup, or a host directory"],
|
||||
["Publish the service", "<code>ports</code>", "an address of its own for the LXC and the access URL of the service"],
|
||||
["Use hardware", "<code>devices</code>, <code>group_add</code>", "<code>devN</code> entries with the effective host GID and a validated profile"],
|
||||
["Connect dependencies", "<code>networks</code>, <code>depends_on</code>", "a private bridge, fixed addresses, start order and healthchecks"],
|
||||
["Update", "<code>pull</code> and recreate", "a new rootfs with the same persistent contract"]
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "kept",
|
||||
"title": "What is kept and what changes",
|
||||
"blocks": [
|
||||
{
|
||||
"cards": {
|
||||
"items": [
|
||||
{ "icon": "archive", "title": "Kept", "body": "The official image, its internal paths, its environment, its startup process and its functional documentation." },
|
||||
{ "icon": "boxes", "title": "Adapted", "body": "The runtime environment: network, persistence, devices, permissions and dependencies use native Proxmox VE LXC primitives." },
|
||||
{ "icon": "shield", "title": "Not translated", "body": "A Compose key with no safe equivalent is listed as a blocker. Templates with blockers are not offered, and sensitive options are confirmed during the installation." },
|
||||
{ "icon": "refresh", "title": "Recorded", "body": "Image digest, resources, paths, network, devices and stack membership are stored in the instance contract, which updates and recreations reproduce." }
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "pages",
|
||||
"title": "Pages of this section",
|
||||
"blocks": [
|
||||
{
|
||||
"next": {
|
||||
"items": [
|
||||
{ "label": "How an OCI image is translated", "href": "/docs/oci-manager/architecture", "tail": "from the image and its Compose file to a native LXC." },
|
||||
{ "label": "Install an image that is not in the catalog", "href": "/docs/oci-manager/custom-image", "tail": "Compose, docker run or an image reference." },
|
||||
{ "label": "Data, paths and networking", "href": "/docs/oci-manager/storage-network", "tail": "container disks, host directories, Rclone mounts and addresses." },
|
||||
{ "label": "Devices and acceleration", "href": "/docs/oci-manager/hardware", "tail": "GPU, NVIDIA, Coral, USB and other devices." },
|
||||
{ "label": "Multi-container applications", "href": "/docs/oci-manager/stacks", "tail": "application, database and cache as coordinated LXCs." },
|
||||
{ "label": "Install, update and recreate", "href": "/docs/oci-manager/lifecycle", "tail": "the instance contract and its operations." },
|
||||
{ "label": "In ProxMenux Monitor", "href": "/docs/oci-manager/monitor", "tail": "versions, updates, console output and terminal." }
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,243 @@
|
||||
{
|
||||
"meta": {
|
||||
"title": "Install, update and recreate | ProxMenux",
|
||||
"description": "The instance contract of an OCI container and the operations that use it: update, recreate, remove and recovery of an interrupted operation."
|
||||
},
|
||||
"header": {
|
||||
"title": "Install, update and recreate",
|
||||
"description": "Each instance keeps a reproducible contract, so its rootfs can be replaced without losing configuration or persistent data.",
|
||||
"section": "OCI manager Apps"
|
||||
},
|
||||
"sections": [
|
||||
{
|
||||
"id": "contract",
|
||||
"title": "The instance contract",
|
||||
"blocks": [
|
||||
{
|
||||
"p": "After an installation, the effective configuration is stored in <code>/usr/local/share/proxmenux/oci/instances/VMID/oci-compose.json</code>. It is not a copy of the original Compose file: it is the reproducible contract of the LXC that exists on this host."
|
||||
},
|
||||
{
|
||||
"code": {
|
||||
"code": "instances/\n└── 105/\n └── oci-compose.json\n ├── image and resolved digest\n ├── resources and network\n ├── environment (secrets protected)\n ├── container disks and host directories\n ├── hardware profile and devices\n ├── console log and terminal mode\n └── stack membership and lifecycle"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "manage",
|
||||
"title": "Manage installed OCI applications",
|
||||
"intro": "This option of the main screen lists the registered instances with their application and image. Each one is checked against its contract before any action; a CT that no longer matches its record is neither modified nor deleted. The same update and recreation are offered in the Updates tab of <monitorLink>ProxMenux Monitor</monitorLink>.",
|
||||
"blocks": [
|
||||
{
|
||||
"table": {
|
||||
"headers": [
|
||||
"Option",
|
||||
"What changes",
|
||||
"What is kept"
|
||||
],
|
||||
"rows": [
|
||||
[
|
||||
"Update the image with the saved configuration",
|
||||
"The rootfs is replaced by the image the saved channel publishes today",
|
||||
"Contract, container disks, host directories, network and devices"
|
||||
],
|
||||
[
|
||||
"Recreate: edit resources, network, paths and GPU",
|
||||
"The editor opens with the current contract; the CT is rebuilt with the changes",
|
||||
"The data of container disks and host directories"
|
||||
],
|
||||
[
|
||||
"Remove: delete the application and its containers",
|
||||
"The LXC, or every member of a stack, and their contracts are deleted",
|
||||
"Host directories, with their content"
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"p": "For a multi-container application the menu offers <strong>Update every container of the application</strong> and the removal. A stack is not recreated."
|
||||
},
|
||||
{
|
||||
"figure": {
|
||||
"src": "/oci-manager/manage-menu.png",
|
||||
"alt": "List of installed OCI applications and the update, recreate and remove options",
|
||||
"caption": "Manage installed OCI applications."
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "update",
|
||||
"title": "A transactional update",
|
||||
"blocks": [
|
||||
{
|
||||
"mermaid": {
|
||||
"chartCode": "sequenceDiagram\n participant U as {{user}}\n participant P as ProxMenux\n participant R as {{registry}}\n participant X as Proxmox VE\n U->>P: {{update}}\n P->>R: {{resolve}}\n R-->>P: digest\n P->>P: {{verify}}\n P->>X: {{backup}}\n P->>X: {{import}}\n P->>X: {{reapply}}\n X-->>P: healthcheck\n alt {{healthy}}\n P-->>U: {{commit}}\n else {{failure}}\n P->>X: Rollback\n P-->>U: {{restored}}\n end",
|
||||
"labels": {
|
||||
"user": "User",
|
||||
"registry": "OCI registry",
|
||||
"update": "Update instance",
|
||||
"resolve": "Resolve the tag",
|
||||
"verify": "Verify archive and preflight",
|
||||
"backup": "Stop and back up the CT",
|
||||
"import": "Import the new rootfs",
|
||||
"reapply": "Apply the contract again",
|
||||
"healthy": "healthy",
|
||||
"failure": "failure",
|
||||
"commit": "Contract published with the new digest",
|
||||
"restored": "Previous instance restored"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"list": {
|
||||
"items": [
|
||||
"When the registry still serves the installed digest, nothing is downloaded and the instance is not touched.",
|
||||
"The new image is verified layer by layer before the CT stops. A download that arrives damaged is fetched a second time; an image already cached that fails the check is downloaded again.",
|
||||
"Settings changed in Proxmox VE after the installation (memory, swap, cores, CPU limit, CPU priority, start with the node) are kept and carried into the new contract.",
|
||||
"Any other difference between the CT and its contract stops the update before the CT is stopped."
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"flow": {
|
||||
"nodes": [
|
||||
{
|
||||
"label": "Before",
|
||||
"detail": "rootfs A\n/config mp0\n/media host directory"
|
||||
},
|
||||
{
|
||||
"label": "Update",
|
||||
"detail": "replaces only\nthe rootfs"
|
||||
},
|
||||
{
|
||||
"label": "After",
|
||||
"detail": "rootfs B\n/config mp0\n/media host directory"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "recovery",
|
||||
"title": "Recovering an interrupted operation",
|
||||
"intro": "Update and recreation use a persistent transaction. If the process, the terminal or the node is interrupted after the CT stops, the operation is not taken as finished.",
|
||||
"blocks": [
|
||||
{
|
||||
"steps": {
|
||||
"items": [
|
||||
{
|
||||
"title": "Pending marker",
|
||||
"body": "When Manage installed OCI applications opens, the saved state shows that the replacement was never published."
|
||||
},
|
||||
{
|
||||
"title": "View status",
|
||||
"body": "Shows the phase that was reached without changing containers or data."
|
||||
},
|
||||
{
|
||||
"title": "Recover the previous installation",
|
||||
"body": "Restores the verified native backup taken before the replacement and the previous contract."
|
||||
},
|
||||
{
|
||||
"title": "Stacks as a unit",
|
||||
"body": "For a multi-container application, every member is recovered from the same transaction point, not only the selected one."
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"calloutWarning": {
|
||||
"title": "Host directories are outside the rollback",
|
||||
"body": "The backup covers the rootfs and the container disks it includes. A host directory is not reverted, because other LXCs may use its data. Updating, recreating or recovering an instance with host directories asks for a confirmation of this first."
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "remove",
|
||||
"title": "Removing an OCI application",
|
||||
"blocks": [
|
||||
{
|
||||
"p": "Before the confirmation, a summary is built from the real configuration: the containers that are removed, the data deleted with them, the private network that is released and the host directories that are kept. The confirmation defaults to <strong>No</strong>, since the data of the deleted disks cannot be recovered afterwards."
|
||||
},
|
||||
{
|
||||
"table": {
|
||||
"headers": [
|
||||
"Resource",
|
||||
"On removal",
|
||||
"Reason"
|
||||
],
|
||||
"rows": [
|
||||
[
|
||||
"rootfs and container disks",
|
||||
"Deleted",
|
||||
"They belong only to the container"
|
||||
],
|
||||
[
|
||||
"Host directory",
|
||||
"Kept, with its content",
|
||||
"Other applications may use it"
|
||||
],
|
||||
[
|
||||
"Instance contract",
|
||||
"Retired after a successful removal",
|
||||
"No CT is associated with it any more"
|
||||
],
|
||||
[
|
||||
"Private bridge of a stack",
|
||||
"Released with the stack",
|
||||
"It has no members left to connect"
|
||||
],
|
||||
[
|
||||
"A single member of a stack",
|
||||
"Not removed on its own",
|
||||
"The whole application is removed, so no stack is left incomplete"
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"figure": {
|
||||
"src": "/oci-manager/remove-summary.png",
|
||||
"alt": "Removal summary with the containers, data and host directories affected",
|
||||
"caption": "The removal summary, built from the real configuration."
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "archives",
|
||||
"title": "Downloaded images and host space",
|
||||
"blocks": [
|
||||
{
|
||||
"p": "The OCI archive is used to build the rootfs; the running CT does not read it. After an installation or an update, the downloaded archives of that operation are listed with their size and their deletion is offered. Deleting them frees the space without affecting the container or its data."
|
||||
},
|
||||
{
|
||||
"calloutInfo": {
|
||||
"title": "Updates do not need the archive",
|
||||
"body": "Without the archive, an update resolves the saved channel and downloads the new digest. With it, an archive is reused only when its reference and integrity match what is requested."
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "registry",
|
||||
"title": "Registry and cleanup",
|
||||
"blocks": [
|
||||
{
|
||||
"p": "The registered contracts are compared with the real CTs. A contract is orphaned only when its VMID no longer exists or no longer carries the expected instance identity. The cleanup does not delete volumes or external data by inference."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "channel",
|
||||
"title": "A rolling tag is not an unattended update",
|
||||
"blocks": [
|
||||
{
|
||||
"p": "The catalog installs the rolling tag its maintainer publishes, but the effective digest is resolved, recorded and changed only through an explicit update with preflight and rollback. <monitorLink>ProxMenux Monitor</monitorLink> compares the installed digest with the one the registry publishes and shows, and notifies, when a new image is available."
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
{
|
||||
"meta": {
|
||||
"title": "OCI containers in ProxMenux Monitor | ProxMenux",
|
||||
"description": "What ProxMenux Monitor shows for a container installed by OCI manager Apps: application and image versions, new images, console output and the Proxmox VE terminal."
|
||||
},
|
||||
"header": {
|
||||
"title": "In ProxMenux Monitor",
|
||||
"description": "A container installed by OCI manager Apps appears in VMs & LXCs like any other LXC. Its modal reads the installation record: application and image versions, new images, console output and terminal.",
|
||||
"section": "OCI manager Apps"
|
||||
},
|
||||
"sections": [
|
||||
{
|
||||
"id": "intro",
|
||||
"blocks": [
|
||||
{
|
||||
"calloutInfo": {
|
||||
"title": "The record is the source",
|
||||
"body": "OCI manager Apps knows what it installed and where it came from. The Monitor reads that installation record instead of probing the container, so the data is the same whether the container is running or stopped."
|
||||
}
|
||||
},
|
||||
{
|
||||
"table": {
|
||||
"headers": [
|
||||
"Tab",
|
||||
"What changes for an OCI container"
|
||||
],
|
||||
"rows": [
|
||||
[
|
||||
"App",
|
||||
"The application is identified from the record, and updates are tracked by image"
|
||||
],
|
||||
[
|
||||
"Updates",
|
||||
"The image is updated or the container recreated, with the same flow as the OCI menu"
|
||||
],
|
||||
[
|
||||
"Mounts",
|
||||
"Container disks and host directories, with their usage"
|
||||
],
|
||||
[
|
||||
"Logs",
|
||||
"Only for OCI containers: the console output of the application"
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "app",
|
||||
"title": "App",
|
||||
"intro": "The <appLink>App</appLink> tab offers the installed application as a detection, with the name, logo, port and scheme of the record. Registering it opens the editor with the method <strong>OCI image (installed by ProxMenux)</strong>, which needs no configuration.",
|
||||
"blocks": [
|
||||
{
|
||||
"cards": {
|
||||
"items": [
|
||||
{
|
||||
"icon": "boxes",
|
||||
"title": "Application",
|
||||
"body": "The version of the application inside the image, read from the image itself: its environment or its <code>org.opencontainers.image.version</code> label. It is informative."
|
||||
},
|
||||
{
|
||||
"icon": "archive",
|
||||
"title": "Image",
|
||||
"body": "The build date and digest of the installed image. The update is decided here: the installed digest is compared with the one the registry publishes today for the same tag."
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"list": {
|
||||
"items": [
|
||||
"When the registry publishes a new digest, the card shows <strong>New image</strong> with its date and digest, even if the application version inside did not change: a rebuild on an updated base is an update.",
|
||||
"When the digests match, the card reads <strong>Version</strong>.",
|
||||
"The card links to the repository of the image: its GitHub project, or its Docker Hub page for an official image.",
|
||||
"The access link uses the LAN address of the container, also for the main member of a multi-container application, which has a second address on its private network.",
|
||||
"The button <strong>Refresh data</strong> reads the record and the registry again. The options to search for applications or register another one are not offered: the container holds exactly the application of its record."
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"figure": {
|
||||
"src": "/oci-manager/monitor-app-tab.png",
|
||||
"alt": "App tab of an OCI container with the application version, the image and the repository link",
|
||||
"caption": "App tab of a container installed by OCI manager Apps."
|
||||
}
|
||||
},
|
||||
{
|
||||
"p": "The check runs once a day with the update checks of the Monitor, and <strong>Refresh data</strong> runs it at once. A new image is sent as a notification through the channels configured in <notificationsLink>Notifications</notificationsLink>. The update is applied from the Updates tab."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "updates",
|
||||
"title": "Updates",
|
||||
"intro": "For a container installed by OCI manager Apps the Updates tab shows the application with its installed image and, when the registry publishes one, the new image, in the same format as any other application. The package and application updaters of an ordinary LXC do not appear.",
|
||||
"blocks": [
|
||||
{
|
||||
"table": {
|
||||
"headers": [
|
||||
"Button",
|
||||
"What it opens"
|
||||
],
|
||||
"rows": [
|
||||
[
|
||||
"Update",
|
||||
"The update of <lifecycleLink>Manage installed OCI applications</lifecycleLink> for this container, in the Monitor terminal. It can run whether or not there is a new image; with none, nothing is changed. In a multi-container application it updates every member."
|
||||
],
|
||||
[
|
||||
"Recreate",
|
||||
"The recreation editor (resources, network, paths and GPU). It is not offered for a multi-container application."
|
||||
],
|
||||
[
|
||||
"Recover",
|
||||
"Replaces Update when an operation on the container was interrupted, and opens its recovery."
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"p": "External changes, host directories and multi-container applications are handled as in the OCI menu. When the terminal closes, the image, the record and the mounts are read again."
|
||||
},
|
||||
{
|
||||
"table": {
|
||||
"headers": [
|
||||
"Option",
|
||||
"Behaviour"
|
||||
],
|
||||
"rows": [
|
||||
[
|
||||
"Keep the backup taken before updating",
|
||||
"Every update backs up the container to restore it if the update fails. With this option that same backup is kept in the chosen storage and appears among the backups of the CT; on Proxmox Backup Server a backup is written before the update."
|
||||
],
|
||||
[
|
||||
"Scheduled updates",
|
||||
"The image is updated at the chosen time only when the registry publishes a new one, and optionally only once it is 1, 3, 7 or 14 days old. A container with changes made outside ProxMenux is skipped and reported; one with host directories runs only when that was confirmed when the schedule was saved."
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "logs",
|
||||
"title": "Logs",
|
||||
"intro": "The Logs tab appears only for containers installed by OCI manager Apps, between Mounts and Backups. It shows the standard output and error of the main process of the image, the same output <code>docker logs</code> shows for a Docker container.",
|
||||
"blocks": [
|
||||
{
|
||||
"list": {
|
||||
"items": [
|
||||
"The output is kept on the host in <code>/var/log/proxmenux/oci/VMID.console.log</code> (mode 0600), from the first start and across restarts, so it can be read with the container stopped.",
|
||||
"The file is rotated at 10 MB, keeping three compressed copies (<code>/etc/logrotate.d/proxmenux-oci</code>).",
|
||||
"The last 100, 500 or 1000 lines are shown. While the container runs, new lines are followed live; scrolling up pauses the follow, and <strong>Follow</strong> resumes it.",
|
||||
"A filter shows only the lines that contain a text, and <strong>Download</strong> saves the lines loaded.",
|
||||
"Colour codes are removed and a line that a progress bar redraws is shown in its final state.",
|
||||
"The tab reads the file on every open; it is not cached."
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"calloutInfo": {
|
||||
"title": "First-start credentials",
|
||||
"body": "Images that print a generated password on their first start leave it in this output, as <code>docker logs</code> does. The installer reads it from there to show it in its summary."
|
||||
}
|
||||
},
|
||||
{
|
||||
"figure": {
|
||||
"src": "/oci-manager/monitor-logs-tab.png",
|
||||
"alt": "Logs tab with the console output of an OCI container, the line selector, the filter and the follow button",
|
||||
"caption": "Console output of an OCI container."
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "terminal",
|
||||
"title": "Proxmox VE console",
|
||||
"blocks": [
|
||||
{
|
||||
"p": "An OCI image runs its own process as PID 1 and no login service, so the default console of an LXC would open a terminal nothing answers on. Containers installed by OCI manager Apps are created with <code>cmode: shell</code>: the Proxmox VE console opens a shell with <code>lxc-attach</code>, the equivalent of <code>docker exec</code>, with the running application untouched."
|
||||
},
|
||||
{
|
||||
"list": {
|
||||
"items": [
|
||||
"The shell is the one <code>/etc/passwd</code> gives to root in the image. An image whose root has no shell, or ships none, keeps the default console.",
|
||||
"The shell is root inside the container, without a password. Who can open it is decided by the <code>VM.Console</code> permission of Proxmox VE.",
|
||||
"The terminal of the Monitor enters the container with <code>pct enter</code>, which works the same way."
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "mounts",
|
||||
"title": "Mounts",
|
||||
"blocks": [
|
||||
{
|
||||
"p": "The <mountsLink>Mounts</mountsLink> tab lists the container disks and host directories of the container. The usage of a container disk on block storage such as LVM-thin is read from the filesystem of the disk, which is mounted only inside the container."
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,383 @@
|
||||
{
|
||||
"meta": {
|
||||
"title": "Multi-container applications | ProxMenux",
|
||||
"description": "How OCI manager Apps turns an application with its database and cache into coordinated native LXCs: plan, private network, dependency hook, validation and transactional updates."
|
||||
},
|
||||
"header": {
|
||||
"title": "Multi-container applications",
|
||||
"description": "An application with its database, cache and other services becomes several coordinated native LXCs, installed and updated as one.",
|
||||
"section": "OCI manager Apps"
|
||||
},
|
||||
"sections": [
|
||||
{
|
||||
"id": "intro",
|
||||
"blocks": [
|
||||
{
|
||||
"calloutInfo": {
|
||||
"title": "One application for the user, several LXCs for Proxmox VE",
|
||||
"body": "A multi-container definition is one entry of the catalog. The installer creates one native LXC per service and keeps their dependencies explicit. Immich, Nextcloud, Paperless-ngx and Tandoor are installed this way."
|
||||
}
|
||||
},
|
||||
{
|
||||
"mermaid": {
|
||||
"chartCode": "flowchart LR\n C[\"Docker Compose\"] --> O[\"{{plan}}\"]\n O --> A[\"{{app}}<br/>{{appNet}}\"]\n O --> D[\"{{db}}<br/>{{private}}\"]\n O --> R[\"{{cache}}<br/>{{private}}\"]\n V1[(\"config\")] --> A\n V2[(\"database\")] --> D\n V3[(\"cache\")] --> R\n D --> A\n R --> A",
|
||||
"labels": {
|
||||
"plan": "Stack plan",
|
||||
"app": "Application LXC",
|
||||
"appNet": "LAN + private",
|
||||
"db": "PostgreSQL LXC",
|
||||
"cache": "Valkey LXC",
|
||||
"private": "private"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "plan",
|
||||
"title": "1. The plan, before any container exists",
|
||||
"blocks": [
|
||||
{
|
||||
"p": "No LXC is created while the Compose definition is interpreted. A complete plan comes first, with every member, image, VMID, network, path, secret, order and health check. If the plan is not consistent, the installation does not start."
|
||||
},
|
||||
{
|
||||
"table": {
|
||||
"headers": [
|
||||
"Plan element",
|
||||
"Contents",
|
||||
"Checked before creating anything"
|
||||
],
|
||||
"rows": [
|
||||
[
|
||||
"Members",
|
||||
"Main application, database, cache, machine learning and other dependencies",
|
||||
"Unique VMIDs, known roles and exactly one main member"
|
||||
],
|
||||
[
|
||||
"Images",
|
||||
"Rolling reference, architecture and resolved digest of each service",
|
||||
"All exist, support the architecture and pass the OCI integrity check"
|
||||
],
|
||||
[
|
||||
"Network",
|
||||
"Private bridge, subnet, a fixed address per service and LAN access for the main member",
|
||||
"No collision with existing bridges or subnets and no repeated address"
|
||||
],
|
||||
[
|
||||
"Persistence",
|
||||
"Container disks, host directories, owners and backup",
|
||||
"No overlapping paths, storage available and declared permissions"
|
||||
],
|
||||
[
|
||||
"Secrets",
|
||||
"Database password, application keys and initial credentials",
|
||||
"Generated once and given only to the members that use them"
|
||||
],
|
||||
[
|
||||
"Lifecycle",
|
||||
"Start order, stop order and a health check per member",
|
||||
"The main application starts last and stops first"
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "create",
|
||||
"title": "2. Creation, member by member",
|
||||
"blocks": [
|
||||
{
|
||||
"steps": {
|
||||
"items": [
|
||||
{
|
||||
"title": "Reserve every VMID",
|
||||
"body": "The Proxmox VE inventory and the instance registry are checked. An existing CT is not adopted and a contract that still belongs to another instance is not reused."
|
||||
},
|
||||
{
|
||||
"title": "Prepare every image",
|
||||
"body": "All images are resolved, downloaded and verified before the first container is created."
|
||||
},
|
||||
{
|
||||
"title": "Create each rootfs",
|
||||
"body": "The official OCI metadata is imported, and the ProxMenux instance identity and the member role are added."
|
||||
},
|
||||
{
|
||||
"title": "Private network",
|
||||
"body": "A bridge and subnet are created and each member gets its fixed address; only the main member also gets the LAN interface."
|
||||
},
|
||||
{
|
||||
"title": "Persistence",
|
||||
"body": "Each database and configuration gets its own container disk; only data meant to be shared uses host directories."
|
||||
},
|
||||
{
|
||||
"title": "Environment",
|
||||
"body": "Internal endpoints, shared secrets and service variables are written. The application reaches its dependencies at their reserved private addresses."
|
||||
},
|
||||
{
|
||||
"title": "Record",
|
||||
"body": "Each member records its native configuration, the rootfs changes that updates have to reproduce and its relation to the stack."
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "checks",
|
||||
"title": "3. Start and check each container",
|
||||
"intro": "A created LXC does not mean a ready service. Dependencies start in order and each one passes a check specific to its service.",
|
||||
"blocks": [
|
||||
{
|
||||
"table": {
|
||||
"headers": [
|
||||
"Service",
|
||||
"Check",
|
||||
"What it shows"
|
||||
],
|
||||
"rows": [
|
||||
[
|
||||
"PostgreSQL",
|
||||
"<code>pg_isready</code> in the CT with the expected host, user and database",
|
||||
"The server accepts connections for the configured database"
|
||||
],
|
||||
[
|
||||
"Redis / Valkey",
|
||||
"<code>redis-cli</code> or <code>valkey-cli</code> <code>PING</code> against its private address",
|
||||
"The broker listens and answers"
|
||||
],
|
||||
[
|
||||
"Immich machine learning",
|
||||
"HTTP <code>GET /ping</code>, plus a check of the selected GPU runtime",
|
||||
"The service answers and the requested acceleration has not fallen back to CPU"
|
||||
],
|
||||
[
|
||||
"Nextcloud",
|
||||
"<code>GET /status.php</code> with <code>installed=true</code>, <code>maintenance=false</code> and <code>needsDbUpgrade=false</code>",
|
||||
"Initialisation finished with no pending migration"
|
||||
],
|
||||
[
|
||||
"Paperless-ngx / Tandoor",
|
||||
"HTTP on the LAN address and real port of the service",
|
||||
"The frontend and its dependencies serve the application"
|
||||
],
|
||||
[
|
||||
"Main application",
|
||||
"The endpoint of its template, for example <code>/api/server/ping</code> in Immich",
|
||||
"The whole stack works through the application that uses the dependencies"
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"calloutWarning": {
|
||||
"title": "running is not healthy",
|
||||
"body": "The running state only says that the LXC process exists. Where the service offers a better check, an exec or HTTP check with a timeout is used. If a member stops or fails its check, the stack is not declared installed."
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "hook",
|
||||
"title": "4. The dependency hook",
|
||||
"blocks": [
|
||||
{
|
||||
"p": "The hook is set only on the main container of a dependent stack. Proxmox VE keeps the script as a snippet and runs it as the <code>hookscript</code> of that CT. The stack recipe is not written in the script: it lives in a separate private contract."
|
||||
},
|
||||
{
|
||||
"codeGrid": {
|
||||
"items": [
|
||||
{
|
||||
"title": "Main CT configuration",
|
||||
"code": "hookscript: local:snippets/proxmenux-stack-dependencies.sh"
|
||||
},
|
||||
{
|
||||
"title": "Private contract (example)",
|
||||
"code": "/etc/pve/priv/proxmenux-stack-VMID.json\n{\n \"schema\": 1,\n \"stack\": \"immich\",\n \"dependencies\": [\n {\"vmid\": 107, \"label\": \"PostgreSQL\", \"healthcheck\": {...}},\n {\"vmid\": 108, \"label\": \"Valkey\", \"healthcheck\": {...}},\n {\"vmid\": 106, \"label\": \"Machine Learning\", \"healthcheck\": {...}}\n ]\n}"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"snippet": {
|
||||
"summary": "Complete source of proxmenux-stack-dependencies.sh",
|
||||
"pathCode": "local:snippets/proxmenux-stack-dependencies.sh",
|
||||
"snippetCode": "stackDependencyHook"
|
||||
}
|
||||
},
|
||||
{
|
||||
"p": "The script is the same for every stack. VMIDs, names, check types and timeouts come from the private contract <code>/etc/pve/priv/proxmenux-stack-VMID.json</code> of each stack."
|
||||
},
|
||||
{
|
||||
"steps": {
|
||||
"items": [
|
||||
{
|
||||
"title": "Proxmox VE calls pre-start",
|
||||
"body": "Before the main CT starts, the hook runs with its VMID and the lifecycle phase."
|
||||
},
|
||||
{
|
||||
"title": "A lock per stack",
|
||||
"body": "<code>flock</code> on <code>/run/lock/proxmenux-stack-VMID.lock</code> prevents two start sequences at the same time."
|
||||
},
|
||||
{
|
||||
"title": "The contract is read and validated",
|
||||
"body": "A known schema, numeric dependencies, labels, an exec, http or running check and a positive timeout are required."
|
||||
},
|
||||
{
|
||||
"title": "Dependencies in order",
|
||||
"body": "Each CT must exist; a stopped one is started and one already running is not restarted."
|
||||
},
|
||||
{
|
||||
"title": "Wait for health",
|
||||
"body": "The check runs every two seconds, and the CT is also confirmed to be still running."
|
||||
},
|
||||
{
|
||||
"title": "The main CT starts",
|
||||
"body": "When every dependency is ready, pre-start ends and Proxmox VE starts the application."
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"calloutInfo": {
|
||||
"title": "The hook does not stop dependencies",
|
||||
"body": "The post-start, pre-stop and post-stop phases do nothing. Stopping the main CT leaves PostgreSQL, Redis, Valkey or machine learning running. The hook orders the start; it does not turn several LXCs into one process."
|
||||
}
|
||||
},
|
||||
{
|
||||
"table": {
|
||||
"headers": [
|
||||
"Type",
|
||||
"Example",
|
||||
"Later starts"
|
||||
],
|
||||
"rows": [
|
||||
[
|
||||
"Dependent stack",
|
||||
"Immich, Nextcloud, application with PostgreSQL",
|
||||
"The hook of the main CT starts the dependencies and waits for them"
|
||||
],
|
||||
[
|
||||
"Application suite",
|
||||
"Arr suite",
|
||||
"No main member: each LXC follows its own <code>onboot</code>"
|
||||
],
|
||||
[
|
||||
"Single application",
|
||||
"Jellyfin",
|
||||
"Proxmox VE starts that LXC directly"
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "stack-checks",
|
||||
"title": "5. Checks on the stack as a whole",
|
||||
"blocks": [
|
||||
{
|
||||
"list": {
|
||||
"items": [
|
||||
"Every VMID exists, is unique and keeps the expected instance identity.",
|
||||
"Every contract belongs to the same stack, keeps its role and its recorded native configuration.",
|
||||
"The main member is last in <code>start_order</code> and first in <code>stop_order</code>.",
|
||||
"No member, volume or rootfs adaptation is missing.",
|
||||
"The hook points to the official snippet, its content is unchanged and its contract matches the stack recipe.",
|
||||
"The observed images and digests match the prepared OCI archives.",
|
||||
"Devices, GPU profiles, mounts, secrets and endpoints still match the contracts.",
|
||||
"After every dependency passes, the endpoint of the main application checks the integration between members."
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "manage",
|
||||
"title": "Managing an installed stack",
|
||||
"intro": "In <strong>Manage installed OCI applications</strong>, any member leads to the whole stack. The menu of a stack offers <strong>Update every container of the application</strong> and <strong>Remove: delete the application and its containers</strong>.",
|
||||
"blocks": [
|
||||
{
|
||||
"steps": {
|
||||
"items": [
|
||||
{
|
||||
"title": "Any member",
|
||||
"body": "The contract of the member names the main VMID and the complete list of members."
|
||||
},
|
||||
{
|
||||
"title": "The stack is reproducible",
|
||||
"body": "Identities, contracts, hook, adaptations and a coordinated replay for that recipe are validated."
|
||||
},
|
||||
{
|
||||
"title": "Images first",
|
||||
"body": "The application is not stopped until every digest has been resolved, downloaded and verified."
|
||||
},
|
||||
{
|
||||
"title": "Stop and back up the set",
|
||||
"body": "Verified native backups are taken with the stack stopped, so application and databases belong to the same moment."
|
||||
},
|
||||
{
|
||||
"title": "Update and check each member",
|
||||
"body": "Adaptation, network, mounts, secrets and devices are applied again before the health check of each member."
|
||||
},
|
||||
{
|
||||
"title": "Publish or recover everything",
|
||||
"body": "The new contracts are published only when every member passes. If one fails, every member is restored."
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"calloutWarning": {
|
||||
"title": "A stack without a coordinated replay is not updated",
|
||||
"body": "If the preparation of a stack cannot be reproduced, the update is refused before the stack is stopped. The application keeps running as it is."
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "failure",
|
||||
"title": "6. When something fails",
|
||||
"blocks": [
|
||||
{
|
||||
"p": "During an installation, an error stops and removes the incomplete containers that operation created, and the private bridge if the operation created it. A stack is not published as valid until the whole sequence finishes."
|
||||
},
|
||||
{
|
||||
"p": "An update is transactional: all images are prepared first, then the stack stops, each member is backed up and the backup verified, and only then is each rootfs replaced. Members start one by one with their health check; if one fails, every member is restored from the same set of backups, so the database and the application never belong to different moments."
|
||||
},
|
||||
{
|
||||
"mermaid": {
|
||||
"chartCode": "flowchart LR\n P[\"{{prepare}}\"] --> S[\"{{stop}}\"]\n S --> B[\"{{backup}}\"]\n B --> R[\"{{replace}}\"]\n R --> H{\"{{healthy}}\"}\n H -- \"{{yes}}\" --> C[\"{{commit}}\"]\n H -- \"{{no}}\" --> X[\"{{rollback}}\"]",
|
||||
"labels": {
|
||||
"prepare": "Prepare every image",
|
||||
"stop": "Stop, main member first",
|
||||
"backup": "Verified backup of every CT",
|
||||
"replace": "Replace rootfs",
|
||||
"healthy": "All healthy?",
|
||||
"yes": "Yes",
|
||||
"no": "No",
|
||||
"commit": "Publish contracts",
|
||||
"rollback": "Restore every member"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "not-assumed",
|
||||
"title": "What a stack does not include",
|
||||
"blocks": [
|
||||
{
|
||||
"list": {
|
||||
"items": [
|
||||
"A suite installed in one flow, such as the Arr suite, is not a dependent stack: its containers have no start order between them.",
|
||||
"The private network does not replace authentication, TLS or the configuration of each application.",
|
||||
"The backup of one LXC does not contain the bridge, the hook or the contracts of the rest of the stack.",
|
||||
"Prowlarr, Sonarr or Radarr receive no indexers, profiles or providers from ProxMenux."
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
{
|
||||
"meta": {
|
||||
"title": "Data, paths and networking | ProxMenux",
|
||||
"description": "How OCI manager Apps keeps the data of an OCI container: container disks, host directories, Rclone mounts, addresses and private networks."
|
||||
},
|
||||
"header": {
|
||||
"title": "Data, paths and networking",
|
||||
"description": "What lives in the rootfs, what survives its replacement, how data is shared between containers and how each container gets its address.",
|
||||
"section": "OCI manager Apps"
|
||||
},
|
||||
"sections": [
|
||||
{
|
||||
"id": "intro",
|
||||
"blocks": [
|
||||
{
|
||||
"calloutInfo": {
|
||||
"title": "Persistence is decided before the LXC exists",
|
||||
"body": "The volumes published by the image and by its Compose file are read before the container is created. Every path that has to survive an update becomes a mount point independent of the rootfs, so the rootfs only holds what belongs to the image and can be replaced."
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "questions",
|
||||
"title": "What the installer asks",
|
||||
"intro": "The template supplies the paths the application needs. Each one is placed on a container disk or on a host directory, and more paths can be added before the summary.",
|
||||
"blocks": [
|
||||
{
|
||||
"steps": {
|
||||
"items": [
|
||||
{ "title": "Required paths", "body": "<code>/config</code>, <code>/data</code>, libraries, downloads and every volume the application declares are listed." },
|
||||
{ "title": "Location", "body": "Each path is placed on a disk of the container or on an existing host directory." },
|
||||
{ "title": "Storage and size", "body": "A container disk is created on a Proxmox VE storage, <code>local-lvm</code> by default, with the size given. It appears as <code>vm-VMID-disk-N</code> and is attached as <code>mpN</code>." },
|
||||
{ "title": "Host directory", "body": "A host directory is given by its path. A directory that does not exist is created, owned by the user the container maps." },
|
||||
{ "title": "Additional paths", "body": "More pairs of host path or volume and container path can be added before installing." },
|
||||
{ "title": "Summary", "body": "The complete mapping is shown before the CT is created and is stored in its instance contract." }
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "options",
|
||||
"title": "The two persistent locations",
|
||||
"blocks": [
|
||||
{
|
||||
"table": {
|
||||
"headers": ["Property", "Container disk", "Host directory"],
|
||||
"rows": [
|
||||
["In the configuration", "<code>mpN: STORAGE:vm-VMID-disk-N,mp=/config,backup=1,size=16G</code>", "<code>mpN: /mnt/oci-shared/media,mp=/data/media</code>"],
|
||||
["Container backup (vzdump)", "Included, with <code>backup=1</code>", "Not included"],
|
||||
["Size", "Fixed; grown with a resize of the mount point", "The free space of the host filesystem or dataset"],
|
||||
["Other containers", "Mounted only by its own container", "The same directory can be mounted in several containers"],
|
||||
["Snapshots and restore", "Managed by Proxmox VE together with the CT", "Managed on the host storage"],
|
||||
["Removing the application", "Deleted with the container", "Kept, with its content"],
|
||||
["Moving the CT to another node", "Moves with the CT", "The same path has to exist on the other node"]
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"p": "The rootfs is reserved for the binaries and the content of the image. An update or a recreation replaces it without touching either kind of mount point."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "example",
|
||||
"title": "Example: a container with both locations",
|
||||
"blocks": [
|
||||
{
|
||||
"code": {
|
||||
"title": "Jellyfin installed as CT 151 on local-lvm (excerpt)",
|
||||
"code": "rootfs: local-lvm:vm-151-disk-0,size=8G\n# Container disk, part of the CT backup\nmp0: local-lvm:vm-151-disk-1,mp=/config,backup=1,size=16G\n\n# Host directory, outside the CT backup\nmp1: /mnt/oci-shared/media,mp=/data/media"
|
||||
}
|
||||
},
|
||||
{
|
||||
"p": "An update or a recreation replaces only the rootfs: <code>mp0</code> keeps users, libraries and settings, and <code>mp1</code> keeps showing the same media. Restoring the CT backup brings back <code>/config</code>; the media directory is restored, if needed, from the backup of the host storage."
|
||||
},
|
||||
{
|
||||
"flow": {
|
||||
"nodes": [
|
||||
{ "label": "Contract", "detail": "/config" },
|
||||
{ "label": "Location", "detail": "container disk\nor host directory" },
|
||||
{ "label": "LXC", "detail": "always /config\nfor the application" }
|
||||
],
|
||||
"caption": "The application sees the path the image publishes; only where it is stored changes."
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "shared",
|
||||
"title": "One host directory, several containers",
|
||||
"blocks": [
|
||||
{
|
||||
"mermaid": {
|
||||
"chartCode": "flowchart TB\n H[\"{{host}}<br/>/mnt/oci-shared/media\"]\n H --> Q[\"qBittorrent<br/>/data\"]\n H --> J[\"Jellyfin<br/>/data\"]\n H --> R[\"Radarr / Sonarr<br/>/data\"]\n Q -. \"{{config}}\" .-> QV[(\"/config mpN\")]\n J -. \"{{config}}\" .-> JV[(\"/config mpN\")]\n R -. \"{{config}}\" .-> RV[(\"/config mpN\")]",
|
||||
"labels": { "host": "Host directory", "config": "own configuration" }
|
||||
}
|
||||
},
|
||||
{
|
||||
"p": "Each container keeps its configuration on its own disk. The library or the downloads are one host directory mounted at the same internal path in every container, so a path that one application writes is the same path another one reads."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "rclone",
|
||||
"title": "Cloud storage through the Rclone application",
|
||||
"intro": "The Rclone application of the catalog offers, besides its installation, <strong>Enable a mount on an existing Rclone OCI container</strong>. It mounts a remote already created and authorised in the Rclone web UI and publishes it on the host, where other containers can use it as a host directory.",
|
||||
"blocks": [
|
||||
{
|
||||
"steps": {
|
||||
"items": [
|
||||
{ "title": "Container and remote", "body": "The VMID of the Rclone container, the exact name of the remote and, optionally, a path inside it." },
|
||||
{ "title": "Mount name and cache", "body": "The name of the mount and the VFS cache mode: <code>off</code>, <code>minimal</code>, <code>writes</code> or <code>full</code> (default)." },
|
||||
{ "title": "Published views", "body": "A common root, <code>/mnt/oci-shared</code> by default, holds a read/write view in <code>/mnt/oci-shared/remotes/NAME</code> and a read-only view in <code>/mnt/oci-shared/remotes-ro/NAME</code>." },
|
||||
{ "title": "Activation", "body": "After a confirmation, the CT is stopped, its start command and a Proxmox VE hookscript are set, and it is started again. The operation waits until both views are mounted on the host." }
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"calloutInfo": {
|
||||
"title": "If the mount does not come up",
|
||||
"body": "The previous configuration of the container is restored and it is started again, so a failed activation leaves Rclone as it was."
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "network",
|
||||
"title": "Addresses and networks",
|
||||
"intro": "A single application needs an address. A multi-container application also needs a stable network between its members.",
|
||||
"blocks": [
|
||||
{
|
||||
"cards": {
|
||||
"items": [
|
||||
{ "icon": "network", "title": "Single application", "body": "Bridge and DHCP or a fixed CIDR address are chosen. The LXC has an address of its own and the summary shows the complete URLs of the services." },
|
||||
{ "icon": "waypoints", "title": "Multi-container application", "body": "A free subnet is found, a persistent private bridge is created and each member (application, database, cache) gets a fixed address on it." }
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"flow": {
|
||||
"nodes": [
|
||||
{ "label": "LAN", "detail": "reachable address\nmain service only" },
|
||||
{ "label": "Main LXC", "detail": "web / API\nLAN + private network" },
|
||||
{ "label": "Private network", "detail": "PostgreSQL · Valkey · ML\nfixed addresses" }
|
||||
],
|
||||
"caption": "Dependencies talk over the private network and have no address on the LAN."
|
||||
}
|
||||
},
|
||||
{
|
||||
"p": "The stack contract stores bridge, subnet, addresses and the relations between services. Updating or recreating a member reuses the same topology. ProxMenux Monitor opens the main container at its LAN address, not at its address on the private network."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "ownership",
|
||||
"title": "Ownership",
|
||||
"blocks": [
|
||||
{
|
||||
"list": {
|
||||
"items": [
|
||||
"New directories are created with the UID and GID the unprivileged LXC maps.",
|
||||
"Existing directories are not re-owned recursively.",
|
||||
"Sockets, system files and sensitive paths are not offered as generic host directories."
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "backup",
|
||||
"title": "What a container backup contains",
|
||||
"blocks": [
|
||||
{
|
||||
"table": {
|
||||
"headers": ["Element", "In the CT vzdump", "Where it is kept"],
|
||||
"rows": [
|
||||
["OCI rootfs", "Yes", "The CT backup; it can also be rebuilt from the image and the contract"],
|
||||
["Container disk with <code>backup=1</code>", "Yes", "The CT backup"],
|
||||
["Host directory", "No", "The backup of the host storage"],
|
||||
["Instance contract", "No", "<code>/usr/local/share/proxmenux/oci/instances/VMID/</code> on the host"],
|
||||
["Multi-container application", "Each member in its own backup", "The backups of every member, plus the stack contract and its bridge on the host"]
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -64,7 +64,7 @@
|
||||
"heading": "How it works under the hood",
|
||||
"items": [
|
||||
"Dialog menu lists the 6 codes; you pick one.",
|
||||
"If <code>config.json</code> exists: <code>jq --arg lang \"$new_language\" '.language = $lang'</code> updates the field in place.",
|
||||
"If <code>config.json</code> exists: <code>jq --arg lang \"$new_language\" '.language = $lang''</code> updates the field in place.",
|
||||
"If <code>config.json</code> doesn't exist: a fresh one is created with the language code in a single-field object.",
|
||||
"Confirmation dialog: <em>\"Language changed to [code]\"</em>.",
|
||||
"<code>exec bash config_menu.sh</code> reloads the Settings menu with the new language active."
|
||||
|
||||
Reference in New Issue
Block a user