{"id":54088,"date":"2025-11-18T06:31:41","date_gmt":"2025-11-18T06:31:41","guid":{"rendered":"https:\/\/www.devopsschool.com\/blog\/?p=54088"},"modified":"2026-02-21T08:28:55","modified_gmt":"2026-02-21T08:28:55","slug":"docker-tutorials-engine-v29-x-guide-to-major-changes-architecture-migration","status":"publish","type":"post","link":"https:\/\/www.devopsschool.com\/blog\/docker-tutorials-engine-v29-x-guide-to-major-changes-architecture-migration\/","title":{"rendered":"Docker Tutorials: Engine v29.x \u2014 Guide to Major Changes, Architecture &amp; Migration"},"content":{"rendered":"\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"800\" src=\"https:\/\/www.devopsschool.com\/blog\/wp-content\/uploads\/2025\/11\/docker-_compressed.jpg\" alt=\"\" class=\"wp-image-54089\" srcset=\"https:\/\/www.devopsschool.com\/blog\/wp-content\/uploads\/2025\/11\/docker-_compressed.jpg 800w, https:\/\/www.devopsschool.com\/blog\/wp-content\/uploads\/2025\/11\/docker-_compressed-300x300.jpg 300w, https:\/\/www.devopsschool.com\/blog\/wp-content\/uploads\/2025\/11\/docker-_compressed-150x150.jpg 150w, https:\/\/www.devopsschool.com\/blog\/wp-content\/uploads\/2025\/11\/docker-_compressed-768x768.jpg 768w, https:\/\/www.devopsschool.com\/blog\/wp-content\/uploads\/2025\/11\/docker-_compressed-250x250.jpg 250w, https:\/\/www.devopsschool.com\/blog\/wp-content\/uploads\/2025\/11\/docker-_compressed-80x80.jpg 80w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83d\udccc Introduction<\/h2>\n\n\n\n<p>Docker remains the most popular container runtime for developers and sysadmins, but starting from <strong>Docker Engine v29.x<\/strong>, significant internal architecture changes have emerged. These changes impact <strong>how container images are stored<\/strong>, <strong>where layered filesystems exist<\/strong>, and <strong>how Docker integrates with its underlying containerd runtime<\/strong>.<\/p>\n\n\n\n<p>This guide covers:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Major differences between Docker \u226428 and v29+<\/li>\n\n\n\n<li>Internal storage layout changes<\/li>\n\n\n\n<li>Impacts on developers and administrators<\/li>\n\n\n\n<li>How to inspect image and container layers now<\/li>\n\n\n\n<li>Migration and compatibility guidance<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h2 class=\"wp-block-heading\">\ud83e\uddf1 Docker Architecture \u2014 Before v29<\/h2>\n\n\n\n<p>In earlier Docker versions (up to v28.x):<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Feature<\/th><th>Description<\/th><\/tr><\/thead><tbody><tr><td><strong>Image Storage<\/strong><\/td><td>Docker used its \u201cGraphDriver\u201d model, storing image layers under <code>\/var\/lib\/docker\/overlay2\/<\/code> or similar (e.g., <code>aufs<\/code>, <code>btrfs<\/code>)<\/td><\/tr><tr><td><strong>Container Storage<\/strong><\/td><td>Containers used a writable layer on top of the image root filesystem, served by the same driver (<code>overlay2<\/code>)<\/td><\/tr><tr><td><strong>Image Metadata<\/strong><\/td><td>Found under <code>\/var\/lib\/docker\/image\/&lt;driver&gt;\/layerdb\/<\/code><\/td><\/tr><tr><td><strong>Docker Internal Runtime<\/strong><\/td><td>Docker used its own embedded runtime (<code>dockerd<\/code>), talking directly to container execution (<code>runc<\/code>)<\/td><\/tr><tr><td><strong>Networking<\/strong><\/td><td>Docker relied on <code>iptables<\/code> to manage NAT, firewall rules, port forwarding, and DOCKER-USER chains<\/td><\/tr><tr><td><strong>Minimum API Versions<\/strong><\/td><td>Docker Engine accepted older client versions (v1.24+) depending on the server version<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h2 class=\"wp-block-heading\">\ud83c\udd95 What&#8217;s New in Docker 29.x?<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">\ud83c\udfaf Key Changes<\/h3>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Area<\/th><th>What\u2019s Changed in Docker v29+<\/th><th>Why It Matters<\/th><\/tr><\/thead><tbody><tr><td><strong>Storage Model<\/strong><\/td><td>Docker <strong>adopts containerd\u2019s native image store + snapshotter<\/strong> as default<\/td><td>Massive change: image layers are no longer under <code>\/var\/lib\/docker\/image\/...<\/code> but under <code>\/var\/lib\/containerd\/...<\/code><\/td><\/tr><tr><td><strong>Image Layer Paths<\/strong><\/td><td>Layers are stored in: <code>\/var\/lib\/containerd\/io.containerd.snapshotter.v1.overlayfs\/snapshots\/&lt;id&gt;\/fs\/<\/code><\/td><td>If you\u2019re looking for layer data for forensic or debug, this path is now standard<\/td><\/tr><tr><td><strong>Container RootFS Path<\/strong><\/td><td>Merged runtime container filesystem is now under: <code>\/var\/lib\/docker\/rootfs\/overlayfs\/&lt;container-id&gt;\/<\/code><\/td><td>A clean split between image storage (containerd) and runtime (docker)<\/td><\/tr><tr><td><strong>Networking Support<\/strong><\/td><td>Experimental support for <strong>nftables<\/strong> (in addition to iptables)<\/td><td>Future-proofing for systems where iptables is deprecated<\/td><\/tr><tr><td><strong>API Version<\/strong><\/td><td>Minimum Docker API version is now <strong>1.44<\/strong><\/td><td>Older clients or tools using old SDKs will need updates<\/td><\/tr><tr><td><strong>Go Module Migration<\/strong><\/td><td>Docker Engine (moby) fully migrated to <strong>Go modules<\/strong><\/td><td>Easier version tracking and imports for developers<\/td><\/tr><tr><td><strong>Legacy Storage Driver Deprecation<\/strong><\/td><td>Direct usage of overlay2\/aufs \u201cgraph drivers\u201d is being phased out<\/td><td>All new installs use containerd snapshotters by default<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h2 class=\"wp-block-heading\">\ud83d\udd0d Storage Layout \u2013 Then vs Now<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Before Docker v29:<\/h3>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">\/<span class=\"hljs-keyword\">var<\/span>\/lib\/docker\/\n  \u251c\u2500\u2500 overlay2\/               <span class=\"hljs-comment\"># Data for layers and containers<\/span>\n  \u251c\u2500\u2500 containers\/\n  \u251c\u2500\u2500 image\/\n  \u2502    \u2514\u2500\u2500 overlay2\/\n  \u2502         \u2514\u2500\u2500 layerdb\/\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h3 class=\"wp-block-heading\">Docker v29 and after:<\/h3>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">\/<span class=\"hljs-keyword\">var<\/span>\/lib\/docker\/\n  \u251c\u2500\u2500 rootfs\/overlayfs\/       <span class=\"hljs-comment\"># Merged rootfs for active containers<\/span>\n  \u251c\u2500\u2500 containers\/\n  \u251c\u2500\u2500 volumes\/\n\n<span class=\"hljs-comment\"># Image layers are now here:<\/span>\n  \n\/<span class=\"hljs-keyword\">var<\/span>\/lib\/containerd\/\n  \u251c\u2500\u2500 io.containerd.content.v1.content\/\n  \u251c\u2500\u2500 io.containerd.snapshotter.v1.overlayfs\/\n  \u2502    \u2514\u2500\u2500 snapshots\/&lt;id&gt;\/fs    <span class=\"hljs-comment\"># Physical image layer content<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h2 class=\"wp-block-heading\">\ud83e\udde0 Developer Notes<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Image Developers<\/strong>: Image layers are now under <code>\/var\/lib\/containerd\/...<\/code> \u2014 not <code>\/var\/lib\/docker\/...<\/code>.<\/li>\n\n\n\n<li><strong>Container Debuggers<\/strong>: Live container filesystems are still under <code>\/var\/lib\/docker\/rootfs\/...<\/code>.<\/li>\n\n\n\n<li><strong>Storage Tools<\/strong>: Tools that scanned <code>\/var\/lib\/docker\/image\/<\/code> will need updates for Docker 29+.<\/li>\n\n\n\n<li><strong>DevOps Pipelines<\/strong>: Docker API version must be \u22651.44 \u2014 update older SDKs or CI automation.<\/li>\n\n\n\n<li><strong>Advanced Users<\/strong>: Use <code>ctr<\/code> or <code>nerdctl<\/code> to inspect containerd-managed snapshots.<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h2 class=\"wp-block-heading\">\ud83d\udee0\ufe0f Practical Examples<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">\ud83e\uddfe Retrieve Image Snapshot Info via <code>ctr<\/code>:<\/h3>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\">ctr -n moby images ls | grep <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">image<\/span>&gt;<\/span>\nctr -n moby image info docker.io\/<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">image<\/span>&gt;<\/span>:tag\nctr -n moby snapshots ls\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h3 class=\"wp-block-heading\">\ud83d\uddc2\ufe0f View Image Layer Contents:<\/h3>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">cd \/<span class=\"hljs-keyword\">var<\/span>\/lib\/containerd\/io.containerd.snapshotter.v1.overlayfs\/snapshots\/<span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">id<\/span>&gt;<\/span>\/fs\nls -al\n<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h3 class=\"wp-block-heading\">\ud83e\uddfe Inspect Container RootFS:<\/h3>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">cd \/<span class=\"hljs-keyword\">var<\/span>\/lib\/docker\/rootfs\/overlayfs\/<span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">container-id<\/span>&gt;<\/span>\/\nls -al\n<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h2 class=\"wp-block-heading\">\u26a0\ufe0f Impact and Migration Guidance<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Existing Hosts<\/strong>:\n<ul class=\"wp-block-list\">\n<li>Docker v29 does <strong>not force migration<\/strong> \u2014 your existing overlay2\/aufs data is retained.<\/li>\n\n\n\n<li>Plan carefully if manually moving to containerd storage backend.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>New Installations<\/strong>:\n<ul class=\"wp-block-list\">\n<li>Automatically use containerd image store and snapshotter \u2014 follow the new paths.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Tools &amp; Plugins<\/strong>:\n<ul class=\"wp-block-list\">\n<li>Ensure compatibility with API 1.44+ and the containerd layout.<\/li>\n\n\n\n<li>Update scanning, backup, and forensic tools accordingly.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>CI\/CD Agents<\/strong>:\n<ul class=\"wp-block-list\">\n<li>Ensure Docker SDK versions, client libraries, and API calls are upgraded.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<h2 class=\"wp-block-heading\">\ud83e\udded Conclusion<\/h2>\n\n\n\n<p>Docker v29 is a <strong>major evolutionary jump<\/strong>, aligning Docker more deeply with <strong>containerd\u2019s architecture<\/strong>, cleaning up storage models, modernizing networking, and improving modularity.<\/p>\n\n\n\n<p>This change benefits the ecosystem \u2014 but <strong>requires awareness and adaptation<\/strong> from developers, administrators, and tool maintainers.<\/p>\n\n\n\n<p><strong>Key paths to remember<\/strong>:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Image Layers<\/strong>:<br><code>\/var\/lib\/containerd\/io.containerd.snapshotter.v1.overlayfs\/snapshots\/&lt;snapshot-id&gt;\/fs<\/code><\/li>\n\n\n\n<li><strong>Container Filesystems<\/strong>:<br><code>\/var\/lib\/docker\/rootfs\/overlayfs\/&lt;container-id&gt;\/<\/code><\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\">\n\n\n\n<p><\/p>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>\ud83d\udccc Introduction Docker remains the most popular container runtime for developers and sysadmins, but starting from Docker Engine v29.x, significant internal architecture changes have emerged. These changes&#8230; <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"_joinchat":[],"footnotes":""},"categories":[11138],"tags":[],"class_list":["post-54088","post","type-post","status-publish","format-standard","hentry","category-best-tools"],"_links":{"self":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts\/54088","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/comments?post=54088"}],"version-history":[{"count":2,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts\/54088\/revisions"}],"predecessor-version":[{"id":59875,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts\/54088\/revisions\/59875"}],"wp:attachment":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/media?parent=54088"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/categories?post=54088"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/tags?post=54088"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}