diff --git a/plugins/docker/ingest.py b/plugins/docker/ingest.py index bf6726f..b67a404 100644 --- a/plugins/docker/ingest.py +++ b/plugins/docker/ingest.py @@ -321,3 +321,16 @@ async def persist_host_docker(session, host, snapshots, swarm=None, disk=None) - resource_name=resource, metric_name="is_healthy", value=1.0 if health == "healthy" else 0.0, ) + + # Reap containers that vanished from the host's latest listing. Without this, + # removed one-shot containers (CI job runners, buildkit builders, codex jobs) + # accumulate forever as permanent "stopped" rows and inflate every count. + # The listing is authoritative (event derivation already treats absence as + # removal), so anything not in the newest snapshot no longer exists on the + # host. Mirrors the image/swarm/disk persisters. An empty snapshot legitimately + # means the host has no containers, so the unfiltered delete is correct. + seen_names = {c["name"] for c in latest_containers if c.get("name")} + reap = delete(DockerContainer).where(DockerContainer.host_id == host.id) + if seen_names: + reap = reap.where(DockerContainer.name.notin_(seen_names)) + await session.execute(reap) diff --git a/plugins/docker/templates/docker/widget_resources.html b/plugins/docker/templates/docker/widget_resources.html index 3358bc3..c4605fa 100644 --- a/plugins/docker/templates/docker/widget_resources.html +++ b/plugins/docker/templates/docker/widget_resources.html @@ -1,36 +1,28 @@ -{# docker/widget_resources.html — dashboard widget: CPU + memory bars, busiest containers #} +{# docker/widget_resources.html — dashboard widget: the busiest running containers + by CPU, each with labeled CPU + memory utilisation bars. #} {% if not rows %}