{"id":1598,"date":"2026-11-04T02:00:33","date_gmt":"2026-11-04T02:00:33","guid":{"rendered":"https:\/\/virtualserversvps.com\/blog\/?p=1598"},"modified":"2026-11-04T02:00:33","modified_gmt":"2026-11-04T02:00:33","slug":"docker-overlay2-disk-usage-prune-policy","status":"publish","type":"post","link":"https:\/\/virtualserversvps.com\/blog\/docker-overlay2-disk-usage-prune-policy\/","title":{"rendered":"Keeping overlay2 Disk Usage Under Control Without Breaking Running Containers"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Docker&#8217;s overlay2 driver keeps every image layer, every stopped container&#8217;s writable layer and every build cache on the same filesystem as <code>\/var\/lib\/docker<\/code>. On a VPS with a 40 GB disk, a handful of Node builds can fill 30 GB in a week, and the first visible symptom is usually a container that dies at startup or a Postgres that refuses writes. The layout is what makes it confusing: <code>docker system df<\/code> reports 12 GB while <code>du<\/code> shows 22 GB, and neither number includes the 7 GB sitting in orphaned layers.<\/p>\n\n\n<p>Aggressive cleanup is dangerous on a live host. <code>docker system prune -a<\/code> will happily remove an image whose running container has a different tag, and <code>--volumes<\/code> deletes named volumes that no container references \u2014 including the one your database is about to mount on restart. The approach below keeps running containers untouched: work out what is actually reclaimable, prune in narrow steps, then put retention policies in place so the problem does not recur. Disk headroom is the reason <a href=\"https:\/\/virtualserversvps.com\/\">running containers on a dedicated KVM slice<\/a> beats sharing an overcommitted host, since you can see and own the whole filesystem.<\/p>\n<!-- \/wp:post-content -->\n\n<!-- wp:heading {\"level\":2} -->\n<h2 class=\"wp-block-heading\">Reading the real disk number<\/h2>\n<!-- \/wp:heading -->\n\n<p>Start with three different views. <code>docker system df -v<\/code> breaks usage down by image, container and build cache, and includes the shared layer column that explains most of the discrepancy. <code>du -sh \/var\/lib\/docker\/overlay2<\/code> gives the on-disk truth including layers no longer referenced by any image. <code>df -h \/var\/lib\/docker<\/code> tells you whether the block device itself is full, which is a different problem from a full layer store.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:code -->\n<pre class=\"wp-block-code\"><code>docker system df -v | head -40\ndu -sh \/var\/lib\/docker\/overlay2 \/var\/lib\/docker\/volumes\ndf -h \/var\/lib\/docker\nfind \/var\/lib\/docker\/overlay2 -maxdepth 1 -type d -mtime +7 | wc -l<\/code><\/pre>\n<!-- \/wp:code -->\n\n<!-- wp:paragraph -->\n<p>A common misconfiguration is Docker defaulting to the root filesystem when a larger data volume exists. Check <code>docker info | grep 'Docker Root Dir'<\/code>; if it points at <code>\/<\/code> and you have a separate disk mounted elsewhere, moving the data root is the highest-value fix available, and it is a symlink plus a daemon restart, not a reinstall.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading {\"level\":2} -->\n<h2 class=\"wp-block-heading\">Safe prune sequence<\/h2>\n<!-- wp:heading -->\n\n<!-- wp:paragraph -->\n<p>Prune from the least to the most destructive, and check the reported reclaimable figure before confirming. Dangling images are unreferenced first: nothing points to them, so removing them cannot break a running container. Build cache is next and is nearly always safe, though it slows the next build. Stopped containers are third \u2014 those only matter if you plan to restart them.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:list -->\n<ul><li><code>docker image prune<\/code> \u2014 dangling images only, safe<\/li><li><code>docker builder prune --filter until=168h<\/code> \u2014 build cache older than a week<\/li><li><code>docker container prune --filter until=72h<\/code> \u2014 stopped containers aged out<\/li><li><code>docker image prune -a<\/code> \u2014 every image without a container; inspect first<\/li><li><code>docker volume prune<\/code> \u2014 never without <code>docker volume ls<\/code> review<\/li><\/ul>\n<!-- \/wp:list -->\n\n<!-- wp:heading {\"level\":2} -->\n<h2 class=\"wp-block-heading\">Why running containers survive, and which do not<\/h2>\n<!-- wp:paragraph -->\n<p>A running container holds a reference to its image, so <code>docker image prune -a<\/code> will not remove it. It will remove the image a stopped container depends on, because a stopped container is not a reference in the same sense, and <code>docker start<\/code> will then fail with &#8220;no such image&#8221;. Check <code>docker ps -a --filter status=exited<\/code> before any <code>-a<\/code> prune; if the list is non-empty, decide per container rather than in bulk.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Build cache is the safest large win on a development host and the least safe on a build server, because losing it turns a 40-second incremental build into a 12-minute cold one. On a 4 GB VPS the cache is also the largest single consumer after images. Keep it if builds run more than a few times per day; drop it aggressively if this host only runs production containers.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:code -->\n<pre class=\"wp-block-code\"><code>docker builder prune --filter until=168h --keep-storage 5GB\ndocker container prune --filter until=72h\ndocker image prune -a --filter until=336h<\/code><\/pre>\n<!-- \/wp:code -->\n\n<!-- wp:heading {\"level\":2} -->\n<h2 class=\"wp-block-heading\">Volumes are not images and need their own policy<\/h2>\n<!-- wp:paragraph -->\n<p>Named volumes live under <code>\/var\/lib\/docker\/volumes<\/code> and are never touched by image prunes. They are also the usual source of a 15 GB database in a container you thought was disposable. Treat them as durable storage: back them up, then decide on retention. <code>docker volume prune<\/code> only removes volumes not referenced by any container, running or stopped, which means a deleted-but-not-yet-recreated container&#8217;s data looks reclaimable.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:table -->\n<figure class=\"wp-block-table\"><table><thead><tr><th>Command<\/th><th>Touches running containers<\/th><th>Touches volumes<\/th><\/tr><\/thead><tbody><tr><td>docker image prune<\/td><td>No<\/td><td>No<\/td><\/tr><tr><td>docker image prune -a<\/td><td>No<\/td><td>No<\/td><\/tr><tr><td>docker system prune<\/td><td>No<\/td><td>No<\/td><\/tr><tr><td>docker system prune -a &#8211;volumes<\/td><td>No, but breaks restart<\/td><td>Yes<\/td><\/tr><tr><td>rm -rf \/var\/lib\/docker\/*<\/td><td>Destroys everything<\/td><td>Yes<\/td><\/tr><\/tbody><\/table><\/figure>\n<!-- \/wp:table -->\n\n<!-- wp:heading {\"level\":2} -->\n<h2 class=\"wp-block-heading\">Controlling size before it accumulates<\/h2>\n<!-- wp:paragraph -->\n<p>Two daemon settings do most of the work. <code>data-root<\/code> moves the store to a dedicated disk, and the <code>overlay2.size<\/code> storage-opt caps each container&#8217;s writable layer on XFS with project quotas. On ext4 that option is unavailable, so if per-container limits matter, format the data disk as XFS with <code>pquota<\/code> mounted. Without a cap, one runaway log file inside a container can consume the whole disk.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:code -->\n<pre class=\"wp-block-code\"><code># \/etc\/docker\/daemon.json\n{\n  \"data-root\": \"\/srv\/docker\",\n  \"storage-driver\": \"overlay2\",\n  \"storage-opts\": [\"overlay2.size=20G\"],\n  \"log-driver\": \"json-file\",\n  \"log-opts\": {\"max-size\": \"20m\", \"max-file\": \"3\"}\n}<\/code><\/pre>\n<!-- \/wp:code -->\n\n<!-- wp:paragraph -->\n<p>Log rotation is the other half of the equation. The default <code>json-file<\/code> driver grows without limit, and container logs regularly reach multiple GB on a quiet service after a crash loop. Setting <code>max-size<\/code> at 20 MB with three files caps each container&#8217;s log at 60 MB. Existing containers keep their old settings until recreated, so a <code>docker compose up -d --force-recreate<\/code> is required for the change to take effect.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:heading {\"level\":2} -->\n<h2 class=\"wp-block-heading\">Scheduling cleanup with a hard floor<\/h2>\n<!-- wp:paragraph -->\n<p>A weekly cron job is enough if it is guarded by a disk threshold. Prune only when usage exceeds 75 percent, so a normally lean host never loses build cache. Run it against the narrow commands, log the reclaimed figure, and alert if the host is still above 90 percent after the run \u2014 that indicates a volume or a base image that needs manual attention.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:code -->\n<pre class=\"wp-block-code\"><code>#!\/bin\/sh\nuse=$(df --output=pcent \/var\/lib\/docker | tail -1 | tr -dc 0-9)\n[ \"$use\" -lt 75 ] &amp;&amp; exit 0\ndocker builder prune -f --filter until=168h\ndocker image prune -f --filter until=336h\ndocker container prune -f --filter until=72h\ndf -h \/var\/lib\/docker<\/code><\/pre>\n<!-- \/wp:code -->\n\n<!-- wp:heading {\"level\":2} -->\n<h2 class=\"wp-block-heading\">What to check after a prune<\/h2>\n<!-- wp:paragraph -->\n<p>Confirm every container is still running, then verify the ones with mounts can still reach their data. <code>docker ps --format '{{.Names}} {{.Status}}'<\/code> should show no &#8220;Restarting&#8221; states in the minutes after the job. A container that goes into a restart loop post-prune almost always lost its image or its volume, and both are recoverable by re-pulling or restoring, not by rebooting the VPS.<\/p>\n<!-- \/wp:paragraph -->\n\n<!-- wp:paragraph -->\n<p>Add <code>docker system events --filter type=image --since 1h<\/code> to your troubleshooting notes; it shows exactly which layers were removed and when. With a data root on its own disk, per-container size caps, log rotation and a thresholded weekly prune, overlay2 usage on <a href=\"https:\/\/virtualserversvps.com\/\">a KVM-based VPS<\/a> stays bounded and predictable instead of drifting upward until something breaks.<\/p>\n<!-- \/wp:paragraph -->\n","protected":false},"excerpt":{"rendered":"<p>Docker&#8217;s overlay2 driver keeps every image layer, every stopped container&#8217;s writable layer and every build cache on the same filesystem as \/var\/lib\/docker. On a VPS with a 40 GB disk,&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"iawp_total_views":0,"footnotes":""},"categories":[3],"tags":[],"class_list":["post-1598","post","type-post","status-publish","format-standard","hentry","category-performance-optimization"],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v26.1 (Yoast SEO v26.1) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Keeping overlay2 Disk Usage Under Control Without Breaking Running Containers - Virtual Servers VPS Blog<\/title>\n<meta name=\"description\" content=\"Docker&#039;s overlay2 driver keeps every image layer, every stopped container&#039;s writable layer and every build cache on the same filesystem as\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/virtualserversvps.com\/blog\/docker-overlay2-disk-usage-prune-policy\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Keeping overlay2 Disk Usage Under Control Without Breaking Running Containers\" \/>\n<meta property=\"og:description\" content=\"Keeping overlay2 Disk Usage Under Control Without Breaking Running Containers\" \/>\n<meta property=\"og:url\" content=\"https:\/\/virtualserversvps.com\/blog\/docker-overlay2-disk-usage-prune-policy\/\" \/>\n<meta property=\"og:site_name\" content=\"Virtual Servers VPS Blog\" \/>\n<meta property=\"article:published_time\" content=\"2026-11-04T02:00:33+00:00\" \/>\n<meta name=\"author\" content=\"Virtual-Servers-Vps-Editor\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Virtual-Servers-Vps-Editor\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"6 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/virtualserversvps.com\/blog\/docker-overlay2-disk-usage-prune-policy\/\",\"url\":\"https:\/\/virtualserversvps.com\/blog\/docker-overlay2-disk-usage-prune-policy\/\",\"name\":\"Keeping overlay2 Disk Usage Under Control Without Breaking Running Containers - Virtual Servers VPS Blog\",\"isPartOf\":{\"@id\":\"https:\/\/virtualserversvps.com\/blog\/#website\"},\"datePublished\":\"2026-11-04T02:00:33+00:00\",\"author\":{\"@id\":\"https:\/\/virtualserversvps.com\/blog\/#\/schema\/person\/82a299a8284a66ff49f97c74684724a0\"},\"description\":\"Docker's overlay2 driver keeps every image layer, every stopped container's writable layer and every build cache on the same filesystem as\",\"breadcrumb\":{\"@id\":\"https:\/\/virtualserversvps.com\/blog\/docker-overlay2-disk-usage-prune-policy\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/virtualserversvps.com\/blog\/docker-overlay2-disk-usage-prune-policy\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/virtualserversvps.com\/blog\/docker-overlay2-disk-usage-prune-policy\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/virtualserversvps.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Keeping overlay2 Disk Usage Under Control Without Breaking Running Containers\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/virtualserversvps.com\/blog\/#website\",\"url\":\"https:\/\/virtualserversvps.com\/blog\/\",\"name\":\"Virtual Servers VPS Blog\",\"description\":\"\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/virtualserversvps.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/virtualserversvps.com\/blog\/#\/schema\/person\/82a299a8284a66ff49f97c74684724a0\",\"name\":\"Virtual-Servers-Vps-Editor\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/virtualserversvps.com\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/d820b15f1cd028e97610d9adf536df7be5cb6423869967037d468d5355fa003f?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/d820b15f1cd028e97610d9adf536df7be5cb6423869967037d468d5355fa003f?s=96&d=mm&r=g\",\"caption\":\"Virtual-Servers-Vps-Editor\"},\"sameAs\":[\"https:\/\/virtualserversvps.com\/blog\"],\"url\":\"https:\/\/virtualserversvps.com\/blog\/author\/virtualserversvps\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Keeping overlay2 Disk Usage Under Control Without Breaking Running Containers - Virtual Servers VPS Blog","description":"Docker's overlay2 driver keeps every image layer, every stopped container's writable layer and every build cache on the same filesystem as","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/virtualserversvps.com\/blog\/docker-overlay2-disk-usage-prune-policy\/","og_locale":"en_US","og_type":"article","og_title":"Keeping overlay2 Disk Usage Under Control Without Breaking Running Containers","og_description":"Keeping overlay2 Disk Usage Under Control Without Breaking Running Containers","og_url":"https:\/\/virtualserversvps.com\/blog\/docker-overlay2-disk-usage-prune-policy\/","og_site_name":"Virtual Servers VPS Blog","article_published_time":"2026-11-04T02:00:33+00:00","author":"Virtual-Servers-Vps-Editor","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Virtual-Servers-Vps-Editor","Est. reading time":"6 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/virtualserversvps.com\/blog\/docker-overlay2-disk-usage-prune-policy\/","url":"https:\/\/virtualserversvps.com\/blog\/docker-overlay2-disk-usage-prune-policy\/","name":"Keeping overlay2 Disk Usage Under Control Without Breaking Running Containers - Virtual Servers VPS Blog","isPartOf":{"@id":"https:\/\/virtualserversvps.com\/blog\/#website"},"datePublished":"2026-11-04T02:00:33+00:00","author":{"@id":"https:\/\/virtualserversvps.com\/blog\/#\/schema\/person\/82a299a8284a66ff49f97c74684724a0"},"description":"Docker's overlay2 driver keeps every image layer, every stopped container's writable layer and every build cache on the same filesystem as","breadcrumb":{"@id":"https:\/\/virtualserversvps.com\/blog\/docker-overlay2-disk-usage-prune-policy\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/virtualserversvps.com\/blog\/docker-overlay2-disk-usage-prune-policy\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/virtualserversvps.com\/blog\/docker-overlay2-disk-usage-prune-policy\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/virtualserversvps.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Keeping overlay2 Disk Usage Under Control Without Breaking Running Containers"}]},{"@type":"WebSite","@id":"https:\/\/virtualserversvps.com\/blog\/#website","url":"https:\/\/virtualserversvps.com\/blog\/","name":"Virtual Servers VPS Blog","description":"","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/virtualserversvps.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/virtualserversvps.com\/blog\/#\/schema\/person\/82a299a8284a66ff49f97c74684724a0","name":"Virtual-Servers-Vps-Editor","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/virtualserversvps.com\/blog\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/d820b15f1cd028e97610d9adf536df7be5cb6423869967037d468d5355fa003f?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/d820b15f1cd028e97610d9adf536df7be5cb6423869967037d468d5355fa003f?s=96&d=mm&r=g","caption":"Virtual-Servers-Vps-Editor"},"sameAs":["https:\/\/virtualserversvps.com\/blog"],"url":"https:\/\/virtualserversvps.com\/blog\/author\/virtualserversvps\/"}]}},"_links":{"self":[{"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/posts\/1598","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/comments?post=1598"}],"version-history":[{"count":1,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/posts\/1598\/revisions"}],"predecessor-version":[{"id":1599,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/posts\/1598\/revisions\/1599"}],"wp:attachment":[{"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/media?parent=1598"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/categories?post=1598"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/tags?post=1598"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}