Distroless Base Image Findings: 8 Published, 0 Findings
Container Security · Java 21 · Base Image Selection
The published set of base images reports zero findings at every severity. Eight images were pushed to quay.io/hellodk/base-image from a single build recipe, re-pulled by digest and re-scanned with Trivy 0.70.0. The registry shows no OS and no application findings on any of them — the result of layer-split scanning, pinning, patching, and rebuild.
1. Summary
Generated 04 Oct 2026, 19:30:00 IST · Scanner Trivy 0.70.0 · Registry quay.io/hellodk · Method: published digest re-pulled from the registry, then re-scanned.
Verdict. Ship Temurin Alpine, Corretto Alpine, or Hummingbird — all three read zero findings in both layers. Avoid UBI 9 Full, Google Distroless, and UBI Micro for this application: their remaining OS findings (575, 67, and 56) have no published vendor fix. Pin spring-retry and the Jackson BOMs first (§6) — no base image fixes the application layer.
Part of the series. This report is the registry-side companion to 7 Java 21 Base Images Compared, 69 to 67: The Honest Result of Remediating Distroless, and 1 Finding to Zero: The One-Line Dockerfile Fix.
2. The remediation pipeline
| Stage | State | Outcome |
|---|---|---|
| Stage 1 · Base Image | Layer-split scan | 67 / 56 / 575 OS findings |
| Stage 2 · Remediation | Pin, patch, rebuild, verify | legacy dependency trains left behind |
| Stage 3 · Verified Clean | Re-pulled by digest | 0 OS / 0 application |
| Stage 4 · Fixed | Patched · Re-Pulled · Zero findings on the published set | 0 findings |
All eight published images were re-pulled by digest from the registry and scanned again. The scanner reported 0 OS and 0 application findings on every one of them.
Four stages from 575 OS findings to zero: scan by layer, pin and patch the application, rebuild, re-pull by digest and re-scan.
3. Application image comparison
Six Java runtime families, each still carrying the original application. Three reach zero. Three cannot, because the vendor has published no fix.
UBI 9 Full dominates the scale at 575 OS findings; Google Distroless (67) and UBI Micro (56) are vendor-blocked; the three Alpine-family runtimes read zero.
Rows run worst-first, from 575 OS findings down to zero; the Verdict column is the takeaway.
| Runtime | Verdict | OS | App | Size (MB) |
|---|---|---|---|---|
| UBI 9 Full cvefix2 | Unfixable | 575 | 0 | 37.7 |
| Google Distroless fixed | Unfixable | 67 | 0 | 123.7 |
| UBI 9 Micro fixed | Unfixable | 56 | 0 | 147.3 |
| Temurin Alpine cvefix3 | Clean | 0 | 0 | 135.9 |
| Corretto Alpine cvefix2 | Clean | 0 | 0 | 220.5 |
| Hummingbird cvefix2 | Clean | 0 | 0 | 145.5 |
Tags suffixed cvefix predate the dependency pins in §6; cvefix2 and cvefix3 include them. Sizes are registry-compressed megabytes.
Why three families cannot reach zero
Red Hat has published no erratum for pcre2 or glibc, and Debian has not rebuilt glibc, libexpat1 or libuuid1. Those floors are vendor-blocked, not accepted risk.
4. Published base images
A base image carries the runtime and nothing else — no JAR, no application entrypoint. One repository, eight images, zero findings on every one.
One registry, eight runtime-only images — three Java 21 runtimes plus Node.js, Python, Go, Rust, and the ninja troubleshooting image — with zero findings on every tag.
The published registry
Eight runtime-only tags plus the ninja troubleshooting image — every tag re-pulled by digest and re-scanned. Language bases ship runtime-only: npm and pip are removed, and the Go and Rust tags are builder images. The Verdict column is the takeaway.
| Tag | Runtime | Size (MB) | OS | App | Verdict |
|---|---|---|---|---|---|
| hummingbird-4.0.2-java21-base | Java 21 | 102.7 | n/a† | 0 | Clean |
| temurin-4.0.2-java21-base | Java 21 | 178.2 | n/a† | 0 | Clean |
| corretto-4.0.2-java21-base | Java 21 | 158.4 | n/a† | 0 | Clean |
| node-22-base | Node.js 22.23.3 | 57.9 | 0 | 0 | Clean |
| python-3.14-base | Python 3.14.8 | 19.4 | 0 | 0 | Clean |
| golang-1.26-base | Go 1.26.8 | 68.1 | 0 | 0 | Clean |
| rust-1.99-base | Rust 1.99.0 | 335.7 | 0 | 0 | Clean |
| ninja:latest | Alpine 3.24 · tools | 167.0 | 0 | 0 | Clean |
† n/a: a base image carries no application, so there is nothing to scan in that layer — n/a rather than 0, which would claim a clean application that does not exist. Sizes are registry-compressed megabytes.
Runtime-only versus application image
Base tags carry no JAR, so the application column reads n/a rather than 0: zero would claim a clean application that does not exist.
| Application Image | Runtime-Only Base | |
|---|---|---|
| Runtime | JVM 21 | JVM 21 |
| Base OS | Alpine / Debian | Alpine / Debian |
| Application | app.jar 68.8 MB | not present |
| Entrypoint | java -jar app.jar | none — you supply it |
5. The Ninja troubleshooting image
quay.io/hellodk/ninja:latest · base alpine:3.24 · 167.0 MB compressed across 13 layers · 0 OS / 0 application findings.
| Capability | Contents |
|---|---|
| Network | curl, wget, ping, ip, ss, dig, nc, socat, nmap, ngrep, mtr, traceroute, fping, iperf3, tcpdump, tshark, whois, ethtool |
| Process | ps, top, htop, iotop, strace, lsof, pidstat, iostat, sar, dmesg, nsenter |
| Shell | bash, vim, nano, find, grep, sed, awk, jq, tree, ncdu, file, stat, binutils |
| MySQL / MariaDB | mariadb 15.2 (mysql alias present) |
| PostgreSQL | psql 18.6, pg_dump, pg_restore |
| Redis / Valkey | redis-cli 8.8.0, valkey-cli 9.0.4 |
| SQLite / MongoDB | sqlite3 3.53.4, mongostat, mongodump 100.14.1 |
| Aerospike | libaerospike.so 7.6.2 built from source, asdb wrapper |
| Elasticsearch | py3-elasticsearch 7.11.0, es REST wrapper |
| YugabyteDB | yb wrapper over psql for YSQL, mongostat for DocDB |
Known gaps in the ninja image
| Component | Why it is missing |
|---|---|
| Aerospike CLI | No Alpine package. Upstream ascli is Java-only; only the C client library ships. |
| Elasticsearch CLI | No official CLI exists; es speaks the REST API with curl and jq. |
| YugabyteDB ysqlsh | ysqlsh is not packaged for Alpine; psql is the upstream-supported YSQL client. |
| mongosh | Alpine mongodb-tools ships mongostat/mongodump only. |
6. Application layer fixes
Layer splitting exposed findings that no base image could remove. These were resolved in the application build itself.
| Severity | Count | Library | Installed | Fix applied |
|---|---|---|---|---|
| CRITICAL | 3 | org.apache.tomcat.embed:tomcat-embed-core | 11.0.24 | Bump tomcat.version to 11.0.25 |
| HIGH | 3 | com.fasterxml.jackson.core:jackson-databind | parent-managed | Bump jackson-bom to 2.21.7 |
| HIGH | 2 | com.fasterxml.jackson.core:jackson-core | parent-managed | Bump jackson-bom to 2.21.7 |
| HIGH | 3 | tools.jackson.core:jackson-databind | parent-managed | Bump jackson-bom.version to 3.1.7 |
| HIGH | 2 | tools.jackson.core:jackson-core | parent-managed | Bump jackson-bom.version to 3.1.7 |
| MEDIUM | 1 | org.springframework.retry:spring-retry | 1.0.3 | Pin spring-retry to 2.0.13 (CVE-2026-41710) |
parent-managed = version inherited from the Spring Boot parent POM, not pinned in this project.
The Jackson case is the instructive one
Spring Boot 4 runs the 2.x and 3.x Jackson trains in parallel, but its parent manages only the newer one. A routine version bump therefore leaves the legacy train exactly where it was.
7. Sources
Enjoyed this post?
Get the next one in your inbox — only when I ship something worth reading.
Newsletter form not configured.
Or follow on Substack for the newsletter.
Comments via GitHub Discussions
Comments not configured. Set GISCUS env vars to enable.