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:
- Base OS image — Alpine, Debian, Ubuntu, or a distroless base — carries OS packages with their own CVE history.
- Installed OS packages — anything added via
apt-get installorapk addduring the build. - Language runtime and app dependencies — npm packages, Python wheels, Java JARs pulled in during the build, each with its own known-vulnerability history.
- Application code and configuration — secrets accidentally baked into a layer, overly permissive file permissions, or a
USER rootthat 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.