An image is a snapshot of an operating system and a dependency tree, and both accumulate known vulnerabilities from the day they are built. This lesson shows how to scan with Docker Scout and Trivy, how to read and prioritise the results, how to fix the findings that matter, and how to lint Dockerfiles and generate a software bill of materials so scanning becomes routine rather than a one-off audit.
A scanner walks the image's layers and builds an inventory: OS packages (from apk, dpkg or rpm databases), language packages (package-lock.json, requirements.txt metadata, Java jars, Go binaries) and sometimes secrets and misconfigurations. It then matches each package version against vulnerability databases and reports CVEs with a severity, whether a fixed version exists, and which layer introduced the package. That last detail matters: a CVE in the base image is fixed by updating FROM, while one in node_modules is fixed in the lockfile.
Scout is built into recent Docker CLIs and Docker Desktop. It needs no installation:
docker scout quickview my-api:1.0
# Target │ my-api:1.0 │ 0C 2H 9M 14L
# Base │ node:22 │ 0C 1H 7M 12L
docker scout cves --only-severity critical,high --only-fixed my-api:1.0
docker scout recommendations my-api:1.0 # suggests newer base tags with fewer CVEs
docker scout compare --to my-api:0.9 my-api:1.0quickview separates findings inherited from the base image from those you introduced. recommendations is often the fastest fix: switching from node:22 to node:22-slim or node:22-alpine removes hundreds of packages you never used. compare is useful in pull requests to show that a change did not add vulnerabilities.
Trivy is an open-source scanner that runs anywhere, which makes it the usual choice for CI. It can be installed natively or run as a container that reads the local image store through the Docker socket, as in the sample code.
trivy image my-api:1.0 # full report
trivy image --severity HIGH,CRITICAL --ignore-unfixed my-api:1.0
trivy image --exit-code 1 --severity CRITICAL my-api:1.0 # fail a pipeline
trivy image --format json -o report.json my-api:1.0
trivy fs --scanners vuln,secret,misconfig . # scan the source tree and Dockerfile
trivy config Dockerfile # misconfiguration checks only--ignore-unfixed hides vulnerabilities that no package update can address yet, which keeps the report actionable. A .trivyignore file lists CVE IDs you have assessed and accepted, ideally with a comment and an expiry date.
Not every finding needs action today. Sort by:
| Factor | Ask |
|--------|-----|
| Severity | Critical and High first; use the CVSS score, not only the label |
| Fix available | Is there a patched version? If not, only mitigation is possible |
| Reachability | Is the vulnerable component actually used at run time? A CVE in apt matters little in a container that never runs apt |
| Exposure | Is the container internet-facing, or an internal batch job? |
A pipeline that fails on any CVE will be ignored within a week. Fail on Critical and High with a fix available and report the rest.
docker pull node:22-alpine and rebuild; most OS-level CVEs are fixed upstream within days.slim, alpine, or distroless variants carry fewer packages, so fewer CVEs.npm audit fix, pip install -U, or a Dependabot-style bot on the lockfile.curl, package manager caches; multi-stage builds do this naturally.FROM node:22-alpine@sha256:...) and let a bot bump the digest.A software bill of materials lists every component in an image, so you can answer "are we affected?" the day a new CVE appears without rescanning everything:
docker scout sbom --format spdx my-api:1.0 > sbom.spdx.json
trivy image --format cyclonedx --output sbom.cdx.json my-api:1.0
docker buildx build --sbom=true --provenance=true -t my-api:1.0 . # attach at build timeLinting catches problems before an image exists. hadolint flags unpinned versions, missing --no-install-recommends, latest tags and shell-form CMD; run it in CI next to the scanner.
What does `--ignore-unfixed` do in a Trivy scan?
docker scout quickview, cves and recommendations are built in; Trivy is the portable choice for CI with --exit-code 1.Next lesson: Docker Best Practices and Security — consolidate everything into a checklist for small, safe, maintainable images.