Safeguard
Containers

Scanning Docker Images for Vulnerabilities: How To

Knowing how to scan Docker images for vulnerabilities before they ship is the difference between catching a known CVE in CI and finding it in an incident report.

Safeguard Team
Product
5 min read

Learning how to scan Docker images for vulnerabilities is mostly about knowing what layers to look at: the base OS image, the packages installed on top of it, and the application dependencies baked into the final layer. Each layer can introduce known CVEs independently, and a scanner that only checks one of them gives you a false sense of coverage. Here's a practical workflow for scanning an image end to end, plus how to act on what comes back.

What Actually Gets Scanned in a Docker Image?

A container image is a stack of layers, and vulnerabilities can live in any of them:

  1. Base OS image — Alpine, Debian, Ubuntu, or a distroless base — carries OS packages with their own CVE history.
  2. Installed OS packages — anything added via apt-get install or apk add during the build.
  3. Language runtime and app dependencies — npm packages, Python wheels, Java JARs pulled in during the build, each with its own known-vulnerability history.
  4. Application code and configuration — secrets accidentally baked into a layer, overly permissive file permissions, or a USER root that never gets dropped.

A scanner that only checks OS packages will miss a vulnerable npm dependency, and vice versa — which is why the tools worth using explicitly claim coverage across both the OS and language ecosystem layers.

Step-by-Step: Scanning a Docker Image

1. Pick a Scanner With Ecosystem Coverage

Open source options like Trivy, Grype, and Docker Scout, plus commercial SCA and container scanning platforms, all read the image's layer manifest and cross-reference package versions against vulnerability databases (the OSV database, NVD, and vendor-specific feeds). For most teams, the practical difference between tools comes down to database freshness, false-positive rate, and how well the output integrates into existing CI.

2. Scan Locally Before Pushing

Running a scan against a locally built image, before it's pushed to a registry, catches problems at the cheapest possible point:

trivy image myapp:latest

This surfaces CVEs by severity, with the affected package, installed version, and fixed version (if one exists) for each finding.

3. Gate the Build on Severity Thresholds

Rather than blocking every finding (which produces alert fatigue fast), most teams gate builds on critical and high-severity CVEs with an available fix, and track lower-severity or no-fix-available findings for scheduled remediation instead of blocking every release.

4. Scan the Registry, Not Just the Build

New CVEs get disclosed against packages you already shipped. Scanning images already sitting in your registry — on a schedule, not just at build time — catches this class of drift, since an image that was clean last week can have a newly disclosed CVE against one of its packages today.

5. Track the Base Image Separately

Because most CVEs in a scan trace back to the base image rather than your own application code, keeping base images current (rebuilding on a new base tag regularly, not just when something breaks) resolves a large share of findings without touching application code at all.

How Do You List and Track What's Actually Deployed?

Knowing what's running matters as much as scanning it. docker images gives you a local list of images and tags; for a full inventory across a registry, most registries expose an API (Docker Hub, ECR, GCR, and private registries like Harbor all support this) to enumerate what's stored, which platforms use to reconcile "what's scanned" against "what's actually deployed."

What About Less Common Base Images?

Scanners vary in how well they support less mainstream distributions — Rocky Linux, for example, is common in regulated or enterprise environments migrating off CentOS, and not every scanner's package database has equally deep coverage for it. Before adopting a base image for a production workload, confirm your scanner actually maintains current vulnerability data for that distribution rather than assuming universal support.

FAQ

What's the difference between scanning at build time and scanning a registry?

Build-time scanning catches problems before an image ships. Registry scanning catches vulnerabilities disclosed after the image was already built and pushed — both are needed, since a clean scan at build time doesn't stay accurate forever.

Do I need separate tools for OS packages vs. app dependencies?

Not necessarily — most modern scanners (Trivy, Grype, and commercial SCA platforms) cover both in a single scan and report. Confirm ecosystem coverage before relying on any single tool for full-stack visibility.

Should every vulnerability finding block a build?

No. Blocking every finding regardless of severity creates alert fatigue and teaches developers to bypass the gate. Most teams block on critical/high severity with an available fix and track the rest for scheduled remediation.

How often should I re-scan images already in production?

On a schedule — daily or weekly is common — since new CVEs are disclosed continuously against packages that haven't changed in your image at all.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.