Because a scratch image contains no operating system package database, so an image scanner has nothing to enumerate. Your application binary may still hold vulnerable libraries compiled in. An empty result means the scanner found no installed packages, not that the image is free of known flaws.
What scratch means
Docker documents scratch as a reserved, minimal base. Using it signals that the next instruction in the Dockerfile is the first filesystem layer of your image. It is typically used for minimal images containing only what an application needs, such as a single compiled binary.
That is good for attack surface. It is also why a scan looks empty. Most image scanners learn what is installed by reading package manager records, such as the dpkg or apk databases. A scratch image has none.
What a scanner can and cannot read
| Image content | Typical scanner visibility |
|---|---|
| Packages in an OS package database | Listed with versions |
| Language packages with metadata files | Often listed |
| A compiled binary with no metadata | Usually not listed, unless the scanner reads embedded build data |
| Files copied in with no record | Not listed |
Some compiled languages embed module information in the binary. The Go toolchain does this, and reading it is covered in go binary build info and dependency scanning. Other languages embed nothing useful, so the library versions inside are invisible to an image scan.
How to get a real inventory
- Scan the source, not only the image. The lockfile in the repository lists what went into the build.
- Generate an SBOM at build time, from the build tool, and attach it to the image. That is more reliable than reverse engineering the image afterwards.
- Keep the multi-stage builder image scan too. The final stage may be empty, but the builder stage knows the dependencies. See detecting vulnerabilities in multi-stage Docker builds.
- Record what is in the image deliberately, such as CA certificates and timezone data copied from another stage, because those files also age.
How to tell empty from clean
Ask the scanner for the count of components it found, not only the count of findings. Zero components with a nonempty binary is a coverage gap. Zero findings with a hundred components is a clean result. They look the same in a summary that only shows findings.
Try it yourself
Click through it at your own pace, or open it full screen. Notice how findings attach to packages, then think about what happens when there are no package records.
Safeguard supports SBOM and dependency scanning and offers a free scan at gold.safeguard.sh/scan. Combine source level and image level views so a minimal image does not hide its contents.
Quick check
Is a scratch image safer than a full base image?
It has less to attack, since it ships fewer files. It still contains whatever you compiled in, so libraries inside the binary still need tracking.
Should I stop scanning images that come back empty?
No. Keep the image scan, add a build time SBOM, and scan the source lockfile so the inventory comes from a place that knows the dependencies.
Sources
Last reviewed: 19 October 2026.