Container Hardening Through Distroless Images and Minimal Runtimes
A few years ago, I was helping a team prepare a production workload for a security assessment.
The application itself wasn't particularly complex.
A small API.
A handful of dependencies.
Nothing unusual.
When the vulnerability scan finished, the report contained hundreds of findings.
At first glance it looked catastrophic.
Developers immediately assumed the application dependencies were vulnerable.
But when we started investigating, something surprising emerged.
Most findings had nothing to do with the application.
They came from software the application wasn't even using.
Things like:
- Bash
- Package managers
- Linux utilities
- System libraries
- OS packages
The application needed none of them.
Yet every one of them had become part of the attack surface.
The problem wasn't the code.
The problem was everything surrounding the code.
That experience fundamentally changed how I think about containers.
Many teams spend enormous effort securing applications while shipping containers full of software that production never needed in the first place.
This article explores one of the simplest yet most effective DevSecOps principles:
Security often improves when you remove things.
We'll look at:
- Why most containers contain unnecessary software
- How shells and package managers increase attack surface
- Why distroless images are becoming increasingly popular
- Operational trade-offs nobody talks about
- Practical ways to reduce container risk without sacrificing reliability
Containers Slowly Become Tiny Servers
Most containers start small.
Then reality happens.
Someone installs curl for debugging.
Someone adds bash for troubleshooting.
Someone needs apt during a build.
Another engineer adds networking tools.
A few months later the container contains half a Linux distribution.
It still runs the same application.
But it now carries significantly more software than required.
I've seen production containers include:
- vim
- curl
- wget
- apt
- bash
- package repositories
- debugging utilities
- development libraries
None of which were necessary for serving production traffic.
The application was only using:
Runtime
Application
Dependencies
Everything else was operational baggage.
The Hidden Cost of "Just One More Tool"
Every package introduces consequences.
Not immediately.
But eventually.
Additional software means:
- More vulnerabilities
- More CVEs
- Larger SBOMs
- More patching effort
- More scanning time
- More attack surface
Security teams discover this quickly.
The vulnerability scanner doesn't care whether a package is being used.
If it's present, it's evaluated.
If it's vulnerable, it becomes your problem.
This is why image hardening isn't just about reducing image size.
It's about reducing unnecessary software exposure.
Let's Talk About Bash
Whenever I discuss container hardening, one question appears almost immediately:
"What's wrong with Bash?"
The answer is:
Nothing.
Bash is incredibly useful.
The issue is whether production actually needs it.
Many containers include Bash simply because the base image includes Bash.
Not because the application requires it.
That distinction matters.
An Attacker Loves Helpful Tools
Imagine an attacker somehow gains command execution inside a container.
What would they prefer?
Container A:
Application only
or
Container B:
bash
curl
wget
apt
network tools
system utilities
The answer is obvious.
Every additional utility provides capabilities.
Capabilities can become opportunities.
This is why security engineers often talk about:
Reducing attack surface.
The fewer tools available, the fewer options available to an attacker.
Security Through Removal
Many security initiatives focus on adding controls.
More scanners.
More policies.
More monitoring.
Those are valuable.
But one of the most effective security improvements is simply removing software.
Software cannot be exploited if it doesn't exist.
This idea isn't new.
It aligns perfectly with classic security principles:
- Least privilege
- Least functionality
- Minimized attack surface
Containers give us an excellent opportunity to apply these principles directly.
The Full Linux Distribution Problem
Many teams begin with images like:
FROM ubuntu
or
FROM debian
because they're familiar.
There's nothing inherently wrong with that.
The challenge is that production often inherits an enormous amount of software it never uses.
A typical full distribution includes:
- Package managers
- Shells
- Core utilities
- Service management tools
- Libraries
- Networking utilities
Applications rarely need most of these.
Yet they all become part of the image.
Smaller Images Usually Mean Fewer Problems
One thing I've consistently observed:
Smaller runtime images tend to be easier to secure.
Not because size itself is secure.
Because smaller images typically contain fewer components.
Fewer components generally mean:
- Fewer vulnerabilities
- Less maintenance
- Simpler compliance reviews
- Faster scans
This is where distroless images become extremely interesting.
Distroless Images Change The Question
Traditional container images ask:
What operating system should I run?
Distroless images ask:
What does the application actually require?
That's a fundamentally different mindset.
What Is A Distroless Image?
A distroless image contains only the minimum components required to run an application.
Typically:
- Runtime
- Essential libraries
- Application
And intentionally excludes:
- Shells
- Bash
- Package managers
- Most operating system utilities
The goal is simple:
Remove everything that isn't required.
Distroless In Practice
A traditional runtime might look like:
FROM node:22-slim
COPY dist/ /app
CMD ["node","server.js"]
A distroless runtime might look like:
FROM gcr.io/distroless/nodejs22
COPY dist/ /app
WORKDIR /app
CMD ["server.js"]
Notice what's missing.
No shell.
No package manager.
No operating system tooling.
Only what's needed to execute the application.
Vulnerability Reports Start Looking Different
One of the first things teams notice after adopting distroless images is vulnerability scan results.
The application vulnerabilities remain.
Distroless doesn't magically fix those.
However operating system findings often drop significantly because many of the packages generating findings no longer exist.
This creates:
- Cleaner reports
- Less remediation work
- Better signal-to-noise ratio
Security teams tend to appreciate this quickly.
The Trade-Off Nobody Mentions
Distroless images are not free.
They solve one set of problems while introducing another.
The biggest challenge?
Debugging.
You cannot do this:
docker exec -it container bash
Because Bash isn't there.
You cannot install tools on demand.
You cannot troubleshoot the way many engineers are accustomed to.
For some teams, this feels uncomfortable initially.
Distroless Forces Better Operations
Interestingly, this limitation often produces positive outcomes.
Teams become less dependent on logging into containers.
Instead they invest in:
- Better logs
- Better metrics
- Better tracing
- Better observability
In mature environments this is generally preferable.
Production debugging should happen through observability platforms, not interactive shell sessions.
Distroless nudges teams in that direction.
When Distroless Makes Sense
Distroless is usually a strong fit for:
- APIs
- Microservices
- Stateless workloads
- Kubernetes workloads
- Production services
Especially when:
- Monitoring is mature
- Logging is centralized
- Tracing exists
- Teams rarely need interactive access
When Distroless Might Be A Bad Idea
Not every workload should be distroless.
Examples include:
- Legacy applications
- Frequent manual debugging
- Operationally immature environments
- Applications requiring shell-based startup logic
Engineering always involves trade-offs.
The goal isn't maximum minimalism.
The goal is appropriate minimalism.
Secure Containers Are Often Boring Containers
One of the most valuable lessons in platform engineering is that secure systems are usually uneventful.
They contain fewer moving parts.
Fewer surprises.
Fewer packages.
Fewer opportunities for things to go wrong.
Distroless images embrace that philosophy.
They remove the assumption that every container should behave like a tiny Linux server.
Instead, they treat containers as application delivery vehicles.
And in modern cloud-native environments, that's usually exactly what they should be.



Top comments (0)