Docker Hardened Images Series — Blog 1
What We Are Building
By the end of this blog you will understand the real problem with typical container images, know what a Docker Hardened Image (DHI) actually is, authenticate to dhi.io, pull your first hardened image, run it, and compare it side-by-side with a normal official image.
You will leave with a clear “why” and a working first image.
Why This Matters
Most teams still start with official images like python:3.13 or node:22. These images are convenient, but they usually contain:
- Many packages you never use
- Shells, package managers and debugging tools
- Known CVEs that keep appearing in scans
- A larger attack surface than your application needs
When a vulnerability is disclosed, you spend time triaging, rebuilding and redeploying — often for packages that should never have been in the runtime image in the first place.
A hardened image is deliberately minimised and continuously maintained so that the number of known vulnerabilities stays near zero and the attack surface is dramatically smaller.
Docker Hardened Images (DHI) are Docker’s official implementation of this idea. The Community tier is free and open under Apache 2.0.
What You Should Know Before Starting
- Basic Docker knowledge (
docker pull,docker run,docker images) - A free Docker account
- Docker Desktop or Docker Engine with the Scout CLI plugin (comes with Docker Desktop)
Important Distinctions (Do Not Confuse These)
| Term | What it means | Automatically secure? |
|---|---|---|
| Small / Slim | Fewer packages, smaller size | No |
| Minimal | Only what is needed to run | Better, still not enough |
| Distroless | No shell, no package manager | Good reduction |
| Hardened (DHI) | Minimal + continuous CVE patching + signed SBOM + SLSA provenance + non-root by default | Closest to “secure by default” |
Smaller does not automatically mean secure. Hardening is about continuous maintenance, provenance and reduced attack surface together.
Before vs After — Attack Surface
Explanation
Left side shows a typical official image full of extra tools and packages.
Right side shows a hardened image that keeps only what is required to run the application. Fewer components = fewer places for vulnerabilities.
Step 1 — See the Problem Yourself
Pull a normal official image and check its size and vulnerabilities (example with Python):
docker pull python:3.13
docker images python:3.13
Output
➜ dhi docker pull python:3.13
3.13: Pulling from library/python
018e5aeb5455: Pull complete
fcff83b0426f: Pull complete
6e8333d59c37: Pull complete
50fe3346a36b: Pull complete
cecdaccf3d35: Pull complete
7c92d7371fba: Pull complete
4d3df26e3af2: Pull complete
ae85bf126208: Download complete
1635e0a2f722: Download complete
Digest: sha256:e16ab55c341bfd0e7da665bc2d48939cff890b43a41867fe6e1f0690638ceb7c
Status: Downloaded newer image for python:3.13
docker.io/library/python:3.13
What's next:
View a summary of image vulnerabilities and recommendations → docker scout quickview python:3.13
➜ dhi
➜ dhi docker images python:3.13
i Info → U In Use
IMAGE ID DISK USAGE CONTENT SIZE EXTRA
python:3.13 e16ab55c341b 1.62GB 420MB
➜ dhi
If you have Docker Scout:
docker scout cves python:3.13 --platform linux/amd64
Docker scout Output
(It's too lengthy output added the main summary for getting context)
313 vulnerabilities found in 52 packages
CRITICAL 3
HIGH 42
MEDIUM 22
LOW 199
UNSPECIFIED 47
What's next:
View base image update recommendations → docker scout recommendations python:3.13
➜ dhi
You will usually see a long list of CVEs and a relatively large image size. Note the numbers — we will compare them soon.
Step 2 — Browse the Hardened Images Catalog
Open the official catalog:
https://hub.docker.com/hardened-images/catalog
Search for python, node, golang, nginx, etc.
You will see variants (Debian/Alpine, runtime/dev, etc.) and security information.
Step 3 — Authenticate to dhi.io
Community images live on the dhi.io registry and require authentication (same Docker account you already have).
docker login dhi.io
Output
➜ dhi docker login dhi.io
Authenticating with existing credentials... [Username: vellankikoti]
i Info → To login with a different account, run 'docker logout' followed by 'docker login'
Login Succeeded
➜ dhi
Use your Docker Hub username and password (or a personal access token).
Step 4 — Pull and Run Your First DHI
docker pull dhi.io/python:3.13
Output
➜ dhi docker pull dhi.io/python:3.13
3.13: Pulling from python
3969c08c9319: Pull complete
a3577369b942: Pull complete
267158a60674: Pull complete
26a837766441: Pull complete
61947a7c3e2f: Pull complete
Digest: sha256:45c7b8b9a4edeb7b41ee8dfb1e902dd37782e9d78bef53ae31d01bcb6259b7bf
Status: Downloaded newer image for dhi.io/python:3.13
dhi.io/python:3.13
What's next:
View a summary of image vulnerabilities and recommendations → docker scout quickview dhi.io/python:3.13
➜ dhi
Run a simple command:
docker run --rm dhi.io/python:3.13 python -c "print('Hello from Docker Hardened Image')"
Output
➜ dhi docker run --rm dhi.io/python:3.13 python -c "print('Hello from Docker Hardened Image')"
Hello from Docker Hardened Image
Notice that many DHI runtime images are intentionally minimal — they may not contain a full shell or package manager. That is by design.
Check the size:
docker images dhi.io/python:3.13
➜ dhi docker images dhi.io/python:3.13
i Info → U In Use
IMAGE ID DISK USAGE CONTENT SIZE EXTRA
dhi.io/python:3.13 45c7b8b9a4ed 148MB 26MB
➜ dhi docker images python:3.13
i Info → U In Use
IMAGE ID DISK USAGE CONTENT SIZE EXTRA
python:3.13 e16ab55c341b 1.62GB 420MB
➜ dhi
Step 5 — Compare Side-by-Side with Docker Scout
docker scout compare dhi.io/python:3.13 \
--to python:3.13 \
--platform linux/amd64 \
--ignore-unchanged
Look at the Overview section. You will typically see:
- Large reduction in image size
- Many (sometimes almost all) CVEs removed
- Clear difference in the number of packages
Your exact numbers will vary by tag and date, but the improvement is usually dramatic.
Comparison Flow
Explanation
Scout analyses both images and shows the concrete differences in size, packages and known vulnerabilities. This is the fastest way to see the value of a hardened image.
What Just Happened Internally
- You authenticated to the dedicated DHI registry (
dhi.io) - You pulled a continuously maintained, minimal image built by Docker
- The image is designed to run as non-root by default
- It comes with signed SBOMs, SLSA Build Level 3 provenance and VEX data (we will inspect these in later blogs)
- The comparison proves the reduction in attack surface with real numbers
Let’s Break It (Gently)
Try to run an interactive shell on a minimal runtime image:
docker run -it --rm dhi.io/python:3.13 bash
Output
➜ dhi docker run -it --rm dhi.io/python:3.13 bash
What's next:
Debug this container error with Gordon → docker ai "help me fix this container error"
docker: Error response from daemon: failed to create task for container: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: error during container init: exec: "bash": executable file not found in $PATH
Run 'docker run --help' for more information
➜ dhi
It will often fail because many runtime variants intentionally do not include a shell.
That is expected. Use a -dev variant when you need a shell for building, and keep the runtime image minimal.
Production Thinking
In a real project I would:
- Start every new service with a DHI base image from day one
- Use multi-stage builds (dev variant for build, runtime variant for final image)
- Treat the Scout comparison as part of the PR checklist
- Prefer Community tier first; move to Select/Enterprise only when I need FIPS, SLA or customisation
Security Considerations
- Near-zero known CVEs is a strong starting point, but it is not a complete security programme
- A hardened base image does not make insecure application code safe
- Always still scan your final application image
- Non-root by default removes a whole class of privilege-escalation issues
- Smaller attack surface means fewer packages that can be exploited
Common Mistakes
- Expecting every DHI to have a shell and package manager
- Forgetting
docker login dhi.io - Comparing images without specifying
--platform - Thinking “I pulled a small image, so I am secure”
- Putting secrets or unnecessary tools into the final runtime image
Troubleshooting
| Problem | Check | Fix |
|---|---|---|
unauthorized when pulling |
docker login dhi.io |
Log in again with Docker Hub credentials |
| Image has no shell | You pulled a runtime variant | Use a -dev variant for build stages |
| Scout compare fails | Scout plugin / login | Ensure Docker Desktop + logged in |
| Numbers look different | Tag or platform | Always pin platform and compare same tags |
Cleanup
docker rmi dhi.io/python:3.13 python:3.13 2>/dev/null || true
What We Learned
- Typical official images carry unnecessary packages and CVEs
- Docker Hardened Images are minimal, continuously patched, non-root by default, and come with strong supply-chain metadata
- Community tier is free; you only need
docker login dhi.io - A simple Scout comparison immediately shows the practical benefit
You can now pull and run hardened images with confidence.
What’s Next?
In Blog 2 we look inside a hardened image (layers, distroless nature, non-root user) and start building real application images on top of DHI bases using multi-stage Dockerfiles.
References
- https://docs.docker.com/dhi/ — official overview and tiers
- https://docs.docker.com/dhi/get-started/ — quickstart
- https://docs.docker.com/dhi/explore/what/ — what hardened images are
- https://docs.docker.com/dhi/how-to/use/ — authentication and basic usage
- https://hub.docker.com/hardened-images/catalog — public catalog
All commands and behaviour verified against current official documentation (August 2026).




Top comments (0)