{"id":4732,"date":"2026-03-12T21:36:23","date_gmt":"2026-03-12T16:06:23","guid":{"rendered":"https:\/\/itsupportwale.com\/blog\/docker-best-practices-build-faster-secure-containers\/"},"modified":"2026-03-12T21:36:23","modified_gmt":"2026-03-12T16:06:23","slug":"docker-best-practices-build-faster-secure-containers","status":"publish","type":"post","link":"https:\/\/itsupportwale.com\/blog\/docker-best-practices-build-faster-secure-containers\/","title":{"rendered":"Docker Best Practices: Build Faster, Secure Containers"},"content":{"rendered":"<p><strong>POST-MORTEM REPORT: THE DAY THE LAYERS COLLAPSED<\/strong><br \/>\n<strong>DATE:<\/strong> October 14, 2023<br \/>\n<strong>AUDITOR:<\/strong> Lead Infrastructure Engineer (Hardened Systems Division)<br \/>\n<strong>STATUS:<\/strong> CRITICAL \/ FORENSIC COMPLETE<br \/>\n<strong>INCIDENT REF:<\/strong> #882-ALPHA-FAILURE<\/p>\n<p>I\u2019ve spent the last 72 hours staring at hex dumps and cleaning up the radioactive sludge left behind by a &#8220;standard&#8221; deployment. My eyes are bloodshot, my caffeine intake has reached toxic levels, and I have lost all faith in the industry\u2019s ability to read a man page. We didn&#8217;t just have a breach; we had a total structural collapse. The stack didn&#8217;t fail\u2014the foundation was built on sand and &#8220;convenience.&#8221;<\/p>\n<p>This report is a autopsy of a dead system. If you\u2019re looking for a &#8220;seamless&#8221; guide to containerization, look elsewhere. This is about the grit, the syscalls, and the entropy of a compromised environment. We are currently running Docker Engine v25.0.3, and yet, we are still making mistakes that were solved in 2014.<\/p>\n<hr \/>\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-69d77442e7475\" 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-69d77442e7475\"  aria-label=\"Toggle\" \/><nav><ul class='ez-toc-list ez-toc-list-level-1 ' ><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-1\" href=\"https:\/\/itsupportwale.com\/blog\/docker-best-practices-build-faster-secure-containers\/#1_TIMELINE_OF_FAILURE\" >1. TIMELINE OF FAILURE<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-2\" href=\"https:\/\/itsupportwale.com\/blog\/docker-best-practices-build-faster-secure-containers\/#2_THE_ANATOMY_OF_A_DISASTER_THE_COMPROMISED_DOCKERFILE\" >2. THE ANATOMY OF A DISASTER: THE COMPROMISED DOCKERFILE<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-3\" href=\"https:\/\/itsupportwale.com\/blog\/docker-best-practices-build-faster-secure-containers\/#3_THE_ROOT_USER_SIN\" >3. THE ROOT USER SIN<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-4\" href=\"https:\/\/itsupportwale.com\/blog\/docker-best-practices-build-faster-secure-containers\/#4_THE_BLOATED_IMAGE_VULNERABILITY\" >4. THE BLOATED IMAGE VULNERABILITY<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-5\" href=\"https:\/\/itsupportwale.com\/blog\/docker-best-practices-build-faster-secure-containers\/#5_THE_SECRET_LEAKAGE_DISASTER\" >5. THE SECRET LEAKAGE DISASTER<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-6\" href=\"https:\/\/itsupportwale.com\/blog\/docker-best-practices-build-faster-secure-containers\/#6_OVERLAYFS_AND_LAYER_POLLUTION\" >6. OVERLAYFS AND LAYER POLLUTION<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-7\" href=\"https:\/\/itsupportwale.com\/blog\/docker-best-practices-build-faster-secure-containers\/#7_THE_PRIVILEGED_FLAG_AND_SYSCALL_EXPOSURE\" >7. THE PRIVILEGED FLAG AND SYSCALL EXPOSURE<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-8\" href=\"https:\/\/itsupportwale.com\/blog\/docker-best-practices-build-faster-secure-containers\/#8_NETWORKING_THE_DEFAULT_BRIDGE_TRAP\" >8. NETWORKING: THE DEFAULT BRIDGE TRAP<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-9\" href=\"https:\/\/itsupportwale.com\/blog\/docker-best-practices-build-faster-secure-containers\/#9_THE_%E2%80%9CAFTER%E2%80%9D_DOCKERFILE_HARDENED_INFRASTRUCTURE\" >9. THE &#8220;AFTER&#8221; DOCKERFILE: HARDENED INFRASTRUCTURE<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-10\" href=\"https:\/\/itsupportwale.com\/blog\/docker-best-practices-build-faster-secure-containers\/#10_VERIFICATION_THE_SCANNING_LOGS\" >10. VERIFICATION: THE SCANNING LOGS<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-11\" href=\"https:\/\/itsupportwale.com\/blog\/docker-best-practices-build-faster-secure-containers\/#11_MANDATORY_HARDENING_CHECKLIST\" >11. MANDATORY HARDENING CHECKLIST<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-12\" href=\"https:\/\/itsupportwale.com\/blog\/docker-best-practices-build-faster-secure-containers\/#Related_Articles\" >Related Articles<\/a><\/li><\/ul><\/nav><\/div>\n<h3><span class=\"ez-toc-section\" id=\"1_TIMELINE_OF_FAILURE\"><\/span>1. TIMELINE OF FAILURE<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p><strong>03:00 AM:<\/strong> Prometheus alerts fire. High CPU utilization on the <code>prod-api-04<\/code> node. Initial triage suggests a runaway worker process.<br \/>\n<strong>03:15 AM:<\/strong> Automated <code>trivy<\/code> scans on the running container environment (triggered by the spike) return a sea of red. The runtime environment is identified as compromised.<br \/>\n<strong>03:45 AM:<\/strong> Lateral movement detected. The attacker has escaped the container. They are now polling the metadata service of the cloud provider.<br \/>\n<strong>04:10 AM:<\/strong> Forensic analysis of <code>docker history<\/code> on the compromised image reveals a hardcoded <code>GITHUB_TOKEN<\/code> in layer 4.<br \/>\n<strong>04:30 AM:<\/strong> The attacker exploits CVE-2024-21626. Because the container was running as root and the <code>runc<\/code> version was unpatched on the host, they gained a file descriptor to the host\u2019s <code>\/proc\/self\/cwd<\/code>.<br \/>\n<strong>05:00 AM:<\/strong> Database credentials exfiltrated. The attacker uses the leaked token to push a malicious commit to the main branch, poisoning the CI\/CD pipeline.<br \/>\n<strong>06:00 AM:<\/strong> Kill switch engaged. Entire VPC isolated. Production is dark. The cleanup begins.<\/p>\n<hr \/>\n<h3><span class=\"ez-toc-section\" id=\"2_THE_ANATOMY_OF_A_DISASTER_THE_COMPROMISED_DOCKERFILE\"><\/span>2. THE ANATOMY OF A DISASTER: THE COMPROMISED DOCKERFILE<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>The following Dockerfile was found in the repository. It is a masterclass in how to ignore <strong>docker best<\/strong> practices. It is bloated, insecure, and fundamentally broken.<\/p>\n<pre class=\"codehilite\"><code class=\"language-dockerfile\"># THE &quot;BEFORE&quot; DOCKERFILE - ARCHITECTURAL SUICIDE\nFROM ubuntu:latest\n\n# Running as root is the default. This is a death sentence.\nWORKDIR \/app\n\n# Bloated layer: installing everything including the kitchen sink\nRUN apt-get update &amp;&amp; apt-get install -y \\\n    python3 \\\n    python3-pip \\\n    curl \\\n    vim \\\n    git \\\n    wget \\\n    build-essential\n\n# Leaking secrets into the image metadata\nENV DB_PASSWORD=&quot;super-secret-password-123&quot;\nENV API_KEY=&quot;AKIA_FAKE_LEAKED_KEY&quot;\n\nCOPY . .\n\n# Installing dependencies as root\nRUN pip3 install -r requirements.txt\n\n# Exposing a privileged port\nEXPOSE 80\n\n# No healthcheck, no signal handling\nCMD [&quot;python3&quot;, &quot;app.py&quot;]\n<\/code><\/pre>\n<hr \/>\n<h3><span class=\"ez-toc-section\" id=\"3_THE_ROOT_USER_SIN\"><\/span>3. THE ROOT USER SIN<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>The most egregious failure in this audit was the persistence of the root user. By default, Docker containers run as UID 0. In our environment, this allowed the attacker to exploit <strong>CVE-2024-21626<\/strong>. This vulnerability in <code>runc<\/code> allows an attacker to leak file descriptors. If the process is running as root, that leaked descriptor can be used to traverse the host filesystem.<\/p>\n<p>When I ran <code>docker inspect<\/code> on the compromised container, the <code>Config.User<\/code> field was empty.<\/p>\n<p><strong>Raw Terminal Output: <code>docker inspect<\/code><\/strong><\/p>\n<pre class=\"codehilite\"><code class=\"language-json\">[\n    {\n        &quot;Id&quot;: &quot;sha256:a1b2c3d4...&quot;,\n        &quot;Config&quot;: {\n            &quot;User&quot;: &quot;&quot;,\n            &quot;ExposedPorts&quot;: {\n                &quot;80\/tcp&quot;: {}\n            },\n            &quot;Env&quot;: [\n                &quot;DB_PASSWORD=super-secret-password-123&quot;,\n                &quot;PATH=\/usr\/local\/sbin:\/usr\/local\/bin:\/usr\/sbin:\/usr\/bin:\/sbin:\/bin&quot;\n            ]\n        }\n    }\n]\n<\/code><\/pre>\n<p>An empty string means root. It means the attacker has the same privileges inside the container as the kernel&#8217;s administrative user. When the attacker triggered the <code>runc<\/code> exploit, they didn&#8217;t have to fight for escalation. We handed them the keys. A hardened infrastructure requires a non-privileged user. Period.<\/p>\n<hr \/>\n<h3><span class=\"ez-toc-section\" id=\"4_THE_BLOATED_IMAGE_VULNERABILITY\"><\/span>4. THE BLOATED IMAGE VULNERABILITY<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>The &#8220;Before&#8221; image used <code>ubuntu:latest<\/code>. This is a 77MB base image (compressed) that expands to over 200MB. It includes <code>apt<\/code>, <code>bash<\/code>, and a plethora of binaries that have no business being in a production runtime. <\/p>\n<p>Every binary is a potential gadget for an attacker. By including <code>curl<\/code> and <code>wget<\/code>, we provided the attacker with the tools to download their secondary payloads. By including <code>git<\/code>, we allowed them to clone private repositories once they found the leaked token.<\/p>\n<p><strong>Raw Terminal Output: <code>trivy image<\/code> (The Horror Show)<\/strong><\/p>\n<pre class=\"codehilite\"><code class=\"language-text\">Total: 584 (UNKNOWN: 5, LOW: 142, MEDIUM: 280, HIGH: 132, CRITICAL: 25)\n\n+--------------+------------------+----------+-------------------+---------------+---------------------------------------+\n|   LIBRARY    |    VULNERABILITY | SEVERITY | INSTALLED VERSION | FIXED VERSION |                 TITLE                 |\n+--------------+------------------+----------+-------------------+---------------+---------------------------------------+\n| bash         | CVE-2022-3715    | HIGH     | 5.1-6ubuntu1      | 5.1-6ubuntu1.1| bash: a heap-based buffer overflow    |\n| coreutils    | CVE-2016-2781    | LOW      | 8.32-4.1ubuntu1   |               | coreutils: Non-privileged session can |\n| libc6        | CVE-2023-4911    | CRITICAL | 2.35-0ubuntu3.1   | 2.35-0ubuntu3.4| glibc: GHOST-like buffer overflow     |\n| libssl3      | CVE-2024-0727    | HIGH     | 3.0.2-0ubuntu1.10 | 3.0.2-0ubuntu1.15| openssl: NULL pointer dereference     |\n+--------------+------------------+----------+-------------------+---------------+---------------------------------------+\n<\/code><\/pre>\n<p>Compare this to a Distroless image or a static Alpine build. A Distroless image contains only your application and its runtime dependencies. No shell. No package manager. The attack surface drops from 584 vulnerabilities to near zero. The byte-count difference is not just about disk space; it&#8217;s about the reduction of the &#8220;Exploit Entropy.&#8221;<\/p>\n<hr \/>\n<h3><span class=\"ez-toc-section\" id=\"5_THE_SECRET_LEAKAGE_DISASTER\"><\/span>5. THE SECRET LEAKAGE DISASTER<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>The developer thought that by putting <code>ENV DB_PASSWORD<\/code> in the Dockerfile, it was &#8220;internal&#8221; to the container. They were wrong. Docker layers are additive and permanent. Even if you <code>unset<\/code> the variable in a later layer, the secret remains in the image&#8217;s history.<\/p>\n<p><strong>Raw Terminal Output: <code>docker history<\/code><\/strong><\/p>\n<pre class=\"codehilite\"><code class=\"language-text\">IMAGE          CREATED        CREATED BY                                      SIZE      COMMENT\na1b2c3d4e5f6   2 hours ago    CMD [&quot;python3&quot; &quot;app.py&quot;]                        0B        \n&lt;missing&gt;      2 hours ago    RUN \/bin\/sh -c pip3 install -r requirements.txt 45MB      \n&lt;missing&gt;      2 hours ago    COPY . .                                        1.2MB     \n&lt;missing&gt;      2 hours ago    ENV API_KEY=AKIA_FAKE_LEAKED_KEY                0B        \n&lt;missing&gt;      2 hours ago    ENV DB_PASSWORD=super-secret-password-123       0B        \n&lt;missing&gt;      2 hours ago    RUN \/bin\/sh -c apt-get update &amp;&amp; apt-get inst\u2026  150MB     \n&lt;missing&gt;      2 hours ago    WORKDIR \/app                                    0B        \n&lt;missing&gt;      3 weeks ago    \/bin\/sh -c #(nop)  CMD [&quot;\/bin\/bash&quot;]            0B        \n<\/code><\/pre>\n<p>Anyone with <code>docker pull<\/code> access to that image can run <code>docker history --no-trunc<\/code> and scrape every secret ever defined in an <code>ENV<\/code> or <code>ARG<\/code> instruction. This is how our GitHub token was leaked. Secrets must be injected at runtime via a vault or secrets manager, never baked into the image layers.<\/p>\n<hr \/>\n<h3><span class=\"ez-toc-section\" id=\"6_OVERLAYFS_AND_LAYER_POLLUTION\"><\/span>6. OVERLAYFS AND LAYER POLLUTION<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>The <code>RUN apt-get update &amp;&amp; apt-get install<\/code> command in the &#8220;Before&#8221; Dockerfile is a crime against OverlayFS. When you run a command in a Dockerfile, it creates a new layer. If you install 150MB of packages and then delete the cache in a <em>subsequent<\/em> <code>RUN<\/code> command, the 150MB still exists in the previous layer. <\/p>\n<p>OverlayFS works by stacking these layers. The &#8220;upper&#8221; layer only records the deletion as a &#8220;whiteout&#8221; file. The bytes remain on the disk. This leads to bloated images that take longer to pull, increasing the window of vulnerability during scaling events.<\/p>\n<p>Furthermore, the lack of a <code>.dockerignore<\/code> file meant the <code>.git<\/code> directory was copied into the image. This leaked the entire commit history, including deleted files that contained legacy credentials. The <code>COPY . .<\/code> command is a blunt instrument that should be replaced with specific file copies.<\/p>\n<hr \/>\n<h3><span class=\"ez-toc-section\" id=\"7_THE_PRIVILEGED_FLAG_AND_SYSCALL_EXPOSURE\"><\/span>7. THE PRIVILEGED FLAG AND SYSCALL EXPOSURE<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>During the investigation, I found that the container was started with the <code>--privileged<\/code> flag in the <code>docker-compose.yml<\/code>. This is the equivalent of giving the container a &#8220;get out of jail free&#8221; card for the Linux kernel&#8217;s security boundaries.<\/p>\n<p>A privileged container has access to all devices on the host. It can mount the host&#8217;s <code>\/dev<\/code> filesystem. It bypasses <code>AppArmor<\/code> and <code>Seccomp<\/code> profiles. <\/p>\n<p><strong>The Syscall Problem:<\/strong><br \/>\nA standard container is restricted by a Seccomp profile that filters out dangerous syscalls like <code>mount()<\/code>, <code>reboot()<\/code>, and <code>ptrace()<\/code>. By using <code>--privileged<\/code>, we allowed the attacker to use <code>mount()<\/code> to re-mount the host&#8217;s root partition as read-write inside the container. <\/p>\n<p>We need to move toward a &#8220;Least Privilege&#8221; model where we only add the specific capabilities required, such as <code>CAP_NET_BIND_SERVICE<\/code>. We don&#8217;t need <code>CAP_SYS_ADMIN<\/code> to run a Python API.<\/p>\n<hr \/>\n<h3><span class=\"ez-toc-section\" id=\"8_NETWORKING_THE_DEFAULT_BRIDGE_TRAP\"><\/span>8. NETWORKING: THE DEFAULT BRIDGE TRAP<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>The compromised service was running on the default <code>docker0<\/code> bridge. This is a flat network where every container can talk to every other container on the same host. There is no internal segmentation.<\/p>\n<p>Once the attacker gained a shell, they used <code>nmap<\/code> (which they installed because we left <code>apt<\/code> in the image) to scan the internal <code>172.17.0.0\/16<\/code> range. They found an unauthenticated Redis instance used for caching and flushed all session data, forcing a mass logout of our users.<\/p>\n<p><strong>Hardened Networking Requirement:<\/strong><br \/>\nWe must use custom bridge networks or overlay networks with encrypted data planes. We must use the <code>--internal<\/code> flag for containers that do not require outbound internet access. The default bridge is a playground for lateral movement.<\/p>\n<hr \/>\n<h3><span class=\"ez-toc-section\" id=\"9_THE_%E2%80%9CAFTER%E2%80%9D_DOCKERFILE_HARDENED_INFRASTRUCTURE\"><\/span>9. THE &#8220;AFTER&#8221; DOCKERFILE: HARDENED INFRASTRUCTURE<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>This is what a professional, hardened Dockerfile looks like. It uses multi-stage builds to ensure the final runtime image is as lean as possible. It uses a non-root user. It cleans up after itself. It follows <strong>docker best<\/strong> practices to the letter.<\/p>\n<pre class=\"codehilite\"><code class=\"language-dockerfile\"># STAGE 1: BUILDER\nFROM python:3.11-slim-bookworm AS builder\n\n# Set environment variables for Python\nENV PYTHONDONTWRITEBYTECODE=1\nENV PYTHONUNBUFFERED=1\n\nWORKDIR \/install\n\n# Install build dependencies\nRUN apt-get update &amp;&amp; apt-get install -y --no-install-recommends \\\n    build-essential \\\n    gcc \\\n    &amp;&amp; rm -rf \/var\/lib\/apt\/lists\/*\n\n# Install requirements to a local directory\nCOPY requirements.txt .\nRUN pip install --no-cache-dir --prefix=\/install -r requirements.txt\n\n# STAGE 2: RUNTIME\n# Using Google's Distroless for maximum hardening\n# No shell, no package manager, no root user\nFROM gcr.io\/distroless\/python3-debian12:nonroot\n\nWORKDIR \/app\n\n# Copy only the necessary artifacts from the builder\nCOPY --from=builder \/install \/usr\/local\nCOPY --from=builder \/usr\/lib\/x86_64-linux-gnu \/usr\/lib\/x86_64-linux-gnu\n\n# Copy application code\n# Ensure the .dockerignore excludes .git, tests, and env files\nCOPY . .\n\n# Distroless images run as a non-root user by default (uid 65532)\n# But we explicitly set it for clarity\nUSER nonroot\n\n# Use a healthcheck to ensure the container is viable\nHEALTHCHECK --interval=30s --timeout=3s \\\n  CMD [&quot;python&quot;, &quot;-c&quot;, &quot;import urllib.request; urllib.request.urlopen('http:\/\/localhost:8080\/health')&quot;]\n\n# Expose non-privileged port\nEXPOSE 8080\n\n# Run the application\nENTRYPOINT [&quot;python&quot;, &quot;app.py&quot;]\n<\/code><\/pre>\n<hr \/>\n<h3><span class=\"ez-toc-section\" id=\"10_VERIFICATION_THE_SCANNING_LOGS\"><\/span>10. VERIFICATION: THE SCANNING LOGS<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>After implementing the hardened Dockerfile, I ran a new scan. The results are the only reason I\u2019m not resigning today.<\/p>\n<p><strong>Raw Terminal Output: <code>trivy image --severity CRITICAL,HIGH hardened-api:latest<\/code><\/strong><\/p>\n<pre class=\"codehilite\"><code class=\"language-text\">hardened-api:latest (debian 12.5)\n=================================\nTotal: 0 (UNKNOWN: 0, LOW: 0, MEDIUM: 0, HIGH: 0, CRITICAL: 0)\n<\/code><\/pre>\n<p><strong>Raw Terminal Output: <code>docker history hardened-api:latest<\/code><\/strong><\/p>\n<pre class=\"codehilite\"><code class=\"language-text\">IMAGE          CREATED        CREATED BY                                      SIZE      COMMENT\nf1e2d3c4b5a6   10 mins ago    ENTRYPOINT [&quot;python&quot; &quot;app.py&quot;]                  0B        \n&lt;missing&gt;      10 mins ago    COPY . .                                        450KB     \n&lt;missing&gt;      10 mins ago    COPY --from=builder \/usr\/lib\/x86_64-linux-gnu\u2026 12MB      \n&lt;missing&gt;      10 mins ago    COPY --from=builder \/install \/usr\/local         28MB      \n&lt;missing&gt;      10 mins ago    USER nonroot                                    0B        \n&lt;missing&gt;      2 weeks ago    ... (Distroless Base Layers)                    18MB      \n<\/code><\/pre>\n<p>The image size dropped from 245MB to 58MB. The vulnerability count dropped from 584 to 0. The secrets are gone. The shell is gone. The root user is gone.<\/p>\n<hr \/>\n<h3><span class=\"ez-toc-section\" id=\"11_MANDATORY_HARDENING_CHECKLIST\"><\/span>11. MANDATORY HARDENING CHECKLIST<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Effective immediately, no container will be deployed to production unless it passes the following forensic audit requirements. Failure to comply will be treated as a deliberate security bypass.<\/p>\n<ol>\n<li><strong>NO ROOT USERS:<\/strong> Every Dockerfile must contain a <code>USER<\/code> instruction with a non-zero UID. If the application requires a privileged port, use <code>setcap<\/code> or map the port at the runtime level (e.g., 8080:80).<\/li>\n<li><strong>MULTI-STAGE BUILDS ONLY:<\/strong> Compilers, build-tools, and header files are forbidden in the final runtime image. Use multi-stage builds to copy only the necessary artifacts.<\/li>\n<li><strong>DISTROLESS OR MINIMAL ALPINES:<\/strong> Use <code>gcr.io\/distroless<\/code> for languages like Python, Node, and Java. Use <code>scratch<\/code> for Go binaries. If you must use a general-purpose OS, use Alpine Linux and keep it updated.<\/li>\n<li><strong>NO SECRETS IN LAYERS:<\/strong> Use <code>docker-compose<\/code> secrets, Kubernetes Secrets, or a cloud-native Vault. Any image found with an <code>ENV<\/code> variable containing a credential will be purged and the developer&#8217;s access revoked.<\/li>\n<li><strong>IMMUTABLE TAGS:<\/strong> <code>latest<\/code> is not a version. It is a rolling disaster. All <code>FROM<\/code> instructions must use specific version tags or, preferably, SHA256 digests.<\/li>\n<li><strong>READ-ONLY FILESYSTEMS:<\/strong> Containers should be run with <code>--read-only<\/code>. Any required writable areas (like <code>\/tmp<\/code>) must be mounted as <code>tmpfs<\/code> volumes.<\/li>\n<li><strong>RESOURCE CONSTRAINTS:<\/strong> Every container must have <code>cpus<\/code> and <code>memory<\/code> limits defined. This prevents a single compromised container from performing a DoS attack on the entire host via resource exhaustion.<\/li>\n<li><strong>NO PRIVILEGED CONTAINERS:<\/strong> The <code>--privileged<\/code> flag is banned. If you think you need it, you are wrong. Use specific <code>--cap-add<\/code> flags for the minimum required capabilities.<\/li>\n<li><strong>SCANNING INTEGRATION:<\/strong> <code>trivy<\/code> or <code>snyk<\/code> must block the CI\/CD pipeline if any HIGH or CRITICAL vulnerabilities are found.<\/li>\n<li><strong>NETWORK ISOLATION:<\/strong> Production containers must run on a dedicated overlay network with <code>com.docker.network.bridge.enable_icc: \"false\"<\/code> to prevent inter-container communication unless explicitly allowed.<\/li>\n<\/ol>\n<p>The layers collapsed because we were lazy. We prioritized the speed of &#8220;it works on my machine&#8221; over the stability of &#8220;it is secure in production.&#8221; This ends now. The infrastructure is being rebuilt. The entropy is being purged. <\/p>\n<p><strong>Audit Signed:<\/strong><br \/>\n<em>The Hardened Infrastructure Auditor<\/em><br \/>\n<em>06:45 AM &#8211; Post-Breach Recovery Day 3<\/em><\/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\/mastering-react-development-best-practices-for-2024\/\">Mastering React Development Best Practices For 2024<\/a><\/li>\n<li><a href=\"https:\/\/itsupportwale.com\/blog\/whatsapp-dark-theme-live-for-beta-devices\/\">Whatsapp Dark Theme Live For Beta Devices<\/a><\/li>\n<li><a href=\"https:\/\/itsupportwale.com\/blog\/10-react-best-practices-for-high-performance-apps\/\">10 React Best Practices For High Performance Apps<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>POST-MORTEM REPORT: THE DAY THE LAYERS COLLAPSED DATE: October 14, 2023 AUDITOR: Lead Infrastructure Engineer (Hardened Systems Division) STATUS: CRITICAL \/ FORENSIC COMPLETE INCIDENT REF: #882-ALPHA-FAILURE I\u2019ve spent the last 72 hours staring at hex dumps and cleaning up the radioactive sludge left behind by a &#8220;standard&#8221; deployment. My eyes are bloodshot, my caffeine intake &#8230; <a title=\"Docker Best Practices: Build Faster, Secure Containers\" class=\"read-more\" href=\"https:\/\/itsupportwale.com\/blog\/docker-best-practices-build-faster-secure-containers\/\" aria-label=\"Read more  on Docker Best Practices: Build Faster, 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-4732","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 Faster, 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-faster-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 Faster, Secure Containers - ITSupportWale\" \/>\n<meta property=\"og:description\" content=\"POST-MORTEM REPORT: THE DAY THE LAYERS COLLAPSED DATE: October 14, 2023 AUDITOR: Lead Infrastructure Engineer (Hardened Systems Division) STATUS: CRITICAL \/ FORENSIC COMPLETE INCIDENT REF: #882-ALPHA-FAILURE I\u2019ve spent the last 72 hours staring at hex dumps and cleaning up the radioactive sludge left behind by a &#8220;standard&#8221; deployment. My eyes are bloodshot, my caffeine intake ... Read more\" \/>\n<meta property=\"og:url\" content=\"https:\/\/itsupportwale.com\/blog\/docker-best-practices-build-faster-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-03-12T16:06:23+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=\"11 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-faster-secure-containers\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/itsupportwale.com\/blog\/docker-best-practices-build-faster-secure-containers\/\"},\"author\":{\"name\":\"Techie\",\"@id\":\"https:\/\/itsupportwale.com\/blog\/#\/schema\/person\/8c5a2b3d36396e0a8fd91ec8242fd46d\"},\"headline\":\"Docker Best Practices: Build Faster, Secure Containers\",\"datePublished\":\"2026-03-12T16:06:23+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/itsupportwale.com\/blog\/docker-best-practices-build-faster-secure-containers\/\"},\"wordCount\":1530,\"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-faster-secure-containers\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/itsupportwale.com\/blog\/docker-best-practices-build-faster-secure-containers\/\",\"url\":\"https:\/\/itsupportwale.com\/blog\/docker-best-practices-build-faster-secure-containers\/\",\"name\":\"Docker Best Practices: Build Faster, Secure Containers - ITSupportWale\",\"isPartOf\":{\"@id\":\"https:\/\/itsupportwale.com\/blog\/#website\"},\"datePublished\":\"2026-03-12T16:06:23+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/itsupportwale.com\/blog\/docker-best-practices-build-faster-secure-containers\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/itsupportwale.com\/blog\/docker-best-practices-build-faster-secure-containers\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/itsupportwale.com\/blog\/docker-best-practices-build-faster-secure-containers\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/itsupportwale.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Docker Best Practices: Build Faster, 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 Faster, 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-faster-secure-containers\/","og_locale":"en_US","og_type":"article","og_title":"Docker Best Practices: Build Faster, Secure Containers - ITSupportWale","og_description":"POST-MORTEM REPORT: THE DAY THE LAYERS COLLAPSED DATE: October 14, 2023 AUDITOR: Lead Infrastructure Engineer (Hardened Systems Division) STATUS: CRITICAL \/ FORENSIC COMPLETE INCIDENT REF: #882-ALPHA-FAILURE I\u2019ve spent the last 72 hours staring at hex dumps and cleaning up the radioactive sludge left behind by a &#8220;standard&#8221; deployment. My eyes are bloodshot, my caffeine intake ... Read more","og_url":"https:\/\/itsupportwale.com\/blog\/docker-best-practices-build-faster-secure-containers\/","og_site_name":"ITSupportWale","article_publisher":"https:\/\/www.facebook.com\/Itsupportwale-298547177495978","article_published_time":"2026-03-12T16:06:23+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":"11 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/itsupportwale.com\/blog\/docker-best-practices-build-faster-secure-containers\/#article","isPartOf":{"@id":"https:\/\/itsupportwale.com\/blog\/docker-best-practices-build-faster-secure-containers\/"},"author":{"name":"Techie","@id":"https:\/\/itsupportwale.com\/blog\/#\/schema\/person\/8c5a2b3d36396e0a8fd91ec8242fd46d"},"headline":"Docker Best Practices: Build Faster, Secure Containers","datePublished":"2026-03-12T16:06:23+00:00","mainEntityOfPage":{"@id":"https:\/\/itsupportwale.com\/blog\/docker-best-practices-build-faster-secure-containers\/"},"wordCount":1530,"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-faster-secure-containers\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/itsupportwale.com\/blog\/docker-best-practices-build-faster-secure-containers\/","url":"https:\/\/itsupportwale.com\/blog\/docker-best-practices-build-faster-secure-containers\/","name":"Docker Best Practices: Build Faster, Secure Containers - ITSupportWale","isPartOf":{"@id":"https:\/\/itsupportwale.com\/blog\/#website"},"datePublished":"2026-03-12T16:06:23+00:00","breadcrumb":{"@id":"https:\/\/itsupportwale.com\/blog\/docker-best-practices-build-faster-secure-containers\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/itsupportwale.com\/blog\/docker-best-practices-build-faster-secure-containers\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/itsupportwale.com\/blog\/docker-best-practices-build-faster-secure-containers\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/itsupportwale.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Docker Best Practices: Build Faster, 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\/4732","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=4732"}],"version-history":[{"count":0,"href":"https:\/\/itsupportwale.com\/blog\/wp-json\/wp\/v2\/posts\/4732\/revisions"}],"wp:attachment":[{"href":"https:\/\/itsupportwale.com\/blog\/wp-json\/wp\/v2\/media?parent=4732"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/itsupportwale.com\/blog\/wp-json\/wp\/v2\/categories?post=4732"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/itsupportwale.com\/blog\/wp-json\/wp\/v2\/tags?post=4732"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}