How to Scan a Docker Image for Vulnerabilities (4 Ways)
Published June 3, 2026 · 12 min read
Every Docker image you build inherits hundreds — sometimes thousands — of packages from its base image and dependencies. Many of them ship with known vulnerabilities. This guide shows you four practical ways to scan a Docker image for vulnerabilities, how to read what the scanner reports, and how to actually fix the findings instead of drowning in a wall of CVEs.
What “scanning a Docker image” actually means
A Docker image is a stack of filesystem layers. A vulnerability scanner unpacks those layers and builds an inventory of everything installed, then checks each item against one or more vulnerability databases. There are two distinct categories it inventories:
- OS packages — read from the package manager's own database:
/lib/apk/db/installed(Alpine),/var/lib/dpkg/status(Debian/Ubuntu), or the RPM database (RHEL/Fedora). - Application dependencies— language ecosystems like npm (
package-lock.json), pip, Go modules, Ruby gems, and Java JARs found inside the image.
The scanner then matches each package and version against vulnerability data and emits a list of findings: a CVE identifier, the affected package, a severity, and — ideally — the version that fixes it. If you want the deeper background on where that CVE data comes from, see our comparison of NVD, OSV, GHSA, and Snyk Intel.
Method 1 — Docker Scout (built in)
If you already have Docker Desktop or a recent Docker CLI, you have a scanner installed. Docker Scout reads your local image and reports CVEs with no extra tooling:
# Quick overview of an image docker scout quickview my-image:tag # Full CVE list, filtered to High and Critical docker scout cves my-image:tag --only-severity critical,high
Scout is the lowest-friction option for a quick local check. Its weakness is depth: it is strongest on OS packages and the most common ecosystems, and it is tied to the Docker tooling. For a thorough audit you will usually reach for one of the dedicated scanners below.
Method 2 — Trivy
Trivy is the most widely used open-source image scanner. Install it, then point it at a local image, a remote image, or a saved tarball:
# Scan a local or remote image by name trivy image my-image:tag # Scan an exported tarball (no daemon needed) trivy image --input my-image.tar # Fail a CI build on High/Critical findings trivy image --exit-code 1 --severity HIGH,CRITICAL my-image:tag
Trivy is fast and covers OS packages plus most language ecosystems. The --exit-code 1 flag is the one to remember — it turns the scan into a build gate.
Method 3 — Grype
Grype, from Anchore, is the other popular open-source CLI. It pairs naturally with Syft (an SBOM generator from the same project) and reads the same image sources:
# Scan an image by name grype my-image:tag # Scan a saved tarball explicitly grype docker-archive:my-image.tar # Only fail on a severity threshold grype my-image:tag --fail-on high
Grype and Trivy will often report slightly different counts on the same image. That is expected — they use different databases and matching rules. We dig into why in the 2026 scanner benchmark.
Method 4 — ScanRook (no install required)
If you do not want to install anything — or you want a single report that merges OSV, NVD, and vendor advisory data with EPSS exploit-probability scores — you can scan a Docker image with ScanRook in three ways:
- Web upload: export the image to a tar (see below) and drag it onto the ScanRook dashboard. No CLI, no agent.
- Registry scan: connect a registry (Docker Hub, GHCR, ECR, or any OCI-compliant registry) and scan an image by name and tag without pulling it yourself.
- CLI: install the scanner and run it locally:
curl -fsSL https://scanrook.sh/install | bash scanrook scan my-image.tar
ScanRook also unpacks nested archives and shaded JARs that simpler file-path scanners miss, and it reads the actual installed package state rather than guessing from advisory metadata. That difference is the subject of our deep dive on installed-state scanning vs. advisory matching.
How to export an image to a tarball
Three of the four methods above can read a tarball, which is the most portable way to move an image to a scanner. Export any local image with docker save:
# Build, then export to a tarball docker build -t my-image:tag . docker save my-image:tag -o my-image.tar
The resulting my-image.tar contains every layer and the image manifest. This is exactly what you upload to ScanRook or feed to Trivy and Grype on a machine that does not have the original image loaded.
How to read the results
Every scanner reports the same core fields. Learn to read them and the output stops being a wall of red:
| Field | What it tells you |
|---|---|
| CVE ID | The unique identifier for the vulnerability. Look it up to understand impact. |
| Package & version | The exact installed component that is affected. |
| Severity / CVSS | Theoretical impact. Useful, but not the same as real-world risk. |
| Fixed version | The version that resolves it. “won't fix” or blank means no patch yet. |
| EPSS / KEV | Exploit probability and known-exploited status — the best signal for what to fix first. |
Do not sort by severity alone. Roughly half of all CVEs are rated High or Critical, so severity is a weak filter. Sort by exploit probability instead — see how to prioritize vulnerabilities with EPSS.
How to fix the vulnerabilities you find
Scanning is only half the job. Most findings fall into one of three buckets, each with a standard fix:
- OS package CVEs: rebuild on an updated base image (e.g. bump
FROM debian:12.4to the latest patch tag) or add anapt-get upgradestep. A stale base image is the single biggest source of findings. - Application dependency CVEs:bump the dependency in your lockfile and rebuild. The scanner's “fixed version” column tells you the target.
- Smaller attack surface:switch to a minimal base image. A distroless or slim image simply contains fewer packages, so it has fewer CVEs to begin with — see Alpine vs Debian vs Distroless.
Automate it in CI/CD
A scan you run by hand once a month is not a control. Wire scanning into your pipeline so every image is checked before it ships, and fail the build on a threshold you choose. Here is the idea in a GitHub Actions step using Trivy:
- name: Scan image
run: |
docker build -t my-image:${{ github.sha }} .
trivy image --exit-code 1 --severity HIGH,CRITICAL my-image:${{ github.sha }}ScanRook can do the same with a policy that gates deployments on severity, EPSS, or CISA KEV membership rather than a single severity threshold. For the broader playbook, read container scanning best practices.
Common pitfalls
- Scanning only the base, not the final image. Your application layers add dependencies. Always scan the final built artifact.
- Ignoring nested JARs.Java apps often bundle dependencies inside shaded or uber-JARs. A scanner that does not unpack them will miss real CVEs — this is exactly how Log4Shell hid in so many images.
- Treating every Critical as urgent. A Critical CVE in a package that is never reachable at runtime can wait; a High CVE with a public exploit cannot. Prioritize by exploitability.
- Scanning once. New CVEs are published daily. Re-scan deployed images on a schedule, not just at build time.
Frequently asked questions
Can I scan a Docker image without installing anything?
Yes. Export the image with docker save my-image:tag -o image.tar and upload the tar to the ScanRook dashboard, or connect a registry and scan by name. No CLI or agent is required.
Trivy, Grype, and Scout give different counts — which is right?
None is simply “right.” They use different vulnerability databases and matching logic. Higher counts often mean more false positives from loose advisory matching, not better coverage. What matters is whether a finding reflects a package actually installed and reachable.
Should the scan fail my build?
Yes, on a threshold you choose. Failing on High and Critical is a common starting point; maturing teams gate on exploit probability (EPSS) and CISA KEV membership instead, which blocks far fewer builds while catching the findings that matter.
How is scanning an image different from generating an SBOM?
An SBOM is the inventory of what is in the image; a vulnerability scan is the inventory matched against vulnerability data. Most scanners generate an SBOM internally as the first step. See what an SBOM is for more.
Scan a Docker image in seconds with ScanRook
Export your image with docker save and upload the tar, or connect a registry to scan by name. ScanRook merges OSV, NVD, and vendor advisory data with EPSS exploit scores into one prioritized report — no agent to install.