diff --git a/containers/libreportal/backend/routes/docker-info-routes.js b/containers/libreportal/backend/routes/docker-info-routes.js index 5e33880..69a7935 100644 --- a/containers/libreportal/backend/routes/docker-info-routes.js +++ b/containers/libreportal/backend/routes/docker-info-routes.js @@ -21,12 +21,12 @@ // Last N lines of combined stdout/stderr, multiplex-decoded. // // GET /api/system/storage -// `docker system df` — total + reclaimable per category (images, -// containers, build cache). Cached for STORAGE_TTL_MS because this is one -// of the more expensive calls on a busy daemon. Named volumes are omitted: -// LibrePortal apps keep data in bind mounts, so volume accounting is always -// ~empty here — per-app on-disk usage is generated separately (see -// webuiSystemAppStorage / /data/system/app_storage.json). +// `docker system df` — total + reclaimable for the engine overhead worth +// acting on (images, build cache). Cached for STORAGE_TTL_MS because this is +// one of the more expensive calls on a busy daemon. Named volumes and +// container writable layers are omitted: LibrePortal apps keep data in bind +// mounts, so both read ~empty here — per-app on-disk usage is generated +// separately (see webuiSystemAppStorage / /data/system/app_storage.json). // // Mounted at /api/system in routes.js (so paths are /api/system/containers // etc.). Uses the shared docker util (utils/docker.js) which talks to the @@ -336,12 +336,15 @@ router.get('/storage', async (req, res) => { try { const df = await storageCache.get('df', () => dockerRequest('GET', '/system/df')); if (!df) return res.status(500).json({ error: 'no_data' }); - // Roll the verbose response up into headline numbers per category. - // "Reclaimable" across this page reflects exactly what the Reclaim - // button frees: dangling images + the whole build cache. Tagged-but- - // unused images and stopped containers are deliberately NOT counted — - // the safe prune leaves them alone — so the headline number matches the - // button's effect instead of overstating it. + // Roll the verbose response up into headline numbers per category. We + // surface only the engine overhead worth acting on — images and build + // cache — and skip container writable layers: for LibrePortal that's a + // near-zero scratch number (app data lives in bind mounts, shown per-app + // elsewhere) that just confuses the picture. + // "Reclaimable" reflects exactly what the Reclaim button frees: dangling + // images + the whole build cache. Tagged-but-unused images are + // deliberately NOT counted — the safe prune leaves them alone — so the + // headline matches the button's effect instead of overstating it. const isDangling = (im) => { const tags = im.RepoTags || []; return tags.length === 0 || tags.every(t => t.includes('')); @@ -356,14 +359,6 @@ router.get('/storage', async (req, res) => { }, { count: 0, size: 0, shared: 0, reclaimable: 0 } ); - const sumContainers = (df.Containers || []).reduce( - (a, c) => { - a.count++; - a.size += c.SizeRw || 0; - return a; - }, - { count: 0, size: 0, reclaimable: 0 } - ); const sumBuild = (df.BuildCache || []).reduce( (a, b) => { a.count++; @@ -387,13 +382,12 @@ router.get('/storage', async (req, res) => { containers: im.Containers || 0, created: im.Created, })); - const total = sumImages.size + sumContainers.size + sumBuild.size; - const reclaimable = sumImages.reclaimable + sumContainers.reclaimable + sumBuild.reclaimable; + const total = sumImages.size + sumBuild.size; + const reclaimable = sumImages.reclaimable + sumBuild.reclaimable; res.set('Cache-Control', 'no-store'); res.json({ total, reclaimable, images: sumImages, - containers: sumContainers, build_cache: sumBuild, top_images: topImages, updated: new Date().toISOString(), diff --git a/containers/libreportal/frontend/js/components/admin/system-storage-page.js b/containers/libreportal/frontend/js/components/admin/system-storage-page.js index e53aa3f..e9e129d 100644 --- a/containers/libreportal/frontend/js/components/admin/system-storage-page.js +++ b/containers/libreportal/frontend/js/components/admin/system-storage-page.js @@ -7,8 +7,8 @@ // generator). The headline view for LibrePortal, whose data lives in bind // mounts, not named volumes. // - Docker engine `docker system df` — headline total + reclaimable, a -// per-category donut (images / containers / build cache), per-category cards, -// and the top images by size. +// donut (images / build cache), per-category cards, and the top images by +// size. The daemon's own overhead, separate from the per-app data. // // One backend call (GET /api/system/storage), cached server-side for 5s. // A "Reclaim space" button runs the safe prune (build cache + dangling @@ -130,7 +130,7 @@ class SystemStoragePage { Admin · System

Storage

-

On-disk space by app, plus Docker's images, containers, and build cache.

+

On-disk space by app, plus Docker's own engine usage.