{"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 impact how container images are stored, where layered filesystems exist, and how Docker integrates with its underlying containerd runtime. This guide covers: \ud83e\uddf1 Docker Architecture \u2014 Before v29&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"_kad_post_transparent":"","_kad_post_title":"","_kad_post_layout":"","_kad_post_sidebar_id":"","_kad_post_content_style":"","_kad_post_vertical_padding":"","_kad_post_feature":"","_kad_post_feature_position":"","_kad_post_header":false,"_kad_post_footer":false,"_kad_post_classname":"","_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}]}}