← All guides

Container image scanning

Build & artifacts
On this page

In plain terms

Your application code is only part of what ships. When you build a container image, you also bundle an operating system, system packages, and language runtimes, often hundreds of components you never chose directly (they came from the base image). Container image scanning inventories everything inside an image and checks it for known vulnerabilities.

It's SCA applied to the whole box your app runs in, not just its dependencies.

Why it matters

A pristine codebase running on a two-year-old base image is still exposed to every unpatched OS-level CVE in that image. Base images drift out of date silently, and "it built fine" says nothing about whether openssl inside it has a critical vulnerability. Image scanning is what keeps the gap between "my code is clean" and "my container is clean" from surprising you.

Where it fits

  • Artifact: a built container image (local, or in a registry, referenced by tag or digest).
  • Stage: at build time (scan before you push) and continuously in the registry (a clean image today can have a new CVE tomorrow, the image didn't change, the world did).

How it works

A scanner reads the image layer by layer, enumerates OS packages (from the apk/apt/rpm databases) and language dependencies, and matches them against vulnerability feeds. Good tools map findings to the specific layer that introduced them (so you know whether it's your Dockerfile or the base image at fault) and tell you if a fixed package version exists.

Which layer a finding came from decides who can fix it A container image built in three layers. The application code you copied in is yours to change. The dependencies you installed are yours to bump. The base image underneath brings an operating system, a language runtime, a shell and a package manager, none of which anyone on your team selected individually, and the practical fix for a finding there is a smaller base image or the publisher's next release rather than a patch you apply. EVERY FINDING IN AN IMAGE CAME FROM A LAYER THE LAYER WHO CHOSE IT WHAT FIXING IT MEANS COPY ./app /srv/app you, this morning change the code RUN pip install -r requirements.txt you, at some point bump the dependency FROM python:3.11 somebody else a smaller base image an OS, a runtime, a shell, a package manager and their upstream or their next release Most of what a scan reports lives in the layer nobody on your team picked line by line, which is why the cheapest fix is so often a different base rather than a patch.
A scanner that does not attribute a finding to its layer hands you the same list with the ownership taken out, and ownership is the part that decides what you do next.
ToolNotes
TrivyOSS, very widely used; scans images, filesystems, and more
GrypeOSS, from Anchore; pairs with the Syft SBOM tool
ClairOSS; powers registry-side scanning (e.g. Quay)
Docker Scout, Snyk ContainerCommercial/integrated options

Common pitfalls

  • Choose a small base image. Distroless or Alpine/Chainguard images carry far fewer packages, and therefore far fewer CVEs, than a full ubuntu base. The cheapest fix is often a smaller base.
  • Unreachable OS CVEs. Many flagged packages aren't used by your app; prioritize by exposure and reachability, not raw count.
  • Scan by digest. Tags move; pinning to an image digest makes scans reproducible.
How Draugr fits. Draugr's images control runs Trivy against each container image a component declares, normalized to SARIF. Combined with the component's exposure and criticality, image CVEs are ranked into an actionable order rather than a raw count. See how to declare images in a Saga in the docs.

Grype can serve the same control, on its own or beside Trivy. Both sets of findings arrive in one report, ranked by the same risk model. Two matchers draw on different advisory sources, so where they disagree that difference is a statement about coverage neither makes alone.

Keep learning

  • SCA, vulnerabilities in your application dependencies
  • SBOM, the parts list image scanning builds on
  • IaC misconfiguration, securing the Dockerfile itself