How do you scan a Docker image for known vulnerabilities?
Quick Answer
Vulnerability scanners (like Docker Scout, Trivy, Grype, or a registry's own built-in scanning feature) inspect an image's layers to identify every installed package and its version, then cross-reference that inventory against databases of known vulnerabilities (CVEs) to report which packages in the image have known, disclosed security issues. Scanning should happen both during CI (blocking a build/deploy if severe vulnerabilities are found) and on an ongoing basis for already-deployed images, since new vulnerabilities are discovered continuously in software that hasn't itself changed at all.
Detailed Answer
How scanning actually works
docker scout cves myapp:1.0
# or
trivy image myapp:1.0
A scanner inspects an image's layers to build an inventory of every installed package and its exact version. This includes OS-level packages via the package manager's metadata, plus language-level dependencies — npm packages, Python packages, Go modules, and others, depending on the scanner's capability. It then cross-references this inventory against vulnerability databases, such as the National Vulnerability Database and vendor-specific advisories, to report which specific packages have known, publicly disclosed vulnerabilities (CVEs, or Common Vulnerabilities and Exposures), typically with a severity rating (critical, high, medium, or low) for each.
myapp:1.0
Total: 12 vulnerabilities found
CRITICAL: 1
- CVE-2023-XXXXX in openssl 1.1.1k (fixed in 1.1.1t)
HIGH: 3
- CVE-2022-YYYYY in libcurl 7.68.0 (fixed in 7.74.0)
...
Why images need to be scanned repeatedly, not just once
A vulnerability report for a given image can genuinely change without the image itself ever changing at all. A new CVE disclosed today might affect a package version that's been sitting unchanged inside an already-built, already-deployed image for months. This is why scanning shouldn't be treated as a one-time gate at build time alone. Periodically re-scanning already-deployed images against the continuously updated vulnerability database is essential to catch newly disclosed issues in software you already shipped and forgot about.
Integrating scanning into CI/CD
# Simplified CI pipeline step concept
- name: Scan image for vulnerabilities
run: trivy image --exit-code 1 --severity CRITICAL,HIGH myapp:${{ github.sha }}
A common practice is failing the CI build (--exit-code 1) if the scan finds vulnerabilities at or above a chosen severity threshold. This prevents a genuinely dangerous image from ever reaching a registry or a production deployment in the first place, shifting the security check as early in the pipeline as practical ("shifting left").
What to actually do about a finding
Most findings resolve one of a few ways. The first is to update the base image — often the single highest-leverage fix, since a newer base image tag frequently already includes patched versions of many underlying packages (see the base-image question). The second is to update the specific affected dependency directly, if it's pinned to an outdated version in your own application's dependency manifest. For a small number of unavoidable findings with no available fix yet, the option is to document and accept the risk deliberately, with a tracked exception rather than silently ignoring it, if the vulnerable code path genuinely isn't reachable or exploitable in your specific usage.
Reducing the attack surface in the first place
Scanning is a detection mechanism, not a prevention one. Pairing it with the practices covered elsewhere in this stack meaningfully reduces how much there is to find in the first place. Smaller base images (Alpine, distroless — see that question) simply contain fewer packages overall, and multi-stage builds (see that question) exclude build-time-only tooling from the final image entirely. Both directly shrink the surface a scanner has to report on.
Registry-integrated scanning
Many registries (Docker Hub's paid tiers, GitHub Container Registry, AWS ECR, Harbor) offer built-in scanning that runs automatically on every push, surfacing results directly in the registry's UI/API. This is convenient for centralizing scan results without needing a separate standalone CI step. Standalone tools like Trivy remain popular specifically because they can run identically in any CI system or locally on a developer's machine, independent of which registry is ultimately used.