{"id":4828,"date":"2026-07-03T22:13:10","date_gmt":"2026-07-03T16:43:10","guid":{"rendered":"https:\/\/itsupportwale.com\/blog\/docker-best-practices-build-efficient-secure-containers\/"},"modified":"2026-07-03T22:13:10","modified_gmt":"2026-07-03T16:43:10","slug":"docker-best-practices-build-efficient-secure-containers","status":"publish","type":"post","link":"https:\/\/itsupportwale.com\/blog\/docker-best-practices-build-efficient-secure-containers\/","title":{"rendered":"Docker Best Practices: Build Efficient, Secure Containers"},"content":{"rendered":"<p>I just ran <code>docker images<\/code> on the &#8220;Hello World&#8221; container you pushed to the registry. <\/p>\n<pre class=\"codehilite\"><code class=\"language-bash\">REPOSITORY          TAG       IMAGE ID       CREATED          SIZE\nhello-world-app     latest    f3a2b1c0d9e8   2 minutes ago    2.14GB\n<\/code><\/pre>\n<p>Two. Gigabytes. For a Python script that prints &#8220;Hello World&#8221; and exits. In 1994, I was managing entire SunOS clusters on less disk space than you\u2019ve wasted on a single container layer. You\u2019ve managed to package an entire operating system, a compiler toolchain, three different versions of the Python interpreter, and probably the developer\u2019s local <code>Downloads<\/code> folder into a single OCI image. <\/p>\n<p>This isn&#8217;t &#8220;modern development.&#8221; This is a digital crime scene. You\u2019ve treated Docker like a virtual machine, and in doing so, you\u2019ve created a slow, insecure, and unmaintainable nightmare that I now have to fix before it touches my production nodes.<\/p>\n<div id=\"ez-toc-container\" class=\"ez-toc-v2_0_80 counter-hierarchy ez-toc-counter ez-toc-grey ez-toc-container-direction\">\n<p class=\"ez-toc-title\" style=\"cursor:inherit\">Table of Contents<\/p>\n<label for=\"ez-toc-cssicon-toggle-item-6a48fca215149\" class=\"ez-toc-cssicon-toggle-label\"><span class=\"\"><span class=\"eztoc-hide\" style=\"display:none;\">Toggle<\/span><span class=\"ez-toc-icon-toggle-span\"><svg style=\"fill: #999;color:#999\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" class=\"list-377408\" width=\"20px\" height=\"20px\" viewBox=\"0 0 24 24\" fill=\"none\"><path d=\"M6 6H4v2h2V6zm14 0H8v2h12V6zM4 11h2v2H4v-2zm16 0H8v2h12v-2zM4 16h2v2H4v-2zm16 0H8v2h12v-2z\" fill=\"currentColor\"><\/path><\/svg><svg style=\"fill: #999;color:#999\" class=\"arrow-unsorted-368013\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"10px\" height=\"10px\" viewBox=\"0 0 24 24\" version=\"1.2\" baseProfile=\"tiny\"><path d=\"M18.2 9.3l-6.2-6.3-6.2 6.3c-.2.2-.3.4-.3.7s.1.5.3.7c.2.2.4.3.7.3h11c.3 0 .5-.1.7-.3.2-.2.3-.5.3-.7s-.1-.5-.3-.7zM5.8 14.7l6.2 6.3 6.2-6.3c.2-.2.3-.5.3-.7s-.1-.5-.3-.7c-.2-.2-.4-.3-.7-.3h-11c-.3 0-.5.1-.7.3-.2.2-.3.5-.3.7s.1.5.3.7z\"\/><\/svg><\/span><\/span><\/label><input type=\"checkbox\"  id=\"ez-toc-cssicon-toggle-item-6a48fca215149\"  aria-label=\"Toggle\" \/><nav><ul class='ez-toc-list ez-toc-list-level-1 ' ><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-1\" href=\"https:\/\/itsupportwale.com\/blog\/docker-best-practices-build-efficient-secure-containers\/#THE_CRIME_SCENE_A_2GB_HELLO_WORLD_MANIFESTO\" >THE CRIME SCENE: A 2GB HELLO WORLD MANIFESTO<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-2\" href=\"https:\/\/itsupportwale.com\/blog\/docker-best-practices-build-efficient-secure-containers\/#THE_RECKONING_DISSECTING_YOUR_INCOMPETENCE\" >THE RECKONING: DISSECTING YOUR INCOMPETENCE<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-3\" href=\"https:\/\/itsupportwale.com\/blog\/docker-best-practices-build-efficient-secure-containers\/#THE_REFACTORING_LOG_STRIPPING_THE_BLOAT\" >THE REFACTORING LOG: STRIPPING THE BLOAT<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-4\" href=\"https:\/\/itsupportwale.com\/blog\/docker-best-practices-build-efficient-secure-containers\/#THE_LAYER_CAKE_OF_LIES_UNDERSTANDING_OVERLAYFS\" >THE LAYER CAKE OF LIES: UNDERSTANDING OVERLAYFS<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-5\" href=\"https:\/\/itsupportwale.com\/blog\/docker-best-practices-build-efficient-secure-containers\/#THE_SECURITY_HARDENING_ROOT_IS_A_FIRING_OFFENSE\" >THE SECURITY HARDENING: ROOT IS A FIRING OFFENSE<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-6\" href=\"https:\/\/itsupportwale.com\/blog\/docker-best-practices-build-efficient-secure-containers\/#THE_NUANCES_OF_COPY_VS_ADD\" >THE NUANCES OF COPY VS ADD<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-7\" href=\"https:\/\/itsupportwale.com\/blog\/docker-best-practices-build-efficient-secure-containers\/#SYSCALL_FILTERING_AND_SECCOMP\" >SYSCALL FILTERING AND SECCOMP<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-8\" href=\"https:\/\/itsupportwale.com\/blog\/docker-best-practices-build-efficient-secure-containers\/#THE_FINAL_VERDICT_ADHERING_TO_DOCKER_BEST_PRACTICES_OR_QUITTING\" >THE FINAL VERDICT: ADHERING TO DOCKER BEST PRACTICES OR QUITTING<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-9\" href=\"https:\/\/itsupportwale.com\/blog\/docker-best-practices-build-efficient-secure-containers\/#Related_Articles\" >Related Articles<\/a><\/li><\/ul><\/nav><\/div>\n<h2><span class=\"ez-toc-section\" id=\"THE_CRIME_SCENE_A_2GB_HELLO_WORLD_MANIFESTO\"><\/span>THE CRIME SCENE: A 2GB HELLO WORLD MANIFESTO<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Here is the Dockerfile you handed me. I\u2019ve printed it out just so I could physically throw it in the trash, but for the sake of this post-mortem, let\u2019s look at the wreckage:<\/p>\n<pre class=\"codehilite\"><code class=\"language-dockerfile\"># THE CRIME SCENE\nFROM ubuntu:latest\n\n# Running update without cleaning up the cache\nRUN apt-get update\nRUN apt-get install -y python3 python3-pip build-essential\n\n# Using ADD instead of COPY for no reason\nADD . \/app\nWORKDIR \/app\n\n# Installing dependencies as root\nRUN pip3 install -r requirements.txt\n\n# No user defined, running as root\nCMD [&quot;python3&quot;, &quot;hello.py&quot;]\n<\/code><\/pre>\n<h2><span class=\"ez-toc-section\" id=\"THE_RECKONING_DISSECTING_YOUR_INCOMPETENCE\"><\/span>THE RECKONING: DISSECTING YOUR INCOMPETENCE<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Let\u2019s go through this line by line, because apparently, the concept of &#8220;efficiency&#8221; has been lost to your generation of &#8220;move fast and break things&#8221; developers.<\/p>\n<p><strong>1. <code>FROM ubuntu:latest<\/code><\/strong><br \/>\nYou used the <code>latest<\/code> tag. Never use the <code>latest<\/code> tag. It is a non-deterministic pointer to whatever the maintainer felt like pushing five minutes ago. When this build fails in six months because <code>ubuntu:latest<\/code> moved from 22.04 to 24.04 and broke a dependency, I\u2019m not the one who\u2019s going to stay up until 3 AM fixing it. You are. Furthermore, <code>ubuntu<\/code> is a general-purpose distribution. It contains binaries for things your app will never touch\u2014utilities for hardware management, internationalization files, and man pages. You don\u2019t need <code>sed<\/code>, <code>awk<\/code>, or <code>grep<\/code> inside a container that only runs a Python script.<\/p>\n<p><strong>2. Multiple <code>RUN<\/code> commands<\/strong><br \/>\nEvery <code>RUN<\/code> instruction in a Dockerfile creates a new layer in the filesystem. You ran <code>apt-get update<\/code> in one layer and <code>apt-get install<\/code> in another. Do you know what that does? It stores the entire package index in the first layer. Even if you tried to delete it in a later layer, it stays in the image history forever. It\u2019s like painting a wall black, then painting it white, and wondering why the coat of paint is so thick.<\/p>\n<p><strong>3. <code>build-essential<\/code> in a production image<\/strong><br \/>\nWhy is <code>gcc<\/code>, <code>g++<\/code>, and <code>make<\/code> in my production environment? Unless your app is compiling C extensions at runtime (which it shouldn&#8217;t be), this is a massive security risk. If an attacker gains shell access to your container, you\u2019ve kindly provided them with all the tools they need to compile a rootkit or a crypto-miner right on the spot.<\/p>\n<p><strong>4. The <code>ADD<\/code> instruction<\/strong><br \/>\nYou used <code>ADD . \/app<\/code>. The <code>ADD<\/code> instruction is a relic. It has &#8220;magic&#8221; features like auto-extracting tarballs and fetching remote URLs. Unless you specifically need that magic, use <code>COPY<\/code>. It\u2019s explicit. It\u2019s predictable. And because you didn&#8217;t include a <code>.dockerignore<\/code> file, you just copied your <code>.git<\/code> directory, your <code>__pycache__<\/code>, and your local environment variables into the image. That\u2019s where that 2GB is coming from.<\/p>\n<p><strong>5. Running as <code>root<\/code><\/strong><br \/>\nYou didn&#8217;t define a <code>USER<\/code>. By default, Docker runs as root. If there is a container escape vulnerability in the kernel (and there will be), the attacker doesn&#8217;t just have your app; they have the host. Running as root in a container is a firing offense in my book.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"THE_REFACTORING_LOG_STRIPPING_THE_BLOAT\"><\/span>THE REFACTORING LOG: STRIPPING THE BLOAT<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>We are going to rebuild this using a multi-stage build and a specific, slim base image. We are going to use <code>python:3.11.5-slim-bookworm<\/code>. Why? Because it\u2019s based on Debian, it\u2019s predictable, and it doesn&#8217;t include the kitchen sink.<\/p>\n<pre class=\"codehilite\"><code class=\"language-dockerfile\"># STAGE 1: Builder\nFROM python:3.11.5-slim-bookworm AS builder\n\n# Set environment variables to keep Python from being annoying\nENV PYTHONDONTWRITEBYTECODE=1\nENV PYTHONUNBUFFERED=1\n\nWORKDIR \/build\n\n# Install build dependencies only here\nRUN apt-get update &amp;&amp; apt-get install -y --no-install-recommends \\\n    build-essential \\\n    &amp;&amp; rm -rf \/var\/lib\/apt\/lists\/*\n\nCOPY requirements.txt .\nRUN pip install --no-cache-dir --prefix=\/install -r requirements.txt\n\n# STAGE 2: Final Runtime\nFROM python:3.11.5-slim-bookworm\n\n# Create a non-privileged user\nRUN groupadd -g 10001 appuser &amp;&amp; \\\n    useradd -u 10001 -g appuser -s \/bin\/sh appuser\n\nWORKDIR \/app\n\n# Copy only the installed site-packages from the builder\nCOPY --from=builder \/install \/usr\/local\nCOPY --chown=appuser:appuser hello.py .\n\nUSER appuser\n\nENTRYPOINT [&quot;python&quot;, &quot;hello.py&quot;]\n<\/code><\/pre>\n<p>Look at the difference. We use a &#8220;Builder&#8221; stage to compile anything that needs compiling, then we copy only the resulting artifacts into a fresh, clean &#8220;Runtime&#8221; stage. The <code>build-essential<\/code> garbage never makes it to the final image. We use <code>--no-install-recommends<\/code> to avoid installing &#8220;suggested&#8221; packages that we don&#8217;t need. We clean up <code>\/var\/lib\/apt\/lists\/*<\/code> in the same <code>RUN<\/code> command to ensure the cache isn&#8217;t persisted in the layer.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"THE_LAYER_CAKE_OF_LIES_UNDERSTANDING_OVERLAYFS\"><\/span>THE LAYER CAKE OF LIES: UNDERSTANDING OVERLAYFS<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>To understand why your 2GB image is a failure, you need to understand how the storage driver works. Most modern Docker installations use <code>overlay2<\/code>. It\u2019s a union filesystem. It stacks directories on top of each other and presents them as a single unified view.<\/p>\n<p>When you run a command like <code>RUN apt-get update<\/code>, Docker creates a new directory (a layer). Any files modified or created during that command are written to this new directory. If you run <code>RUN rm -rf \/var\/lib\/apt\/lists\/*<\/code> in a <em>subsequent<\/em> line, Docker doesn&#8217;t actually delete those files from the underlying storage. It creates a &#8220;whiteout&#8221; file in the new layer that tells the union filesystem to hide the file from the merged view. The bits are still there, taking up space, being pushed to the registry, and being pulled by the production servers.<\/p>\n<p>Let\u2019s look at the <code>docker history<\/code> of your original disaster:<\/p>\n<pre class=\"codehilite\"><code class=\"language-bash\">$ docker history --human --format &quot;{{.CreatedBy}}: {{.Size}}&quot; hello-world-app:latest\n\n\/bin\/sh -c #(nop)  CMD [&quot;python3&quot; &quot;hello.py&quot;]: 0B\n\/bin\/sh -c pip3 install -r requirements.txt: 850MB\n\/bin\/sh -c #(nop) WORKDIR \/app: 0B\n\/bin\/sh -c ADD . \/app: 1.1GB\n\/bin\/sh -c apt-get install -y python3 python3-pip...: 450MB\n\/bin\/sh -c apt-get update: 32MB\n\/bin\/sh -c #(nop)  FROM ubuntu:latest: 77.8MB\n<\/code><\/pre>\n<p>Look at that <code>ADD . \/app<\/code> line. 1.1GB. You copied your local <code>venv<\/code> folder and your <code>.git<\/code> history into the image. Then look at the <code>pip install<\/code> line. 850MB. Because you didn&#8217;t use <code>--no-cache-dir<\/code>, pip saved a copy of every wheel it downloaded in <code>~\/.cache\/pip<\/code>. <\/p>\n<p>In the refactored version, we chain commands using <code>&amp;&amp;<\/code> and clean up in the same layer. This ensures that the temporary files never get committed to the image&#8217;s read-only layers. This is a fundamental &#8220;docker best&#8221; practice that separates the engineers from the script kiddies.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"THE_SECURITY_HARDENING_ROOT_IS_A_FIRING_OFFENSE\"><\/span>THE SECURITY HARDENING: ROOT IS A FIRING OFFENSE<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>I mentioned UID\/GID mapping earlier. Let&#8217;s get into the weeds. When you run a process as root inside a container, it is, by default, the same UID 0 as the root user on the host. While Docker uses Linux Namespaces to isolate the process, namespaces are not a perfect sandbox. There have been numerous &#8220;container escape&#8221; vulnerabilities (like Dirty Pipe or various <code>runc<\/code> exploits) where a process inside the container can break out.<\/p>\n<p>If that process is running as root, it has root privileges on your host kernel. By creating a specific <code>appuser<\/code> with a high UID (like 10001) and using the <code>USER<\/code> instruction, we ensure that even if a breakout occurs, the attacker is trapped in a low-privilege account.<\/p>\n<p>Furthermore, we should be talking about Linux Capabilities. A standard process doesn&#8217;t need the ability to change the system clock (<code>CAP_SYS_TIME<\/code>), modify kernel modules (<code>CAP_SYS_MODULE<\/code>), or bypass file permissions (<code>CAP_DAC_OVERRIDE<\/code>). When I deploy your container, I\u2019m going to use the following flags:<\/p>\n<pre class=\"codehilite\"><code class=\"language-bash\">docker run --cap-drop ALL --cap-add NET_BIND_SERVICE --security-opt no-new-privileges:true hello-world-app:v1.0.0\n<\/code><\/pre>\n<p>This drops every single kernel capability and only adds back the bare minimum needed to bind to a network port. The <code>no-new-privileges<\/code> flag prevents the process from gaining new privileges via <code>setuid<\/code> or <code>setgid<\/code> binaries. If you had written your Dockerfile correctly, you would have known this.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"THE_NUANCES_OF_COPY_VS_ADD\"><\/span>THE NUANCES OF COPY VS ADD<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>I see juniors get this wrong constantly. They think <code>ADD<\/code> is just <code>COPY<\/code> with a shorter name. It isn&#8217;t. <\/p>\n<p><code>COPY<\/code> is the preferred instruction. It does exactly what it says: it copies files or directories from your build context into the container. It is transparent and easy to audit.<\/p>\n<p><code>ADD<\/code>, on the other hand, has two &#8220;features&#8221; that are actually security vulnerabilities in disguise:<br \/>\n1. <strong>Remote URL Support:<\/strong> If you use <code>ADD https:\/\/example.com\/big-file.tar.gz \/app\/<\/code>, Docker will download that file. But it doesn&#8217;t support checksum verification within the instruction. You have no idea if that file was tampered with in transit. A better approach is to use <code>RUN curl ... &amp;&amp; sha256sum ...<\/code>.<br \/>\n2. <strong>Auto-Extraction:<\/strong> If you <code>ADD<\/code> a local <code>.tar.gz<\/code> file, Docker will automatically extract it into the destination directory. This sounds convenient until you realize it can lead to &#8220;Zip Slip&#8221; style attacks or simply unexpected filesystem layouts.<\/p>\n<p>By using <code>COPY<\/code>, we maintain a strict audit trail of what is entering the image. We also leverage the layer cache more effectively. Docker\u2019s build cache works by comparing the checksum of the files being copied. If you use <code>ADD<\/code> with a remote URL, the cache behavior becomes unpredictable.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"SYSCALL_FILTERING_AND_SECCOMP\"><\/span>SYSCALL FILTERING AND SECCOMP<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Since you&#8217;re so fond of using <code>ubuntu:latest<\/code>, I assume you haven&#8217;t given a single thought to the system calls your application makes. Every time your Python script interacts with the outside world\u2014opening a file, sending a network packet, checking the time\u2014it makes a syscall to the host kernel.<\/p>\n<p>A standard Docker container has access to about 300 syscalls. Your &#8220;Hello World&#8221; app probably needs about 15. By leaving the other 285 syscalls available, you are increasing the attack surface of the kernel. <\/p>\n<p>In a real environment, I would run your container through <code>strace<\/code> to see exactly what it&#8217;s doing:<\/p>\n<pre class=\"codehilite\"><code class=\"language-bash\">strace -c -f docker run hello-world-app:v1.0.0\n<\/code><\/pre>\n<p>Then, I would generate a Seccomp (Secure Computing Mode) profile that explicitly allows only those syscalls and denies everything else. If your app suddenly tries to call <code>mount()<\/code> or <code>ptrace()<\/code>, the kernel will kill the process instantly. This is how we run production systems. We don&#8217;t just &#8220;hope&#8221; the code is safe; we enforce safety at the syscall level.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"THE_FINAL_VERDICT_ADHERING_TO_DOCKER_BEST_PRACTICES_OR_QUITTING\"><\/span>THE FINAL VERDICT: ADHERING TO DOCKER BEST PRACTICES OR QUITTING<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>The industry has become lazy. Because compute is cheap and disk space is plentiful, people think it&#8217;s okay to ship 2GB containers for trivial tasks. It isn&#8217;t. Large images take longer to pull, which slows down auto-scaling during traffic spikes. They take longer to scan for vulnerabilities. They consume more expensive NVMe storage on our build servers.<\/p>\n<p>Adhering to &#8220;docker best&#8221; practices isn&#8217;t about being a pedant; it&#8217;s about professional engineering. It\u2019s about knowing that when the pager goes off at 4 AM, the container is as slim, secure, and predictable as possible.<\/p>\n<p>Here is your homework. You will take that 2GB monstrosity and you will refactor it until it is under 100MB. You will use a multi-stage build. You will use a non-root user. You will use specific version tags for every image in the <code>FROM<\/code> line. And if I see <code>apt-get update<\/code> without a corresponding cleanup in the same layer again, I will personally revoke your access to the production cluster and move your desk next to the server room intake fans.<\/p>\n<p>Docker is a tool for isolation and packaging, not a dumping ground for your messy development environment. Treat the filesystem with respect, treat the kernel with caution, and for the love of Ken Thompson, stop using <code>latest<\/code>.<\/p>\n<p>Now, get out of my office and fix this. I have a Perl script from 1998 that\u2019s more efficient than your entire career.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"Related_Articles\"><\/span>Related Articles<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Explore more insights and best practices:<\/p>\n<ul>\n<li><a href=\"https:\/\/itsupportwale.com\/blog\/how-to-install-asterisk-16-on-ubuntu-18-04-lts\/\">How To Install Asterisk 16 On Ubuntu 18 04 Lts<\/a><\/li>\n<li><a href=\"https:\/\/itsupportwale.com\/blog\/mastering-docker-compose-simplify-multi-container-apps-2\/\">Mastering Docker Compose Simplify Multi Container Apps 2<\/a><\/li>\n<li><a href=\"https:\/\/itsupportwale.com\/blog\/artificial-intelligence-news-guide\/\">Artificial Intelligence News Guide<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>I just ran docker images on the &#8220;Hello World&#8221; container you pushed to the registry. REPOSITORY TAG IMAGE ID CREATED SIZE hello-world-app latest f3a2b1c0d9e8 2 minutes ago 2.14GB Two. Gigabytes. For a Python script that prints &#8220;Hello World&#8221; and exits. In 1994, I was managing entire SunOS clusters on less disk space than you\u2019ve wasted &#8230; <a title=\"Docker Best Practices: Build Efficient, Secure Containers\" class=\"read-more\" href=\"https:\/\/itsupportwale.com\/blog\/docker-best-practices-build-efficient-secure-containers\/\" aria-label=\"Read more  on Docker Best Practices: Build Efficient, Secure Containers\">Read more<\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-4828","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.0 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Docker Best Practices: Build Efficient, Secure Containers - ITSupportWale<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/itsupportwale.com\/blog\/docker-best-practices-build-efficient-secure-containers\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Docker Best Practices: Build Efficient, Secure Containers - ITSupportWale\" \/>\n<meta property=\"og:description\" content=\"I just ran docker images on the &#8220;Hello World&#8221; container you pushed to the registry. REPOSITORY TAG IMAGE ID CREATED SIZE hello-world-app latest f3a2b1c0d9e8 2 minutes ago 2.14GB Two. Gigabytes. For a Python script that prints &#8220;Hello World&#8221; and exits. In 1994, I was managing entire SunOS clusters on less disk space than you\u2019ve wasted ... Read more\" \/>\n<meta property=\"og:url\" content=\"https:\/\/itsupportwale.com\/blog\/docker-best-practices-build-efficient-secure-containers\/\" \/>\n<meta property=\"og:site_name\" content=\"ITSupportWale\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/Itsupportwale-298547177495978\" \/>\n<meta property=\"article:published_time\" content=\"2026-07-03T16:43:10+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/itsupportwale.com\/blog\/wp-content\/uploads\/2021\/05\/android-chrome-512x512-1.png\" \/>\n\t<meta property=\"og:image:width\" content=\"512\" \/>\n\t<meta property=\"og:image:height\" content=\"512\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Techie\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Techie\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"10 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/itsupportwale.com\/blog\/docker-best-practices-build-efficient-secure-containers\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/itsupportwale.com\/blog\/docker-best-practices-build-efficient-secure-containers\/\"},\"author\":{\"name\":\"Techie\",\"@id\":\"https:\/\/itsupportwale.com\/blog\/#\/schema\/person\/8c5a2b3d36396e0a8fd91ec8242fd46d\"},\"headline\":\"Docker Best Practices: Build Efficient, Secure Containers\",\"datePublished\":\"2026-07-03T16:43:10+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/itsupportwale.com\/blog\/docker-best-practices-build-efficient-secure-containers\/\"},\"wordCount\":1718,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/itsupportwale.com\/blog\/#organization\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/itsupportwale.com\/blog\/docker-best-practices-build-efficient-secure-containers\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/itsupportwale.com\/blog\/docker-best-practices-build-efficient-secure-containers\/\",\"url\":\"https:\/\/itsupportwale.com\/blog\/docker-best-practices-build-efficient-secure-containers\/\",\"name\":\"Docker Best Practices: Build Efficient, Secure Containers - ITSupportWale\",\"isPartOf\":{\"@id\":\"https:\/\/itsupportwale.com\/blog\/#website\"},\"datePublished\":\"2026-07-03T16:43:10+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/itsupportwale.com\/blog\/docker-best-practices-build-efficient-secure-containers\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/itsupportwale.com\/blog\/docker-best-practices-build-efficient-secure-containers\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/itsupportwale.com\/blog\/docker-best-practices-build-efficient-secure-containers\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/itsupportwale.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Docker Best Practices: Build Efficient, Secure Containers\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/itsupportwale.com\/blog\/#website\",\"url\":\"https:\/\/itsupportwale.com\/blog\/\",\"name\":\"ITSupportWale\",\"description\":\"Tips, Tricks, Fixed-Errors, Tutorials &amp; Guides\",\"publisher\":{\"@id\":\"https:\/\/itsupportwale.com\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/itsupportwale.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/itsupportwale.com\/blog\/#organization\",\"name\":\"itsupportwale\",\"url\":\"https:\/\/itsupportwale.com\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/itsupportwale.com\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/itsupportwale.com\/blog\/wp-content\/uploads\/2023\/09\/cropped-Logo-trans-without-slogan.png\",\"contentUrl\":\"https:\/\/itsupportwale.com\/blog\/wp-content\/uploads\/2023\/09\/cropped-Logo-trans-without-slogan.png\",\"width\":1119,\"height\":144,\"caption\":\"itsupportwale\"},\"image\":{\"@id\":\"https:\/\/itsupportwale.com\/blog\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/www.facebook.com\/Itsupportwale-298547177495978\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/itsupportwale.com\/blog\/#\/schema\/person\/8c5a2b3d36396e0a8fd91ec8242fd46d\",\"name\":\"Techie\",\"sameAs\":[\"https:\/\/itsupportwale.com\",\"iswblogadmin\"],\"url\":\"https:\/\/itsupportwale.com\/blog\/author\/iswblogadmin\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Docker Best Practices: Build Efficient, Secure Containers - ITSupportWale","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:\/\/itsupportwale.com\/blog\/docker-best-practices-build-efficient-secure-containers\/","og_locale":"en_US","og_type":"article","og_title":"Docker Best Practices: Build Efficient, Secure Containers - ITSupportWale","og_description":"I just ran docker images on the &#8220;Hello World&#8221; container you pushed to the registry. REPOSITORY TAG IMAGE ID CREATED SIZE hello-world-app latest f3a2b1c0d9e8 2 minutes ago 2.14GB Two. Gigabytes. For a Python script that prints &#8220;Hello World&#8221; and exits. In 1994, I was managing entire SunOS clusters on less disk space than you\u2019ve wasted ... Read more","og_url":"https:\/\/itsupportwale.com\/blog\/docker-best-practices-build-efficient-secure-containers\/","og_site_name":"ITSupportWale","article_publisher":"https:\/\/www.facebook.com\/Itsupportwale-298547177495978","article_published_time":"2026-07-03T16:43:10+00:00","og_image":[{"width":512,"height":512,"url":"https:\/\/itsupportwale.com\/blog\/wp-content\/uploads\/2021\/05\/android-chrome-512x512-1.png","type":"image\/png"}],"author":"Techie","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Techie","Est. reading time":"10 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/itsupportwale.com\/blog\/docker-best-practices-build-efficient-secure-containers\/#article","isPartOf":{"@id":"https:\/\/itsupportwale.com\/blog\/docker-best-practices-build-efficient-secure-containers\/"},"author":{"name":"Techie","@id":"https:\/\/itsupportwale.com\/blog\/#\/schema\/person\/8c5a2b3d36396e0a8fd91ec8242fd46d"},"headline":"Docker Best Practices: Build Efficient, Secure Containers","datePublished":"2026-07-03T16:43:10+00:00","mainEntityOfPage":{"@id":"https:\/\/itsupportwale.com\/blog\/docker-best-practices-build-efficient-secure-containers\/"},"wordCount":1718,"commentCount":0,"publisher":{"@id":"https:\/\/itsupportwale.com\/blog\/#organization"},"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/itsupportwale.com\/blog\/docker-best-practices-build-efficient-secure-containers\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/itsupportwale.com\/blog\/docker-best-practices-build-efficient-secure-containers\/","url":"https:\/\/itsupportwale.com\/blog\/docker-best-practices-build-efficient-secure-containers\/","name":"Docker Best Practices: Build Efficient, Secure Containers - ITSupportWale","isPartOf":{"@id":"https:\/\/itsupportwale.com\/blog\/#website"},"datePublished":"2026-07-03T16:43:10+00:00","breadcrumb":{"@id":"https:\/\/itsupportwale.com\/blog\/docker-best-practices-build-efficient-secure-containers\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/itsupportwale.com\/blog\/docker-best-practices-build-efficient-secure-containers\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/itsupportwale.com\/blog\/docker-best-practices-build-efficient-secure-containers\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/itsupportwale.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Docker Best Practices: Build Efficient, Secure Containers"}]},{"@type":"WebSite","@id":"https:\/\/itsupportwale.com\/blog\/#website","url":"https:\/\/itsupportwale.com\/blog\/","name":"ITSupportWale","description":"Tips, Tricks, Fixed-Errors, Tutorials &amp; Guides","publisher":{"@id":"https:\/\/itsupportwale.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/itsupportwale.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/itsupportwale.com\/blog\/#organization","name":"itsupportwale","url":"https:\/\/itsupportwale.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/itsupportwale.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/itsupportwale.com\/blog\/wp-content\/uploads\/2023\/09\/cropped-Logo-trans-without-slogan.png","contentUrl":"https:\/\/itsupportwale.com\/blog\/wp-content\/uploads\/2023\/09\/cropped-Logo-trans-without-slogan.png","width":1119,"height":144,"caption":"itsupportwale"},"image":{"@id":"https:\/\/itsupportwale.com\/blog\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/Itsupportwale-298547177495978"]},{"@type":"Person","@id":"https:\/\/itsupportwale.com\/blog\/#\/schema\/person\/8c5a2b3d36396e0a8fd91ec8242fd46d","name":"Techie","sameAs":["https:\/\/itsupportwale.com","iswblogadmin"],"url":"https:\/\/itsupportwale.com\/blog\/author\/iswblogadmin\/"}]}},"_links":{"self":[{"href":"https:\/\/itsupportwale.com\/blog\/wp-json\/wp\/v2\/posts\/4828","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/itsupportwale.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/itsupportwale.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/itsupportwale.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/itsupportwale.com\/blog\/wp-json\/wp\/v2\/comments?post=4828"}],"version-history":[{"count":0,"href":"https:\/\/itsupportwale.com\/blog\/wp-json\/wp\/v2\/posts\/4828\/revisions"}],"wp:attachment":[{"href":"https:\/\/itsupportwale.com\/blog\/wp-json\/wp\/v2\/media?parent=4828"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/itsupportwale.com\/blog\/wp-json\/wp\/v2\/categories?post=4828"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/itsupportwale.com\/blog\/wp-json\/wp\/v2\/tags?post=4828"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}