Safeguard
Containers

Docker Security Scanning: How to Find Vulnerabilities in Your Images

Docker security scanning inspects your container images for known-vulnerable OS and application packages before they reach production. Here is how it works and how to wire it into your pipeline.

Karan Patel
Platform Engineer
6 min read

Docker security scanning is the practice of inspecting a container image for known-vulnerable operating-system and application packages, so you catch a flawed base image or dependency before it ships to production. A Docker image is not one thing — it is a stack of layers, each contributing files and packages — and Docker security scanning works by cataloging every package across those layers and matching them against vulnerability databases. The uncomfortable truth most teams discover on their first scan is that the majority of their image's vulnerabilities come from the base image and the OS packages, not from the application code they wrote.

Here is how scanning actually works, what it catches, and how to make it a gate rather than a report nobody reads.

What is inside an image, and why it matters

When you write FROM node:20 and build, you inherit everything in that base: a Linux distribution, its package manager, dozens of system libraries, and the Node.js runtime. Your RUN apt-get install and npm install lines add more. The final image can easily contain hundreds of packages, most of which you never chose consciously.

Every one of those packages has a version, and every version has a known-vulnerability history. Docker security scanning enumerates the installed packages — OS packages from dpkg/rpm/apk databases and language packages from package.json, requirements.txt, go.sum, and the like — then cross-references each package-version against sources like the National Vulnerability Database and distro-specific advisories.

Because the analysis is per-layer, a good scanner can tell you which layer introduced a vulnerability, which points you straight at the offending line in your Dockerfile.

How scanning works in practice

The mechanics of most scanners follow the same shape:

  1. Unpack the image and read its layer filesystem and metadata.
  2. Build a package inventory — this is effectively generating a software bill of materials (SBOM) for the image.
  3. Match each package against vulnerability feeds to produce a list of findings with severities.
  4. Report, ideally with the fixed version available for each finding so you know the remediation.

Open-source scanners like Trivy and Grype run this locally against any image and are the easiest way to start:

# Scan a local or remote image with Trivy
trivy image myapp:latest

# Fail the command on HIGH or CRITICAL findings — useful in CI
trivy image --exit-code 1 --severity HIGH,CRITICAL myapp:latest

That --exit-code 1 flag is the whole trick to turning a scan into a gate: a nonzero exit fails the CI step, which blocks the pipeline.

The remediation hierarchy

Findings are not all equally actionable, and the order you attack them in matters.

First, update the base image. The single highest-leverage move is usually bumping to a newer, patched base tag or switching to a slimmer base. A -slim or distroless base ships far fewer packages, and fewer packages means a smaller attack surface and fewer things to patch. Rebuilding FROM node:20.11-slim instead of a months-old node:20 tag often clears dozens of findings in one line.

Second, patch the OS packages you actually need. If a vulnerable system library must stay, update it explicitly in the Dockerfile rather than accepting whatever the base pinned.

Third, fix application dependencies. The vulnerable npm or PyPI packages inside your app are the same ones a dependency scan would catch outside the container; an SCA tool and the container scan overlap here, and that overlap is fine — defense in depth.

Fourth, accept and document what you can't fix. Some findings have no fix available yet, or aren't reachable in your usage. Suppress those explicitly with a reason so they don't drown the actionable ones. Do not suppress silently.

Wiring it into CI

A scan you run manually once a quarter finds yesterday's problems. The value comes from running it automatically on every image build.

A workable policy:

  • Scan on every build and every pull request that changes the Dockerfile or dependencies.
  • Gate on new HIGH/CRITICAL findings that have a fix available. Blocking on unfixable findings just teaches people to disable the gate; blocking on fixable ones drives the right behavior.
  • Scan the registry on a schedule too. A new CVE published today makes an image that passed last week newly vulnerable. Rescanning stored images catches vulnerabilities disclosed after the build.
  • Generate and keep the SBOM. Storing the SBOM for each built image means that when the next big CVE drops, you can answer "are we affected" by querying stored inventories instead of rescanning everything under pressure.

That last point is what separates teams that scramble for a week after a major disclosure from teams that answer the question in an hour.

Beyond package vulnerabilities

Docker security scanning in the package-vulnerability sense is necessary but not sufficient. A complete container security posture also covers:

  • Misconfiguration. Running as root, no read-only filesystem, unnecessary capabilities. These are Dockerfile and runtime settings, not package CVEs.
  • Secrets baked into layers. A secret COPYd in and then "removed" in a later layer is still present in the earlier layer's history. Scanners that check for embedded secrets catch this.
  • Runtime behavior. What the container actually does when running is the domain of runtime security and dynamic testing, not static image scanning.

If you want to build the underlying model of how container supply-chain risk propagates from base image to production, our academy has a container-security track.

The bottom line: build Docker security scanning into your pipeline as an automated gate, start from a minimal base, attack findings in the base-image-first order, keep the SBOMs, and rescan on a schedule. A container is only as trustworthy as the packages buried in its layers, and scanning is how you know what is actually in there.

FAQ

What does Docker security scanning actually check?

It inventories every OS and application package across an image's layers and matches each package-version against vulnerability databases like the NVD and distro advisories. The result is a list of known vulnerabilities with severities and, ideally, the fixed version for each.

Why do most of my image vulnerabilities come from the base image?

Because the base image contributes a whole Linux distribution and runtime — often hundreds of packages you never explicitly chose. Updating to a newer or slimmer base (like -slim or distroless) is usually the highest-leverage remediation and can clear many findings at once.

How do I make a scan fail my CI pipeline?

Use the scanner's exit-code flag. For example, trivy image --exit-code 1 --severity HIGH,CRITICAL myapp:latest returns a nonzero exit on qualifying findings, which fails the CI step. Gate on fixable HIGH/CRITICAL findings rather than everything to avoid teams disabling the gate.

Should I rescan images that already passed?

Yes. New CVEs are disclosed constantly, so an image that passed last week can be vulnerable today. Rescan stored registry images on a schedule, and keep each image's SBOM so you can quickly check exposure when a major vulnerability is announced.

Never miss an update

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