Files
maggesandClaude Sonnet 5 e2fefcdf9c
Build Packages / Build .txz packages (push) Failing after 9s
Lint / ShellCheck (push) Failing after 43s
Lint / Validate .plg XML (push) Successful in 10s
Lint / EditorConfig (push) Failing after 6s
Add reproducible build system, native Unraid plugin, and WebUI
- versions.env pins podman, conmon, crun, netavark, aardvark-dns, passt,
  and fuse-overlayfs to verified upstream source checksums; SlackBuild
  recipes, scripts/build-packages.sh, checksums.sh, release.sh, and
  update-versions.sh implement the reproducible pipeline; GitHub Actions
  workflows build in a Slackware container and publish releases without
  committing any binaries.

- plugin/podman.plg installs/updates/removes all eight packages (the
  seven components plus the plugin's own unraid-podman scaffolding
  package) via upgradepkg, using the official Unraid array-event hook
  mechanism (event/disks_mounted, event/stopping) instead of editing
  /boot/config/go. rc.podman and the sbin/ helper scripts implement
  storage creation, config seeding/sync, preflight checks, autostart
  with per-container Safe-Mode, and package verify/update/rollback.

- webui/plugins/podman implements the Dashboard, Containers, Pods,
  Images, Volumes, Networks, Logs, Terminal, Compose, and Settings
  panels against the approved mockup (webui/mockups/prototype.html),
  talking to podman system service exclusively via PodmanClient.php
  (libpod REST API over the Unix socket), with two documented
  exceptions: Terminal's one-shot exec model and Compose's use of the
  podman compose CLI, since libpod has no REST equivalent for either.

- docs/ARCHITECTURE.md and docs/ROADMAP.md record the design decisions
  and honest current status (syntax-checked, unit- and
  integration-tested against fake sockets/servers; not yet run against
  a real Unraid/Podman/Slackware system).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-11 10:51:14 +00:00

950 lines
53 KiB
HTML

<title>unraid-podman — WebUI Mockup</title>
<style>
/* =========================================================================
Design tokens
Grounded in Unraid's real webGUI chrome: near-black masthead, a warm
construction-orange brand accent, dense admin-tool typography via the
OS's own system font stack (Unraid itself ships no custom webfont).
Semantic status colors (good/warn/bad/neutral) are kept separate from
the brand accent, per the container-fleet domain's own vocabulary.
========================================================================= */
:root {
--bg: #eef0f2;
--surface: #ffffff;
--surface-2: #f4f5f7;
--surface-3: #e9ebee;
--border: #dde1e6;
--text: #1c2024;
--text-dim: #5b6572;
--text-faint: #8a94a1;
--accent: #d8541a;
--accent-strong: #b8420f;
--accent-contrast: #fff8f3;
--good: #1a8f4c;
--good-bg: #e4f6ea;
--warn: #9a6b00;
--warn-bg: #fdf1d6;
--bad: #c22b3a;
--bad-bg: #fbe6e8;
--neutral: #5b6572;
--neutral-bg: #e9ebee;
--shadow: 0 1px 2px rgba(20, 22, 26, 0.06), 0 4px 12px rgba(20, 22, 26, 0.05);
--mast-bg: #16181c;
--mast-fg: #d7dae0;
--font-ui: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
--font-mono: ui-monospace, "SF Mono", "Cascadia Code", "Roboto Mono", Consolas, "Liberation Mono", monospace;
}
@media (prefers-color-scheme: dark) {
:root {
--bg: #15171b;
--surface: #1c1f24;
--surface-2: #23262c;
--surface-3: #2b2f36;
--border: #34383f;
--text: #e7e9ec;
--text-dim: #9aa1ab;
--text-faint: #6b7280;
--accent: #ef7f3f;
--accent-strong: #f6975f;
--accent-contrast: #1a1002;
--good: #4cc785;
--good-bg: #123322;
--warn: #e0b23d;
--warn-bg: #3a2e0d;
--bad: #ef6470;
--bad-bg: #3a1519;
--neutral: #9aa1ab;
--neutral-bg: #2b2f36;
--shadow: 0 1px 2px rgba(0, 0, 0, 0.3), 0 8px 24px rgba(0, 0, 0, 0.35);
--mast-bg: #0e0f11;
--mast-fg: #cdd1d7;
}
}
:root[data-theme="dark"] {
--bg: #15171b; --surface: #1c1f24; --surface-2: #23262c; --surface-3: #2b2f36;
--border: #34383f; --text: #e7e9ec; --text-dim: #9aa1ab; --text-faint: #6b7280;
--accent: #ef7f3f; --accent-strong: #f6975f; --accent-contrast: #1a1002;
--good: #4cc785; --good-bg: #123322; --warn: #e0b23d; --warn-bg: #3a2e0d;
--bad: #ef6470; --bad-bg: #3a1519; --neutral: #9aa1ab; --neutral-bg: #2b2f36;
--shadow: 0 1px 2px rgba(0,0,0,.3), 0 8px 24px rgba(0,0,0,.35);
--mast-bg: #0e0f11; --mast-fg: #cdd1d7;
}
:root[data-theme="light"] {
--bg: #eef0f2; --surface: #ffffff; --surface-2: #f4f5f7; --surface-3: #e9ebee;
--border: #dde1e6; --text: #1c2024; --text-dim: #5b6572; --text-faint: #8a94a1;
--accent: #d8541a; --accent-strong: #b8420f; --accent-contrast: #fff8f3;
--good: #1a8f4c; --good-bg: #e4f6ea; --warn: #9a6b00; --warn-bg: #fdf1d6;
--bad: #c22b3a; --bad-bg: #fbe6e8; --neutral: #5b6572; --neutral-bg: #e9ebee;
--shadow: 0 1px 2px rgba(20,22,26,.06), 0 4px 12px rgba(20,22,26,.05);
--mast-bg: #16181c; --mast-fg: #d7dae0;
}
* { box-sizing: border-box; }
body {
margin: 0;
background: var(--bg);
color: var(--text);
font-family: var(--font-ui);
font-size: 14px;
line-height: 1.5;
-webkit-font-smoothing: antialiased;
}
h1, h2, h3 { text-wrap: balance; margin: 0; font-weight: 600; }
::selection { background: var(--accent); color: var(--accent-contrast); }
a { color: inherit; }
button, input, select { font-family: inherit; font-size: inherit; color: inherit; }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 3px; }
.tnum { font-variant-numeric: tabular-nums; }
.mono { font-family: var(--font-mono); }
/* ---- Masthead (real Unraid top bar, non-interactive context) ---- */
.mast {
background: var(--mast-bg);
color: var(--mast-fg);
display: flex;
align-items: center;
gap: 14px;
padding: 0 16px;
height: 46px;
}
.mast .brand { display: flex; align-items: center; gap: 8px; font-weight: 700; letter-spacing: .02em; }
.mast .brand .mark {
width: 22px; height: 22px; border-radius: 5px;
background: linear-gradient(155deg, var(--accent), var(--accent-strong));
display: grid; place-items: center; color: #fff; font-size: 11px; font-weight: 800;
}
.mast .server { font-size: 12.5px; color: var(--mast-fg); opacity: .75; }
.mast .spacer { flex: 1; }
.mast .mast-icons { display: flex; gap: 16px; font-size: 13px; opacity: .7; }
.mainnav {
background: color-mix(in srgb, var(--mast-bg) 88%, var(--surface));
display: flex;
gap: 2px;
padding: 0 10px;
overflow-x: auto;
}
.mainnav a {
display: block;
padding: 10px 14px 8px;
font-size: 12px;
letter-spacing: .04em;
text-transform: uppercase;
color: var(--mast-fg);
opacity: .55;
text-decoration: none;
border-bottom: 2px solid transparent;
white-space: nowrap;
cursor: default;
}
.mainnav a.active {
opacity: 1;
color: var(--accent-strong);
border-bottom-color: var(--accent);
font-weight: 700;
}
/* ---- Page header ---- */
.pagehead {
max-width: 1280px;
margin: 0 auto;
padding: 22px 24px 0;
display: flex;
align-items: flex-end;
justify-content: space-between;
gap: 16px;
flex-wrap: wrap;
}
.pagehead .titlewrap { display: flex; align-items: center; gap: 12px; }
.pagehead .icon {
width: 38px; height: 38px; border-radius: 9px;
background: var(--surface-3);
display: grid; place-items: center;
color: var(--accent);
border: 1px solid var(--border);
}
.pagehead h1 { font-size: 21px; }
.pagehead .meta { font-size: 12.5px; color: var(--text-dim); margin-top: 2px; }
.pagehead .meta .dot { color: var(--good); }
.btn {
appearance: none;
border: 1px solid var(--border);
background: var(--surface);
color: var(--text);
padding: 8px 14px;
border-radius: 7px;
font-size: 13px;
font-weight: 600;
cursor: pointer;
display: inline-flex;
align-items: center;
gap: 6px;
transition: border-color .12s, background .12s;
}
.btn:hover { border-color: var(--text-faint); }
.btn-primary {
background: var(--accent);
border-color: var(--accent);
color: var(--accent-contrast);
}
.btn-primary:hover { background: var(--accent-strong); border-color: var(--accent-strong); }
.btn-danger { color: var(--bad); }
.btn-danger:hover { border-color: var(--bad); }
.btn-icon { padding: 6px 8px; }
.btn[disabled] { opacity: .4; cursor: not-allowed; }
/* ---- Sub navigation (the 10 sections) ---- */
.subnav {
max-width: 1280px;
margin: 14px auto 0;
padding: 0 24px;
display: flex;
gap: 4px;
border-bottom: 1px solid var(--border);
overflow-x: auto;
}
.subnav button {
appearance: none;
background: none;
border: none;
border-bottom: 2px solid transparent;
color: var(--text-dim);
padding: 10px 12px;
font-size: 13px;
font-weight: 600;
cursor: pointer;
white-space: nowrap;
display: flex; align-items: center; gap: 6px;
}
.subnav button:hover { color: var(--text); }
.subnav button.active { color: var(--accent-strong); border-bottom-color: var(--accent); }
.subnav button .n {
font-size: 10.5px; padding: 1px 5px; border-radius: 8px;
background: var(--surface-3); color: var(--text-dim);
}
.main { max-width: 1280px; margin: 0 auto; padding: 20px 24px 64px; }
.panel { display: none; animation: fade .15s ease; }
.panel.active { display: block; }
@keyframes fade { from { opacity: 0; transform: translateY(2px); } to { opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .panel { animation: none; } }
/* ---- Generic building blocks ---- */
.card {
background: var(--surface);
border: 1px solid var(--border);
border-radius: 10px;
box-shadow: var(--shadow);
}
.card-pad { padding: 16px 18px; }
.card-head {
display: flex; align-items: center; justify-content: space-between;
padding: 14px 18px; border-bottom: 1px solid var(--border);
}
.card-head h2 { font-size: 14.5px; }
.card-head .sub { font-size: 12px; color: var(--text-faint); font-weight: 500; }
.grid { display: grid; gap: 14px; }
.stat-grid { grid-template-columns: repeat(6, 1fr); }
@media (max-width: 1080px) { .stat-grid { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 620px) { .stat-grid { grid-template-columns: repeat(2, 1fr); } }
.stat {
background: var(--surface);
border: 1px solid var(--border);
border-radius: 10px;
padding: 14px 16px;
box-shadow: var(--shadow);
}
.stat .label { font-size: 11px; text-transform: uppercase; letter-spacing: .05em; color: var(--text-faint); font-weight: 700; }
.stat .value { font-size: 24px; font-weight: 700; margin-top: 6px; }
.stat .value small { font-size: 13px; color: var(--text-dim); font-weight: 600; }
.stat .bar { height: 5px; border-radius: 3px; background: var(--surface-3); margin-top: 10px; overflow: hidden; }
.stat .bar > span { display: block; height: 100%; background: var(--accent); border-radius: 3px; }
.chip {
display: inline-flex; align-items: center; gap: 5px;
padding: 3px 9px; border-radius: 100px;
font-size: 11.5px; font-weight: 700; letter-spacing: .01em;
white-space: nowrap;
}
.chip .d { width: 6px; height: 6px; border-radius: 50%; background: currentColor; flex: none; }
.chip-good { background: var(--good-bg); color: var(--good); }
.chip-warn { background: var(--warn-bg); color: var(--warn); }
.chip-bad { background: var(--bad-bg); color: var(--bad); }
.chip-neutral { background: var(--neutral-bg); color: var(--neutral); }
table { width: 100%; border-collapse: collapse; font-size: 13px; }
thead th {
text-align: left; font-size: 11px; text-transform: uppercase; letter-spacing: .04em;
color: var(--text-faint); font-weight: 700;
padding: 10px 18px; border-bottom: 1px solid var(--border);
}
tbody td { padding: 11px 18px; border-bottom: 1px solid var(--border); vertical-align: middle; }
tbody tr:last-child td { border-bottom: none; }
tbody tr:hover { background: var(--surface-2); }
.table-wrap { overflow-x: auto; }
.row-name { display: flex; align-items: center; gap: 10px; font-weight: 600; }
.row-name .ico {
width: 26px; height: 26px; border-radius: 6px; flex: none;
background: var(--surface-3); display: grid; place-items: center; font-size: 12px;
border: 1px solid var(--border); color: var(--text-dim);
}
.row-sub { font-size: 11.5px; color: var(--text-faint); font-weight: 500; margin-top: 1px; }
.actions { display: flex; gap: 4px; justify-content: flex-end; }
.usage-mini { display: flex; align-items: center; gap: 8px; min-width: 110px; }
.usage-mini .track { flex: 1; height: 5px; border-radius: 3px; background: var(--surface-3); overflow: hidden; }
.usage-mini .track > span { display: block; height: 100%; background: var(--accent); }
.usage-mini .num { font-size: 11.5px; color: var(--text-dim); width: 34px; text-align: right; }
.toolbar { display: flex; align-items: center; gap: 10px; padding: 14px 18px; border-bottom: 1px solid var(--border); flex-wrap: wrap; }
.search {
flex: 1; min-width: 180px;
background: var(--surface-2); border: 1px solid var(--border); border-radius: 7px;
padding: 7px 11px; font-size: 13px; color: var(--text);
}
.search::placeholder { color: var(--text-faint); }
.filterset { display: flex; gap: 4px; background: var(--surface-2); padding: 3px; border-radius: 8px; }
.filterset button {
appearance: none; border: none; background: none; color: var(--text-dim);
font-size: 12px; font-weight: 700; padding: 5px 10px; border-radius: 6px; cursor: pointer;
}
.filterset button.active { background: var(--surface); color: var(--text); box-shadow: var(--shadow); }
.two-col { display: grid; grid-template-columns: 1.3fr 1fr; gap: 14px; align-items: start; }
@media (max-width: 920px) { .two-col { grid-template-columns: 1fr; } }
.activity-list { list-style: none; margin: 0; padding: 6px 0; }
.activity-list li { display: flex; gap: 10px; padding: 9px 18px; border-bottom: 1px solid var(--border); font-size: 12.5px; }
.activity-list li:last-child { border-bottom: none; }
.activity-list .dot { width: 7px; height: 7px; border-radius: 50%; margin-top: 5px; flex: none; }
.activity-list .txt strong { font-weight: 600; }
.activity-list .when { color: var(--text-faint); font-size: 11px; margin-top: 1px; }
.empty-note { padding: 28px 18px; text-align: center; color: var(--text-faint); font-size: 13px; }
/* ---- Pods ---- */
.pod-card { border: 1px solid var(--border); border-radius: 10px; overflow: hidden; margin-bottom: 14px; background: var(--surface); box-shadow: var(--shadow); }
.pod-card:last-child { margin-bottom: 0; }
.pod-head { display: flex; align-items: center; gap: 10px; padding: 13px 16px; background: var(--surface-2); border-bottom: 1px solid var(--border); }
.pod-head .name { font-weight: 700; font-size: 13.5px; }
.pod-head .infra { font-size: 11.5px; color: var(--text-faint); }
.pod-body table { font-size: 12.5px; }
.pod-body td, .pod-body th { padding: 8px 16px 8px 40px; }
/* ---- Logs ---- */
.logs-layout { display: grid; grid-template-columns: 200px 1fr; min-height: 460px; }
@media (max-width: 760px) { .logs-layout { grid-template-columns: 1fr; } }
.logs-side { border-right: 1px solid var(--border); }
.logs-side .item { padding: 10px 14px; font-size: 12.5px; border-bottom: 1px solid var(--border); cursor: default; display: flex; align-items: center; gap: 8px; }
.logs-side .item.active { background: var(--surface-2); font-weight: 700; box-shadow: inset 2px 0 0 var(--accent); }
.log-toolbar { display: flex; gap: 8px; align-items: center; padding: 10px 14px; border-bottom: 1px solid var(--border); }
.log-pane {
background: #0f1114; color: #c7ccd4; font-family: var(--font-mono); font-size: 12.3px;
padding: 14px 16px; height: 400px; overflow-y: auto; line-height: 1.65;
}
:root:not([data-theme="dark"]) .log-pane { background: #14161a; }
.log-pane .l { white-space: pre-wrap; word-break: break-word; }
.log-pane .ts { color: #6b7280; }
.log-pane .lvl-info { color: #6fb2f5; }
.log-pane .lvl-warn { color: #e0b23d; }
.log-pane .lvl-error { color: #ef6470; }
/* ---- Terminal ---- */
.term-wrap { padding: 16px 18px; }
.term-select { display: flex; gap: 8px; margin-bottom: 12px; align-items: center; font-size: 12.5px; color: var(--text-dim); }
.term-select select {
background: var(--surface-2); border: 1px solid var(--border); border-radius: 6px; padding: 6px 9px; color: var(--text);
}
.term {
background: #0f1114; color: #d7dbe0; font-family: var(--font-mono); font-size: 12.6px;
border-radius: 8px; padding: 14px 16px; height: 380px; overflow-y: auto; line-height: 1.7;
}
.term .prompt { color: #4cc785; }
.term .path { color: #6fb2f5; }
.term .cur { display: inline-block; width: 7px; height: 14px; background: #d7dbe0; vertical-align: text-bottom; animation: blink 1s step-end infinite; }
@media (prefers-reduced-motion: reduce) { .term .cur { animation: none; } }
@keyframes blink { 50% { opacity: 0; } }
/* ---- Compose ---- */
.compose-layout { display: grid; grid-template-columns: 230px 1fr; min-height: 480px; }
@media (max-width: 800px) { .compose-layout { grid-template-columns: 1fr; } }
.compose-side { border-right: 1px solid var(--border); }
.compose-proj { padding: 12px 14px; border-bottom: 1px solid var(--border); cursor: default; }
.compose-proj.active { background: var(--surface-2); box-shadow: inset 2px 0 0 var(--accent); }
.compose-proj .name { font-weight: 700; font-size: 13px; display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.compose-proj .path { font-size: 11px; color: var(--text-faint); margin-top: 2px; font-family: var(--font-mono); }
.compose-body-head { display: flex; align-items: center; justify-content: space-between; padding: 12px 16px; border-bottom: 1px solid var(--border); gap: 10px; flex-wrap: wrap; }
.yaml { background: #0f1114; color: #c7ccd4; font-family: var(--font-mono); font-size: 12.4px; padding: 16px 18px; height: 420px; overflow: auto; line-height: 1.7; }
:root:not([data-theme="dark"]) .yaml { background: #14161a; }
.yaml .k { color: #ef9f76; }
.yaml .s { color: #9fd88a; }
.yaml .c { color: #6b7280; font-style: italic; }
.yaml .p { color: #6fb2f5; }
/* ---- Settings ---- */
.settings-grid { display: grid; gap: 14px; }
.field-row { display: grid; grid-template-columns: 220px 1fr; gap: 16px; padding: 14px 18px; border-bottom: 1px solid var(--border); align-items: start; }
.field-row:last-child { border-bottom: none; }
.field-row label { font-weight: 600; font-size: 13px; }
.field-row .hint { font-size: 11.5px; color: var(--text-faint); margin-top: 4px; max-width: 46ch; }
.field-row input[type="text"], .field-row input[type="number"], .field-row select {
background: var(--surface-2); border: 1px solid var(--border); border-radius: 7px;
padding: 8px 10px; font-size: 13px; color: var(--text); width: 100%; max-width: 340px;
}
.field-row .unit { font-size: 12px; color: var(--text-dim); margin-left: 8px; }
.switch { position: relative; display: inline-block; width: 38px; height: 22px; }
.switch input { display: none; }
.switch .track { position: absolute; inset: 0; background: var(--surface-3); border: 1px solid var(--border); border-radius: 20px; transition: .15s; }
.switch .track::after { content: ""; position: absolute; width: 16px; height: 16px; border-radius: 50%; background: var(--surface); top: 2px; left: 2px; transition: .15s; box-shadow: 0 1px 2px rgba(0,0,0,.3); }
.switch input:checked + .track { background: var(--accent); border-color: var(--accent); }
.switch input:checked + .track::after { transform: translateX(16px); background: #fff; }
.danger-card { border-color: color-mix(in srgb, var(--bad) 40%, var(--border)); }
.danger-card .card-head { border-bottom-color: color-mix(in srgb, var(--bad) 30%, var(--border)); }
.danger-card .card-head h2 { color: var(--bad); }
.badge-update { font-size: 10px; font-weight: 700; color: var(--accent-strong); background: color-mix(in srgb, var(--accent) 16%, transparent); padding: 2px 7px; border-radius: 100px; margin-left: 8px; }
footer.note { max-width: 1280px; margin: 0 auto; padding: 4px 24px 40px; font-size: 11.5px; color: var(--text-faint); }
</style>
<!-- =========================================================================
Real Unraid chrome (masthead + main nav) — recreated for context, not
interactive. Only "Podman" is the actual subject of this mockup.
========================================================================= -->
<div class="mast">
<div class="brand"><span class="mark">U</span> unraid</div>
<div class="server">TOWER · 14d 6h uptime · Array Started</div>
<div class="spacer"></div>
<div class="mast-icons"><span>&#9432; Updates</span><span>&#128276;</span><span>&#128100; root</span></div>
</div>
<nav class="mainnav">
<a>Dashboard</a><a>Main</a><a>Shares</a><a>Users</a><a>Settings</a><a>Plugins</a><a>Docker</a><a>VMs</a>
<a class="active">Podman</a><a>Tools</a>
</nav>
<header class="pagehead">
<div class="titlewrap">
<div class="icon" aria-hidden="true">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none"><path d="M12 2l8 4.5v9L12 20l-8-4.5v-9L12 2z" stroke="currentColor" stroke-width="1.6" stroke-linejoin="round"/><path d="M12 11l8-4.5M12 11v9M12 11L4 6.5" stroke="currentColor" stroke-width="1.6" stroke-linejoin="round"/></svg>
</div>
<div>
<h1>Podman</h1>
<div class="meta"><span class="dot">&#9679;</span> podman.sock connected &nbsp;·&nbsp; 5 of 8 containers running &nbsp;·&nbsp; v6.0.1</div>
</div>
</div>
<div style="display:flex; gap:8px;">
<button class="btn">&#8635; Refresh</button>
<button class="btn btn-primary">+ New Container</button>
</div>
</header>
<nav class="subnav" id="subnav">
<button class="active" data-panel="dashboard">Dashboard</button>
<button data-panel="containers">Containers <span class="n">8</span></button>
<button data-panel="pods">Pods <span class="n">2</span></button>
<button data-panel="images">Images <span class="n">14</span></button>
<button data-panel="volumes">Volumes <span class="n">9</span></button>
<button data-panel="networks">Networks <span class="n">3</span></button>
<button data-panel="logs">Logs</button>
<button data-panel="terminal">Terminal</button>
<button data-panel="compose">Compose</button>
<button data-panel="settings">Settings</button>
</nav>
<main class="main">
<!-- ============================= DASHBOARD ============================= -->
<section class="panel active" id="panel-dashboard">
<div class="grid stat-grid">
<div class="stat"><div class="label">Running</div><div class="value tnum">5 <small>/ 8</small></div></div>
<div class="stat"><div class="label">Pods</div><div class="value tnum">2</div></div>
<div class="stat"><div class="label">Images</div><div class="value tnum">14</div></div>
<div class="stat"><div class="label">Volumes</div><div class="value tnum">9</div></div>
<div class="stat"><div class="label">Networks</div><div class="value tnum">3</div></div>
<div class="stat">
<div class="label">Storage Used</div>
<div class="value tnum">62.4 <small>/ 100 GB</small></div>
<div class="bar"><span style="width:62%"></span></div>
</div>
</div>
<div class="two-col" style="margin-top:14px;">
<div class="card">
<div class="card-head"><h2>Resource Usage</h2><span class="sub">last 60 min</span></div>
<div class="card-pad">
<svg viewBox="0 0 560 140" width="100%" height="140" preserveAspectRatio="none" role="img" aria-label="CPU and memory usage over the last 60 minutes">
<defs>
<linearGradient id="cpuFill" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="var(--accent)" stop-opacity="0.35"/>
<stop offset="1" stop-color="var(--accent)" stop-opacity="0"/>
</linearGradient>
</defs>
<g stroke="var(--border)" stroke-width="1">
<line x1="0" y1="10" x2="560" y2="10"/><line x1="0" y1="45" x2="560" y2="45"/>
<line x1="0" y1="80" x2="560" y2="80"/><line x1="0" y1="115" x2="560" y2="115"/>
</g>
<path d="M0,100 L40,92 L80,96 L120,70 L160,74 L200,60 L240,66 L280,50 L320,58 L360,42 L400,48 L440,36 L480,40 L520,30 L560,34 L560,140 L0,140 Z" fill="url(#cpuFill)"/>
<path d="M0,100 L40,92 L80,96 L120,70 L160,74 L200,60 L240,66 L280,50 L320,58 L360,42 L400,48 L440,36 L480,40 L520,30 L560,34" fill="none" stroke="var(--accent)" stroke-width="2" stroke-linejoin="round" stroke-linecap="round"/>
<circle cx="560" cy="34" r="3.5" fill="var(--accent)"/>
<path d="M0,120 L40,118 L80,121 L120,112 L160,116 L200,108 L240,110 L280,104 L320,109 L360,100 L400,103 L440,96 L480,99 L520,92 L560,95" fill="none" stroke="var(--text-faint)" stroke-width="1.6" stroke-linejoin="round" stroke-linecap="round" stroke-dasharray="1 4"/>
<circle cx="560" cy="95" r="3" fill="var(--text-faint)"/>
</svg>
<div style="display:flex; gap:18px; margin-top:6px; font-size:11.5px; color:var(--text-dim);">
<span><span style="color:var(--accent);">&#9679;</span> CPU · 24%</span>
<span><span style="color:var(--text-faint);">&#9679;</span> Memory · 6.1 / 16 GB</span>
</div>
</div>
</div>
<div class="card">
<div class="card-head"><h2>Recent Activity</h2></div>
<ul class="activity-list">
<li><span class="dot" style="background:var(--good)"></span><div class="txt"><div><strong>sonarr</strong> started</div><div class="when">2 min ago</div></div></li>
<li><span class="dot" style="background:var(--bad)"></span><div class="txt"><div><strong>postgres</strong> health check failed</div><div class="when">11 min ago</div></div></li>
<li><span class="dot" style="background:var(--accent)"></span><div class="txt"><div>Pulled <strong>radarr:latest</strong> (image updated)</div><div class="when">38 min ago</div></div></li>
<li><span class="dot" style="background:var(--good)"></span><div class="txt"><div>Autostart chain completed (7/8)</div><div class="when">6h ago · array start</div></div></li>
<li><span class="dot" style="background:var(--warn)"></span><div class="txt"><div><strong>watchtower</strong> paused (autostart Safe-Mode)</div><div class="when">6h ago</div></div></li>
<li><span class="dot" style="background:var(--neutral)"></span><div class="txt"><div>Network <strong>media-net</strong> created</div><div class="when">2d ago</div></div></li>
</ul>
</div>
</div>
<div class="card" style="margin-top:14px;">
<div class="card-head"><h2>Autostart Queue</h2><span class="sub">/boot/config/plugins/podman/autostart</span></div>
<div class="table-wrap">
<table>
<thead><tr><th>#</th><th>Container</th><th>Delay</th><th>Last Result</th></tr></thead>
<tbody>
<tr><td class="tnum">1</td><td>postgres</td><td class="tnum"></td><td><span class="chip chip-good"><span class="d"></span>started</span></td></tr>
<tr><td class="tnum">2</td><td>nextcloud</td><td class="tnum">5s</td><td><span class="chip chip-good"><span class="d"></span>started</span></td></tr>
<tr><td class="tnum">3</td><td>sonarr</td><td class="tnum"></td><td><span class="chip chip-good"><span class="d"></span>started</span></td></tr>
<tr><td class="tnum">4</td><td>radarr</td><td class="tnum"></td><td><span class="chip chip-good"><span class="d"></span>started</span></td></tr>
<tr><td class="tnum">5</td><td>watchtower</td><td class="tnum"></td><td><span class="chip chip-warn"><span class="d"></span>Safe-Mode (3 failures)</span></td></tr>
</tbody>
</table>
</div>
</div>
</section>
<!-- ============================= CONTAINERS ============================= -->
<section class="panel" id="panel-containers">
<div class="card">
<div class="toolbar">
<input class="search" type="text" placeholder="Search containers by name or image…">
<div class="filterset">
<button class="active">All 8</button><button>Running 5</button><button>Stopped 2</button><button>Unhealthy 1</button>
</div>
</div>
<div class="table-wrap">
<table>
<thead><tr><th>Status</th><th>Name</th><th>Image</th><th>CPU</th><th>Memory</th><th>Ports</th><th>Uptime</th><th></th></tr></thead>
<tbody>
<tr>
<td><span class="chip chip-good"><span class="d"></span>running</span></td>
<td><div class="row-name"><span class="ico">Px</span>plex</div></td>
<td class="mono row-sub">lscr.io/linuxserver/plex:latest</td>
<td class="tnum">18%</td>
<td><div class="usage-mini"><div class="track"><span style="width:41%"></span></div><span class="num tnum">1.6G</span></div></td>
<td class="mono row-sub">32400/tcp</td>
<td class="tnum">14d 6h</td>
<td class="actions"><button class="btn btn-icon" title="Restart">&#8635;</button><button class="btn btn-icon" title="Logs">&#128196;</button><button class="btn btn-icon" title="Shell">&#9099;</button><button class="btn btn-icon" title="Stop">&#9632;</button></td>
</tr>
<tr>
<td><span class="chip chip-good"><span class="d"></span>running</span></td>
<td><div class="row-name"><span class="ico">So</span>sonarr</div></td>
<td class="mono row-sub">lscr.io/linuxserver/sonarr:4.0</td>
<td class="tnum">3%</td>
<td><div class="usage-mini"><div class="track"><span style="width:22%"></span></div><span class="num tnum">420M</span></div></td>
<td class="mono row-sub">8989/tcp</td>
<td class="tnum">6h 12m</td>
<td class="actions"><button class="btn btn-icon" title="Restart">&#8635;</button><button class="btn btn-icon" title="Logs">&#128196;</button><button class="btn btn-icon" title="Shell">&#9099;</button><button class="btn btn-icon" title="Stop">&#9632;</button></td>
</tr>
<tr>
<td><span class="chip chip-good"><span class="d"></span>running</span></td>
<td><div class="row-name"><span class="ico">Ra</span>radarr <span class="badge-update">update</span></div></td>
<td class="mono row-sub">lscr.io/linuxserver/radarr:5.2</td>
<td class="tnum">2%</td>
<td><div class="usage-mini"><div class="track"><span style="width:19%"></span></div><span class="num tnum">380M</span></div></td>
<td class="mono row-sub">7878/tcp</td>
<td class="tnum">6h 12m</td>
<td class="actions"><button class="btn btn-icon" title="Restart">&#8635;</button><button class="btn btn-icon" title="Logs">&#128196;</button><button class="btn btn-icon" title="Shell">&#9099;</button><button class="btn btn-icon" title="Stop">&#9632;</button></td>
</tr>
<tr>
<td><span class="chip chip-bad"><span class="d"></span>unhealthy</span></td>
<td><div class="row-name"><span class="ico">Pg</span>postgres</div></td>
<td class="mono row-sub">postgres:16-alpine</td>
<td class="tnum">1%</td>
<td><div class="usage-mini"><div class="track"><span style="width:34%"></span></div><span class="num tnum">210M</span></div></td>
<td class="mono row-sub">5432/tcp</td>
<td class="tnum">2d 1h</td>
<td class="actions"><button class="btn btn-icon" title="Restart">&#8635;</button><button class="btn btn-icon" title="Logs">&#128196;</button><button class="btn btn-icon" title="Shell">&#9099;</button><button class="btn btn-icon" title="Stop">&#9632;</button></td>
</tr>
<tr>
<td><span class="chip chip-good"><span class="d"></span>running</span></td>
<td><div class="row-name"><span class="ico">Nc</span>nextcloud</div></td>
<td class="mono row-sub">nextcloud:29-apache</td>
<td class="tnum">6%</td>
<td><div class="usage-mini"><div class="track"><span style="width:58%"></span></div><span class="num tnum">890M</span></div></td>
<td class="mono row-sub">443/tcp, 80/tcp</td>
<td class="tnum">6h 12m</td>
<td class="actions"><button class="btn btn-icon" title="Restart">&#8635;</button><button class="btn btn-icon" title="Logs">&#128196;</button><button class="btn btn-icon" title="Shell">&#9099;</button><button class="btn btn-icon" title="Stop">&#9632;</button></td>
</tr>
<tr>
<td><span class="chip chip-neutral"><span class="d"></span>exited</span></td>
<td><div class="row-name"><span class="ico">Pr</span>prowlarr</div></td>
<td class="mono row-sub">lscr.io/linuxserver/prowlarr:1</td>
<td class="tnum"></td>
<td class="tnum"></td>
<td class="mono row-sub">9696/tcp</td>
<td class="tnum"></td>
<td class="actions"><button class="btn btn-icon" title="Start">&#9654;</button><button class="btn btn-icon" title="Logs">&#128196;</button><button class="btn btn-icon" title="Remove">&#128465;</button></td>
</tr>
<tr>
<td><span class="chip chip-warn"><span class="d"></span>Safe-Mode</span></td>
<td><div class="row-name"><span class="ico">Wt</span>watchtower</div></td>
<td class="mono row-sub">containrrr/watchtower:latest</td>
<td class="tnum"></td>
<td class="tnum"></td>
<td class="mono row-sub"></td>
<td class="tnum"></td>
<td class="actions"><button class="btn btn-icon" title="Start">&#9654;</button><button class="btn btn-icon" title="Logs">&#128196;</button><button class="btn btn-icon" title="Remove">&#128465;</button></td>
</tr>
<tr>
<td><span class="chip chip-good"><span class="d"></span>running</span></td>
<td><div class="row-name"><span class="ico">Ha</span>homeassistant</div></td>
<td class="mono row-sub">ghcr.io/home-assistant/home-assistant</td>
<td class="tnum">4%</td>
<td><div class="usage-mini"><div class="track"><span style="width:30%"></span></div><span class="num tnum">610M</span></div></td>
<td class="mono row-sub">8123/tcp</td>
<td class="tnum">3d 4h</td>
<td class="actions"><button class="btn btn-icon" title="Restart">&#8635;</button><button class="btn btn-icon" title="Logs">&#128196;</button><button class="btn btn-icon" title="Shell">&#9099;</button><button class="btn btn-icon" title="Stop">&#9632;</button></td>
</tr>
</tbody>
</table>
</div>
</div>
</section>
<!-- ============================= PODS ============================= -->
<section class="panel" id="panel-pods">
<div class="pod-card">
<div class="pod-head">
<span class="chip chip-good"><span class="d"></span>running</span>
<span class="name">media-stack</span>
<span class="infra">infra: localhost/podman-pause · shared network namespace</span>
</div>
<div class="pod-body table-wrap">
<table>
<thead><tr><th>Container</th><th>Image</th><th>Status</th><th>Ports</th><th></th></tr></thead>
<tbody>
<tr><td>sonarr</td><td class="mono row-sub">linuxserver/sonarr:4.0</td><td><span class="chip chip-good"><span class="d"></span>running</span></td><td class="mono row-sub">8989/tcp</td><td class="actions"><button class="btn btn-icon">&#128196;</button></td></tr>
<tr><td>radarr</td><td class="mono row-sub">linuxserver/radarr:5.2</td><td><span class="chip chip-good"><span class="d"></span>running</span></td><td class="mono row-sub">7878/tcp</td><td class="actions"><button class="btn btn-icon">&#128196;</button></td></tr>
<tr><td>prowlarr</td><td class="mono row-sub">linuxserver/prowlarr:1</td><td><span class="chip chip-neutral"><span class="d"></span>exited</span></td><td class="mono row-sub">9696/tcp</td><td class="actions"><button class="btn btn-icon">&#128196;</button></td></tr>
</tbody>
</table>
</div>
</div>
<div class="pod-card">
<div class="pod-head">
<span class="chip chip-good"><span class="d"></span>running</span>
<span class="name">home-stack</span>
<span class="infra">infra: localhost/podman-pause · shared network namespace</span>
</div>
<div class="pod-body table-wrap">
<table>
<thead><tr><th>Container</th><th>Image</th><th>Status</th><th>Ports</th><th></th></tr></thead>
<tbody>
<tr><td>homeassistant</td><td class="mono row-sub">ghcr.io/home-assistant/home-assistant</td><td><span class="chip chip-good"><span class="d"></span>running</span></td><td class="mono row-sub">8123/tcp</td><td class="actions"><button class="btn btn-icon">&#128196;</button></td></tr>
<tr><td>mosquitto</td><td class="mono row-sub">eclipse-mosquitto:2</td><td><span class="chip chip-good"><span class="d"></span>running</span></td><td class="mono row-sub">1883/tcp</td><td class="actions"><button class="btn btn-icon">&#128196;</button></td></tr>
</tbody>
</table>
</div>
</div>
</section>
<!-- ============================= IMAGES ============================= -->
<section class="panel" id="panel-images">
<div class="card">
<div class="toolbar">
<input class="search" type="text" placeholder="Search images…">
<button class="btn">&#11015; Pull Image</button>
<button class="btn">Prune Unused</button>
</div>
<div class="table-wrap">
<table>
<thead><tr><th>Repository</th><th>Tag</th><th>Image ID</th><th>Size</th><th>Created</th><th>Used By</th><th></th></tr></thead>
<tbody>
<tr><td>lscr.io/linuxserver/plex</td><td class="mono">latest</td><td class="mono row-sub">a3f9c2e1b7d4</td><td class="tnum">1.42 GB</td><td class="tnum">3d ago</td><td class="tnum">1</td><td class="actions"><button class="btn btn-icon">&#128465;</button></td></tr>
<tr><td>lscr.io/linuxserver/sonarr</td><td class="mono">4.0</td><td class="mono row-sub">7e21ac9f0b12</td><td class="tnum">612 MB</td><td class="tnum">6h ago</td><td class="tnum">1</td><td class="actions"><button class="btn btn-icon">&#128465;</button></td></tr>
<tr><td>lscr.io/linuxserver/radarr <span class="badge-update">update</span></td><td class="mono">5.2</td><td class="mono row-sub">44b6f1d8ce90</td><td class="tnum">598 MB</td><td class="tnum">38m ago</td><td class="tnum">1</td><td class="actions"><button class="btn btn-icon">&#128465;</button></td></tr>
<tr><td>postgres</td><td class="mono">16-alpine</td><td class="mono row-sub">c1a08e4477fd</td><td class="tnum">243 MB</td><td class="tnum">9d ago</td><td class="tnum">1</td><td class="actions"><button class="btn btn-icon">&#128465;</button></td></tr>
<tr><td>nextcloud</td><td class="mono">29-apache</td><td class="mono row-sub">9d3c72b1a5e6</td><td class="tnum">890 MB</td><td class="tnum">12d ago</td><td class="tnum">1</td><td class="actions"><button class="btn btn-icon">&#128465;</button></td></tr>
<tr><td>containrrr/watchtower</td><td class="mono">latest</td><td class="mono row-sub">0f5b9a2d7c31</td><td class="tnum">28 MB</td><td class="tnum">40d ago</td><td class="tnum">1</td><td class="actions"><button class="btn btn-icon">&#128465;</button></td></tr>
<tr><td>ghcr.io/home-assistant/home-assistant</td><td class="mono">2026.7</td><td class="mono row-sub">b7e4f0a19c22</td><td class="tnum">1.1 GB</td><td class="tnum">3d ago</td><td class="tnum">1</td><td class="actions"><button class="btn btn-icon">&#128465;</button></td></tr>
<tr><td>eclipse-mosquitto</td><td class="mono">2</td><td class="mono row-sub">2a6d0e5f8b41</td><td class="tnum">14 MB</td><td class="tnum">40d ago</td><td class="tnum">1</td><td class="actions"><button class="btn btn-icon">&#128465;</button></td></tr>
<tr><td>lscr.io/linuxserver/prowlarr</td><td class="mono">1</td><td class="mono row-sub">d8f231b9a706</td><td class="tnum">340 MB</td><td class="tnum">11d ago</td><td class="tnum">0</td><td class="actions"><button class="btn btn-icon">&#128465;</button></td></tr>
</tbody>
</table>
</div>
</div>
</section>
<!-- ============================= VOLUMES ============================= -->
<section class="panel" id="panel-volumes">
<div class="card">
<div class="toolbar">
<input class="search" type="text" placeholder="Search volumes…">
<button class="btn btn-primary">+ New Volume</button>
</div>
<div class="table-wrap">
<table>
<thead><tr><th>Name</th><th>Driver</th><th>Mountpoint</th><th>Size</th><th>Used By</th><th></th></tr></thead>
<tbody>
<tr><td>postgres-data</td><td class="chip chip-neutral">local</td><td class="mono row-sub">…/storage/volumes/postgres-data/_data</td><td class="tnum">1.8 GB</td><td class="tnum">1</td><td class="actions"><button class="btn btn-icon">&#128193;</button><button class="btn btn-icon">&#128465;</button></td></tr>
<tr><td>nextcloud-data</td><td class="chip chip-neutral">local</td><td class="mono row-sub">…/storage/volumes/nextcloud-data/_data</td><td class="tnum">14.2 GB</td><td class="tnum">1</td><td class="actions"><button class="btn btn-icon">&#128193;</button><button class="btn btn-icon">&#128465;</button></td></tr>
<tr><td>ha-config</td><td class="chip chip-neutral">local</td><td class="mono row-sub">…/storage/volumes/ha-config/_data</td><td class="tnum">340 MB</td><td class="tnum">1</td><td class="actions"><button class="btn btn-icon">&#128193;</button><button class="btn btn-icon">&#128465;</button></td></tr>
<tr><td>mosquitto-data</td><td class="chip chip-neutral">local</td><td class="mono row-sub">…/storage/volumes/mosquitto-data/_data</td><td class="tnum">4 MB</td><td class="tnum">1</td><td class="actions"><button class="btn btn-icon">&#128193;</button><button class="btn btn-icon">&#128465;</button></td></tr>
<tr><td>unused-cache</td><td class="chip chip-neutral">local</td><td class="mono row-sub">…/storage/volumes/unused-cache/_data</td><td class="tnum">610 MB</td><td class="tnum">0</td><td class="actions"><button class="btn btn-icon">&#128193;</button><button class="btn btn-icon">&#128465;</button></td></tr>
</tbody>
</table>
</div>
<div class="empty-note" style="border-top:1px solid var(--border); text-align:left; padding:12px 18px;">
App data mapped from Unraid shares (e.g. <span class="mono">/mnt/user/appdata/…</span>) is managed as bind mounts per-container, not listed here — see each container's edit dialog.
</div>
</div>
</section>
<!-- ============================= NETWORKS ============================= -->
<section class="panel" id="panel-networks">
<div class="card">
<div class="toolbar">
<input class="search" type="text" placeholder="Search networks…">
<button class="btn btn-primary">+ New Network</button>
</div>
<div class="table-wrap">
<table>
<thead><tr><th>Name</th><th>Driver</th><th>Subnet</th><th>Gateway</th><th>Containers</th><th></th></tr></thead>
<tbody>
<tr><td><strong>podman0</strong> <span class="row-sub">(default)</span></td><td class="chip chip-neutral">bridge</td><td class="mono">10.89.0.0/24</td><td class="mono">10.89.0.1</td><td class="tnum">3</td><td class="actions"><button class="btn btn-icon">&#9998;</button></td></tr>
<tr><td>media-net</td><td class="chip chip-neutral">bridge</td><td class="mono">10.89.1.0/24</td><td class="mono">10.89.1.1</td><td class="tnum">3</td><td class="actions"><button class="btn btn-icon">&#9998;</button><button class="btn btn-icon">&#128465;</button></td></tr>
<tr>
<td>iot-macvlan</td><td class="chip chip-neutral">macvlan</td><td class="mono">192.168.10.0/24</td><td class="mono">192.168.10.1</td><td class="tnum">1</td>
<td class="actions"><button class="btn btn-icon">&#9998;</button><button class="btn btn-icon">&#128465;</button></td>
</tr>
</tbody>
</table>
</div>
<div class="empty-note" style="border-top:1px solid var(--border); text-align:left; padding:12px 18px; display:flex; gap:8px; align-items:flex-start;">
<span class="chip chip-warn" style="margin-top:1px;"><span class="d"></span>note</span>
<span>macvlan networks can trigger a known kernel issue with some NIC drivers on Unraid ("macvlan call trap"). See the docs before attaching containers that need host-network access on the same interface.</span>
</div>
</div>
</section>
<!-- ============================= LOGS ============================= -->
<section class="panel" id="panel-logs">
<div class="card">
<div class="logs-layout">
<div class="logs-side">
<div class="item active">&#9679; postgres</div>
<div class="item">&#9679; plex</div>
<div class="item">&#9679; sonarr</div>
<div class="item">&#9679; radarr</div>
<div class="item">&#9679; nextcloud</div>
<div class="item">&#9679; homeassistant</div>
<div class="item">&#9679; mosquitto</div>
</div>
<div>
<div class="log-toolbar">
<input class="search" type="text" placeholder="Filter log output…" style="max-width:280px;">
<span class="filterset"><button class="active">Follow</button><button>Paused</button></span>
<div class="spacer" style="flex:1;"></div>
<button class="btn btn-icon" title="Download">&#8681;</button>
</div>
<div class="log-pane" role="log">
<div class="l"><span class="ts">2026-07-11T09:58:02Z</span> <span class="lvl-info">INFO</span> database system is ready to accept connections</div>
<div class="l"><span class="ts">2026-07-11T09:58:14Z</span> <span class="lvl-info">INFO</span> checkpoint starting: time</div>
<div class="l"><span class="ts">2026-07-11T10:04:41Z</span> <span class="lvl-warn">WARN</span> could not receive data from client: Connection reset by peer</div>
<div class="l"><span class="ts">2026-07-11T10:07:52Z</span> <span class="lvl-error">ERROR</span> connection to server was lost</div>
<div class="l"><span class="ts">2026-07-11T10:07:53Z</span> <span class="lvl-info">INFO</span> restartpoint starting: time</div>
<div class="l"><span class="ts">2026-07-11T10:07:58Z</span> <span class="lvl-info">INFO</span> database system is ready to accept connections</div>
<div class="l"><span class="ts">2026-07-11T10:11:03Z</span> <span class="lvl-warn">WARN</span> autovacuum: found orphan temp table in database "app"</div>
</div>
</div>
</div>
</div>
</section>
<!-- ============================= TERMINAL ============================= -->
<section class="panel" id="panel-terminal">
<div class="card">
<div class="term-wrap">
<div class="term-select">
Exec into:
<select><option>sonarr</option><option>plex</option><option>postgres</option><option>nextcloud</option></select>
Shell: <select><option>/bin/bash</option><option>/bin/sh</option></select>
<button class="btn" style="margin-left:auto;">New Session</button>
</div>
<div class="term">
<div><span class="prompt">root@sonarr</span>:<span class="path">/config</span>$ podman exec -it sonarr /bin/bash</div>
<div>root@sonarr:/config# ls -la</div>
<div>drwxr-xr-x 6 abc abc 4096 Jul 11 09:12 .</div>
<div>drwxr-xr-x 1 root root 4096 Jun 02 17:03 ..</div>
<div>-rw-r--r-- 1 abc abc 22016 Jul 11 09:58 sonarr.db</div>
<div>drwxr-xr-x 2 abc abc 4096 Jul 11 06:00 logs</div>
<div>&nbsp;</div>
<div>root@sonarr:/config# tail -n 3 logs/sonarr.txt</div>
<div>10:11:02|Info|RssSyncService: Starting RSS Sync</div>
<div>10:11:04|Info|RssSyncService: RSS Sync Completed. Reported 12 releases</div>
<div>10:11:04|Info|DownloadService: Report sent to SABnzbd</div>
<div>&nbsp;</div>
<div><span class="prompt">root@sonarr</span>:<span class="path">/config</span># <span class="cur"></span></div>
</div>
</div>
</div>
</section>
<!-- ============================= COMPOSE ============================= -->
<section class="panel" id="panel-compose">
<div class="card">
<div class="compose-layout">
<div class="compose-side">
<div class="compose-proj active">
<div class="name">media-stack <span class="chip chip-good"><span class="d"></span>up</span></div>
<div class="path">/boot/config/plugins/podman/compose/media-stack</div>
</div>
<div class="compose-proj">
<div class="name">home-stack <span class="chip chip-good"><span class="d"></span>up</span></div>
<div class="path">/boot/config/plugins/podman/compose/home-stack</div>
</div>
<div class="compose-proj">
<div class="name">staging <span class="chip chip-neutral"><span class="d"></span>down</span></div>
<div class="path">/boot/config/plugins/podman/compose/staging</div>
</div>
</div>
<div>
<div class="compose-body-head">
<strong>media-stack / compose.yaml</strong>
<div style="display:flex; gap:8px;">
<button class="btn">&#11015; Pull</button>
<button class="btn">&#9632; Down</button>
<button class="btn btn-primary">&#9654; Up</button>
</div>
</div>
<div class="yaml"><span class="c"># managed via podman kube play — see docs/ARCHITECTURE.md</span>
<span class="k">services</span>:
<span class="k">sonarr</span>:
<span class="k">image</span>: <span class="s">lscr.io/linuxserver/sonarr:4.0</span>
<span class="k">ports</span>:
- <span class="s">"8989:8989"</span>
<span class="k">volumes</span>:
- <span class="p">/mnt/user/appdata/sonarr</span>:<span class="p">/config</span>
- <span class="p">/mnt/user/media</span>:<span class="p">/media</span>
<span class="k">environment</span>:
<span class="k">PUID</span>: <span class="s">"99"</span>
<span class="k">PGID</span>: <span class="s">"100"</span>
<span class="k">restart</span>: <span class="s">unless-stopped</span>
<span class="k">radarr</span>:
<span class="k">image</span>: <span class="s">lscr.io/linuxserver/radarr:5.2</span>
<span class="k">ports</span>:
- <span class="s">"7878:7878"</span>
<span class="k">volumes</span>:
- <span class="p">/mnt/user/appdata/radarr</span>:<span class="p">/config</span>
- <span class="p">/mnt/user/media</span>:<span class="p">/media</span>
<span class="k">restart</span>: <span class="s">unless-stopped</span>
<span class="k">prowlarr</span>:
<span class="k">image</span>: <span class="s">lscr.io/linuxserver/prowlarr:1</span>
<span class="k">ports</span>:
- <span class="s">"9696:9696"</span>
<span class="k">restart</span>: <span class="s">unless-stopped</span></div>
</div>
</div>
</div>
</section>
<!-- ============================= SETTINGS ============================= -->
<section class="panel" id="panel-settings">
<div class="settings-grid">
<div class="card">
<div class="card-head"><h2>Storage</h2></div>
<div class="field-row">
<label>Storage path</label>
<div>
<input type="text" value="/mnt/cache/system/podman" class="mono">
<div class="hint">Cache pool or dedicated disk — never a path under /mnt/user (FUSE). See Architecture docs.</div>
</div>
</div>
<div class="field-row">
<label>podman.img size</label>
<div><input type="number" value="20" style="max-width:100px;"><span class="unit">GB</span>
<div class="bar" style="max-width:340px; margin-top:10px;"><span style="width:62%"></span></div>
<div class="hint">62.4 GB used of 20 GB nominal — image will need to grow soon.</div>
</div>
</div>
</div>
<div class="card">
<div class="card-head"><h2>Networking</h2></div>
<div class="field-row"><label>Default bridge subnet</label><div><input type="text" value="10.89.0.0/24" class="mono"></div></div>
<div class="field-row"><label>DNS backend</label><div><select><option>aardvark-dns (recommended)</option><option>none</option></select></div></div>
</div>
<div class="card">
<div class="card-head"><h2>Autostart &amp; Lifecycle</h2></div>
<div class="field-row"><label>Start podman on array start</label><div><label class="switch"><input type="checkbox" checked><span class="track"></span></label></div></div>
<div class="field-row"><label>Container stop timeout</label><div><input type="number" value="10" style="max-width:100px;"><span class="unit">seconds</span></div></div>
<div class="field-row"><label>Autostart Safe-Mode threshold</label><div><input type="number" value="3" style="max-width:100px;"><span class="unit">consecutive failures</span>
<div class="hint">watchtower is currently paused — see Dashboard.</div></div></div>
</div>
<div class="card">
<div class="card-head"><h2>Updates</h2><span class="sub">unraid-podman 0.1.0</span></div>
<div class="field-row"><label>Update channel</label><div><select><option>Stable</option><option>Pre-release</option></select></div></div>
<div class="field-row"><label>Package versions</label>
<div class="hint mono" style="max-width:none;">
podman 6.0.1 · conmon 2.2.1 · crun 1.28 · netavark 2.0.0 · aardvark-dns 2.0.0 · passt git6ef3d1c · fuse-overlayfs 1.17
</div>
</div>
</div>
<div class="card danger-card">
<div class="card-head"><h2>Danger Zone</h2></div>
<div class="field-row">
<label>Roll back plugin</label>
<div><button class="btn">Restore previous version (0.0.9)</button></div>
</div>
<div class="field-row">
<label>Remove plugin</label>
<div>
<button class="btn btn-danger">Uninstall unraid-podman…</button>
<div class="hint">Your configuration, backups, and container storage are preserved by default. Full data removal requires a separate confirmation.</div>
</div>
</div>
</div>
</div>
</section>
</main>
<footer class="note">Mockup only — static sample data, not connected to a live podman.sock. See webui/README.md.</footer>
<script>
// Tab switching: the only interactive behavior this static mockup needs.
const buttons = document.querySelectorAll('#subnav button');
buttons.forEach(function (btn) {
btn.addEventListener('click', function () {
buttons.forEach(function (b) { b.classList.remove('active'); });
btn.classList.add('active');
document.querySelectorAll('.panel').forEach(function (p) { p.classList.remove('active'); });
const target = document.getElementById('panel-' + btn.dataset.panel);
if (target) target.classList.add('active');
});
});
</script>