← Back to Blog
Blog Post5 min read

How we eliminated 80% of container vulnerabilities without patching a single one

Urmila Khichada, Lead AppSec Engineer, Madhuprasad B M, Lead Security EngineerJune 24, 2026

Container security had become a treadmill. Every week we ran the scanner, sorted through the results, patched what we could, and pushed the rest into the next sprint. We kept at it for years.

Our production fleet covered dozens of services in TypeScript, Go, Python, and Ruby. Each team had picked a base image when they first set things up, mostly Debian and Alpine variants. Those images ran fine, but they shipped with system packages our applications never used, things like package managers, compression libraries, network utilities, and parsers that nobody called at runtime but that appeared in the scanner results every week.

We asked the wrong question for years

We kept asking "how do we patch faster?" instead of "what if the vulnerable software just wasn't there?"

That led us to distroless base images, containers stripped to the bare minimum needed to run the application. They carry no package manager and no system utilities. If the app doesn't import it, it doesn't ship.

We evaluated several distroless options against four criteria: language coverage, supply chain integrity, flexibility for services needing additional system libraries, and cost at fleet scale. Our requirements included support for all three runtime languages (TypeScript, Python, and Ruby) and a controlled distribution channel for OS-layer packages rather than pulling directly from public registries. The solution we chose checked all four boxes, but took months to execute across our fleet.

What the migration actually looked like

Moving a production fleet to distroless wasn't a one-line Dockerfile change. Every service needed hands-on work.

The first step was multi-stage builds. We compiled and installed dependencies on a full OS, then shipped only the final artifacts into the runtime image.

Diagram of the multi-stage Docker build lifecycle used in the distroless migration

Debugging workflows had to adapt. With distroless images, teams had limited shell access and none of the admin utilities they were used to reaching for. Teams moved toward structured logging, distributed tracing, and ephemeral debug sidecars. Those improvements stuck around long after the migration finished.

We rolled out in waves. Lower-risk services first, then progressively more critical workloads. Each wave surfaced edge cases before they hit the services that couldn't afford surprises.

Our Application Security team drove this across the organization over several months, partnering with service owners at every step.

Measuring impact

Once the migration was complete, we measured the impact across the fleet:

MetricAfterImpact
Total CVEsThousands → Hundreds~80% reduction
OS-Level CVEsEliminatedNear zero
Image SizeOptimized~26% smaller

Those numbers mean roughly 80% of known container vulnerabilities are gone, with the OS-layer attack surface at near zero. Images are about 26% smaller, which translates to faster cold starts, faster deploys, and lower registry costs. What remains are application-level dependencies that service teams directly control.

Changing the way teams think

The numbers matter, but the lasting impact is cultural.

Before, nobody questioned the base image. Teams built on whatever was recommended or already in use, and software accumulated. When a CVE surfaced in a library no service was calling, teams patched or accepted the risk. Nobody asked why it was there.

Now, the default is minimal. New services start distroless. The conversation has flipped from "how do we patch this CVE in a library we don't use?" to "why would we ship software we don't need?"

The cultural shift compounds over time. Every new service that starts minimal is one that will never generate the OS-level noise the old fleet did.

Advice for other teams

If you're staring at a wall of container CVEs and wondering where to start:

  1. Audit what's actually running. You'll likely find that most of your base image is unused by your application. That's your opportunity.

  2. Start with one service. Get the multi-stage build right, validate the deployment pipeline, and prove it works before scaling out.

  3. Invest in observability before you start. Strong logging and tracing need to be in place first, not built alongside the migration.

  4. Don't boil the ocean. Wave-based rollouts let you learn cheaply and build organizational confidence.

  5. Multi-stage builds are harder than they look. Budget more time for the build pipeline work than you would ordinarily expect.

Instead of chasing patches, we removed the software that needed them. With the OS-layer noise gone, our teams now focus exclusively on vulnerabilities they actually own: the dependencies they chose and the code they wrote. The operational benefits add up. Smaller images mean faster deployments and lower registry costs. Fewer CVEs mean less triage time every sprint.

The hours we used to spend on OS-level noise now go toward building features that matter to our members. That's the shift worth measuring.