Skip to content

The same app, two base images, and the package count that falls by an order of magnitude

Artifact — two SBOMs generated by syft for the same application binary, one on a full distro base and one on a distroless (or scratch) base, and the diff between their package lists — a number that drops from hundreds to single digits. An SBOM is only useful if it is acted on; the action here is minimization, and the evidence is that the thing a scanner has to worry about got measurably smaller because you removed packages your app never used.

Rests on — nothing in this phase; it needs only a container image and the syft binary. It opens the supply-chain module, one of the three domains CKS weights most heavily.

Topology — none. Runs entirely on hopper (or forge) with syft and a local image build; the cluster is not touched. Footprint: zero cluster RAM, per the index.

Read — what an SBOM is: a package inventory keyed by ecosystem (OS packages, language deps), each with a version a vulnerability feed can be matched against. The question to answer: why does a smaller base image produce a shorter SBOM even though your application code is identical — what is being counted, and where does most of it come from?

Build — on hopper, build one trivial app two ways and SBOM each:

ssh zain@hopper
mkdir -p ~/sbom && cd ~/sbom
cat > hello.go <<'GO'
package main
func main() { println("hi") }
GO
# full base
cat > Dockerfile.full <<'DOCK'
FROM golang:1.22 AS build
WORKDIR /src
COPY hello.go .
RUN CGO_ENABLED=0 go build -o /hello hello.go
FROM debian:12
COPY --from=build /hello /hello
ENTRYPOINT ["/hello"]
DOCK
# distroless
cat > Dockerfile.min <<'DOCK'
FROM golang:1.22 AS build
WORKDIR /src
COPY hello.go .
RUN CGO_ENABLED=0 go build -o /hello hello.go
FROM gcr.io/distroless/static-debian12
COPY --from=build /hello /hello
ENTRYPOINT ["/hello"]
DOCK
docker build -f Dockerfile.full -t hello:full .
docker build -f Dockerfile.min  -t hello:min  .
syft hello:full -o table | tee full.sbom
syft hello:min  -o table | tee min.sbom
echo "full packages: $(grep -c . full.sbom)"
echo "min packages:  $(grep -c . min.sbom)"

Verify from outside — count the package lines in each and diff the names:

diff <(syft hello:full -o syft-json | jq -r '.artifacts[].name' | sort) \
     <(syft hello:min  -o syft-json | jq -r '.artifacts[].name' | sort)

Expect — the full SBOM lists the entire Debian userland (bash, coreutils, libc, apt, dozens more); the min SBOM lists essentially your binary and a CA-certs bundle. The diff is almost all removals. That is the answer to the reading question: the SBOM counts everything in the image, and in a normal container most of it is base-image OS packages your application never calls — each one a potential CVE that a scanner will flag and you will have to triage. Shrinking the base is the cheapest vulnerability reduction there is, and the SBOM is how you prove it happened.

Write down — the two package counts and one line from the diff that is a package you are glad is gone (e.g. a shell, so a compromised container has no /bin/sh).

Teardown — images and files on hopper; nothing on any cluster:

ssh zain@hopper 'docker rmi hello:full hello:min; rm -rf ~/sbom'