diff --git a/docs/release-control/v6/internal/subsystems/frontend-primitives.md b/docs/release-control/v6/internal/subsystems/frontend-primitives.md
index 68ffebef1e..ebd8a048c8 100644
--- a/docs/release-control/v6/internal/subsystems/frontend-primitives.md
+++ b/docs/release-control/v6/internal/subsystems/frontend-primitives.md
@@ -828,10 +828,17 @@ not a replacement status card, CTA band, or page-local nested card.
canonical resource types.
Workload page membership must use the canonical `platformScopes` list when
present instead of treating `platformType` as exclusive ownership. A Docker
- or Podman app-container can therefore be visible on both the container
- runtime lens and its owning platform page, such as Proxmox when the runtime
- is detected inside a PVE LXC, while TrueNAS app containers stay scoped to
- TrueNAS even when their runtime metadata uses the shared Docker facet.
+ or Podman app-container can therefore carry both the container runtime lens
+ and its owning platform page in routing/filter context, such as Proxmox
+ when the runtime is detected inside a PVE LXC, while TrueNAS app containers
+ stay scoped to TrueNAS even when their runtime metadata uses the shared
+ Docker facet. That membership overlap is not permission to duplicate the
+ detailed container table into every platform overview: Proxmox Overview
+ keeps the default Workloads peer table to VMs and LXCs, and Docker-in-LXC
+ evidence belongs as LXC drawer detail while `/docker`
+ remains the canonical detailed Docker / Podman container lens. The overview
+ table should not add peer rows, badges, or child rows for Docker containers;
+ those signals compete with VM/LXC state and belong one click down.
Primary navigation uses that same membership model: the Docker route is
the container-runtime lens and may be labelled `Containers` in the shell,
while shared source badges, filters, and runtime management copy continue
diff --git a/docs/release-control/v6/internal/subsystems/performance-and-scalability.md b/docs/release-control/v6/internal/subsystems/performance-and-scalability.md
index c4d5071a45..dd06f4073d 100644
--- a/docs/release-control/v6/internal/subsystems/performance-and-scalability.md
+++ b/docs/release-control/v6/internal/subsystems/performance-and-scalability.md
@@ -124,6 +124,8 @@ regression protection.
102. `frontend-modern/src/useAppRuntimeState.ts`
103. `frontend-modern/src/utils/storageSummaryCache.ts`
104. `frontend-modern/src/components/Workloads/WorkloadsSurface.tsx`
+105. `frontend-modern/src/components/Workloads/nestedWorkloadContext.ts`
+106. `frontend-modern/src/components/Workloads/__tests__/nestedWorkloadContext.test.ts`
## Shared Boundaries
@@ -154,6 +156,17 @@ regression protection.
`frontend-modern/src/hooks/useWorkloads.ts` must consume canonical
`platformScopes`; page surfaces must not rebuild platform membership with
ad hoc source or runtime scans.
+ Page-owned workload-type exclusions are part of that same hot path:
+ `useWorkloadsState` must apply them before dedupe, filter-option
+ derivation, summary statistics, and row rendering so a platform overview
+ that demotes nested workloads does not carry hidden rows through downstream
+ table state.
+ When a platform page still needs a visible signal for those demoted nested
+ workloads, `nestedWorkloadContext.ts` must derive a bounded summary from the
+ same raw workload snapshot before row rendering. That derivation must use
+ canonical workload IDs and ambiguity-safe runtime-host matching; row and
+ drawer components may render the summary they are given, but they must not
+ scan hidden workloads per row or guess ownership from ambiguous VMIDs.
The same `useWorkloads.ts` mapping owns canonical-field fallback for
per-row scalars the workload table reads. `WorkloadGuest.uptime` must
fall back to the canonical `Resource.Uptime` field after the
diff --git a/docs/release-control/v6/internal/subsystems/storage-recovery.md b/docs/release-control/v6/internal/subsystems/storage-recovery.md
index c94edb8689..cebcb27820 100644
--- a/docs/release-control/v6/internal/subsystems/storage-recovery.md
+++ b/docs/release-control/v6/internal/subsystems/storage-recovery.md
@@ -141,6 +141,11 @@ dense, and backed by the canonical guest `LastBackup` synchronization from PVE
storage and PBS evidence; the Proxmox Backups tab is the drilldown for
coverage, restore-point, and source-detail evidence, not the first place users
must visit to learn whether a guest is backed up.
+That overview Backup signal belongs to Proxmox VMs and LXCs. If the embedded
+Workloads table demotes Docker-in-LXC `app-container` rows out of peer
+membership, the backup surface must still receive the page model's Proxmox
+guest inventory for VM/LXC coverage and must not reinterpret Docker runtime
+membership as backup or recovery ownership.
The platform page embedding point may pass read-only Proxmox guest inventory
into that backup surface solely for workload identity correlation; the backup
surface must still source restore evidence from the PVE/PBS backup APIs rather
diff --git a/docs/release-control/v6/internal/subsystems/unified-resources.md b/docs/release-control/v6/internal/subsystems/unified-resources.md
index 01e29d2217..c03fbcc723 100644
--- a/docs/release-control/v6/internal/subsystems/unified-resources.md
+++ b/docs/release-control/v6/internal/subsystems/unified-resources.md
@@ -2282,6 +2282,15 @@ overlap: Docker/Podman resources reported from a Proxmox LXC carry both
`proxmox-pve` and `docker`, but TrueNAS app containers that reuse
`DockerData` for runtime metadata remain scoped to `truenas` and must not be
promoted to Docker-managed action targets.
+That overlap is not default peer-row membership on every platform overview.
+The Docker / Podman runtime lens is the canonical detailed table for Docker
+containers, while the Proxmox Overview workload table stays focused on VMs and
+LXCs. Proxmox may surface Docker-inside-LXC evidence as LXC drawer detail, but
+it must not promote those `app-container` rows into the default Proxmox peer
+workload table or add always-visible child rows that compete with VM/LXC scan
+flow. That nested context must be read-only and keyed by the same canonical
+Proxmox guest identity and Docker host source identity used by the resource
+model; ambiguous runtime-host matches must be omitted rather than guessed.
The same facet bundle now also returns grouped recent-change counts by
canonical change kind, so the detail drawer can surface the distribution of
state transitions, restarts, config updates, and anomalies without
diff --git a/frontend-modern/src/components/Workloads/GuestDrawer.test.tsx b/frontend-modern/src/components/Workloads/GuestDrawer.test.tsx
index fbf079587d..d73224562b 100644
--- a/frontend-modern/src/components/Workloads/GuestDrawer.test.tsx
+++ b/frontend-modern/src/components/Workloads/GuestDrawer.test.tsx
@@ -571,6 +571,38 @@ describe('GuestDrawer', () => {
});
});
+ describe('Nested workload context card', () => {
+ it('shows nested Docker container count, sample rows, and canonical Docker link', () => {
+ render(() => (
+
+ ));
+
+ const card = screen.getByTestId('nested-workload-context-card');
+ expect(within(card).getByText('Nested Docker')).toBeInTheDocument();
+ expect(within(card).getByText('Containers')).toBeInTheDocument();
+ expect(within(card).getByText('2')).toBeInTheDocument();
+ expect(within(card).getByText('frigate')).toBeInTheDocument();
+ expect(within(card).getByText('mosquitto')).toBeInTheDocument();
+ expect(
+ within(card).getByRole('link', { name: 'Open Docker page for media-lxc' }),
+ ).toHaveAttribute('href', '/docker');
+ });
+ });
+
// ── Agent info ──
describe('Agent info', () => {
diff --git a/frontend-modern/src/components/Workloads/GuestDrawer.tsx b/frontend-modern/src/components/Workloads/GuestDrawer.tsx
index a623f91c41..712e2f0699 100644
--- a/frontend-modern/src/components/Workloads/GuestDrawer.tsx
+++ b/frontend-modern/src/components/Workloads/GuestDrawer.tsx
@@ -146,6 +146,7 @@ export const GuestDrawer: Component = (props) => {
hasOsInfo={hasOsInfo()}
ipAddresses={ipAddresses()}
networkInterfaces={networkInterfaces()}
+ nestedWorkloadContext={props.nestedWorkloadContext}
normalizedTags={normalizedTags()}
onCustomUrlChange={props.onCustomUrlChange}
customUrl={props.customUrl}
diff --git a/frontend-modern/src/components/Workloads/GuestDrawerOverview.tsx b/frontend-modern/src/components/Workloads/GuestDrawerOverview.tsx
index 2f1a0d63c6..83bf9b6fb6 100644
--- a/frontend-modern/src/components/Workloads/GuestDrawerOverview.tsx
+++ b/frontend-modern/src/components/Workloads/GuestDrawerOverview.tsx
@@ -1,5 +1,6 @@
import { For, Show } from 'solid-js';
import { TerminalSquare } from 'lucide-solid';
+import BoxIcon from 'lucide-solid/icons/box';
import { formatDiscoveryAge } from '@/api/discovery';
import { buildInfrastructureOnboardingPath } from '@/components/Settings/infrastructureWorkspaceModel';
@@ -13,6 +14,7 @@ import type { MetricDisplayThresholds } from '@/utils/metricThresholds';
import { DiskList } from './DiskList';
import { getGuestDrawerMemoryRows, isGuestDrawerVM } from './guestDrawerModel';
+import type { NestedWorkloadContext } from './nestedWorkloadContext';
import {
IN_GUEST_AGENT_INSTALL_ACTION_LABEL,
IN_GUEST_AGENT_INSTALL_TITLE,
@@ -35,6 +37,7 @@ interface GuestDrawerOverviewProps {
showInGuestAgentInstallCue: boolean;
ipAddresses: string[];
networkInterfaces: NonNullable;
+ nestedWorkloadContext?: NestedWorkloadContext;
normalizedTags: string[];
onCustomUrlChange?: GuestDrawerProps['onCustomUrlChange'];
customUrl?: GuestDrawerProps['customUrl'];
@@ -215,6 +218,54 @@ export function GuestDrawerOverview(props: GuestDrawerOverviewProps) {
+
+ {(context) => {
+ const visibleItems = () => context().items.slice(0, 4);
+ const hiddenCount = () => Math.max(0, context().count - visibleItems().length);
+ return (
+
+ );
+ }}
+
+
{/* vSphere placement card: vCenter / Datacenter / Cluster live on
WorkloadGuest.vmware and aren't surfaced by System (Node already
shows the runtime host). Render only when the workload is a
@@ -324,7 +375,10 @@ export function GuestDrawerOverview(props: GuestDrawerOverviewProps) {
{(row) => (