Skip to content

Latest commit

 

History

History
306 lines (241 loc) · 23.2 KB

File metadata and controls

306 lines (241 loc) · 23.2 KB

Image Roadmap — the road to 100 (demand-ranked)

Status: 108 images — the 100 target is met. This is the source-of-truth plan for growing the catalog. The tier tables below stay useful as the demand-ranked backlog; the "road to 100" batch section near the end is kept as a build log of how the first 100 were reached, not as remaining work. It supersedes the batch order from earlier sessions, which was ordered by build ease and GitHub popularity. This version is ordered by actual container demand first, then build effort.

Why demand, not stars

For a hardened-container catalog, the metric that matters is "how often is this run as a container," not GitHub stars. They diverge sharply:

Image ⭐ stars 🐳 Docker pulls Reality
dive 54k 1.7M laptop image-inspector — rarely containerized
k9s 34k 169k terminal UI run locally — almost never a prod container
oauth2-proxy 15k 97M auth sidecar — run as a container everywhere
pomerium 5k 1.6B* identity-aware proxy — massive container use

So popular-but-local tools (k9s, stern, dive, age) are deprioritized, and high-pull proxies/apps (oauth2-proxy, pomerium, …) are promoted.

* Docker pull counts are noisy — cumulative since inception, inflated by CI/bot pulls, and some namespaces (bitnami/*) are being deprecated. Treat as order-of-magnitude, not precise.

License lens: 🟢 permissive (MIT/BSD/Apache/MPL/ISC) add freely · 🟡 AGPL/GPL ok (precedent: loki/tempo/mimir/minio/trufflehog) · 🔴 SSPL/BUSL/EULA avoid or fork.

Build effort: Go single-binary = the proven Batch-B crank (fast). C/Rust/C++ = heavier. Node/frontend = the bwrap frontend quagmire (defer, own effort). Controllers = multi-image, run in-cluster (high demand, more work).


Tier 1 — high demand × easy Go build (do next)

Single static Go binaries and genuinely run as containers. Best impact-per-effort.

Image Upstream License 🐳 pulls Notes
[x] oauth2-proxy oauth2-proxy/oauth2-proxy 🟢 MIT 97M 15k k8s auth sidecar, ubiquitous, CG-gated (#393)
[x] flux (CLI) fluxcd/flux2 🟢 Apache-2.0 3.8M 8k GitOps, CG-gated — embeds install manifests (kustomize bundle at build)
[x] kustomize kubernetes-sigs/kustomize 🟢 Apache-2.0 12M 12k CI/CD standard, CG-gated — monorepo, kustomize/vX tag
[x] sops getsops/sops 🟢 MPL-2.0 22k secrets in CI, CG-gated
[x] crane google/go-containerregistry 🟢 Apache-2.0 4k registry ops, heavy CI use
[x] kubeseal bitnami/sealed-secrets 🟢 Apache-2.0 9k sealed-secrets CLI (canonical repo, not bitnami-labs 301)

Reclassified out of Tier 1 (not clean Go single-binaries — moved to "deferred"):

  • pomerium — huge demand (1.6B pulls) but //go:embeds an arch-specific Envoy binary as its data plane → Tier-3-complexity build, own effort.
  • cmctl — cert-manager's makefile-modules/klone build, no clean -X version injection → needs its own investigation.

Tier 2 — solid demand, easy Go CLIs

Image Upstream License 🐳 pulls Notes
[x] helmfile helmfile/helmfile 🟢 MIT 5k declarative Helm, CD pipelines
[x] regctl regclient/regclient 🟢 Apache-2.0 2k registry client, CI
[x] stern stern/stern 🟢 Apache-2.0 5k multi-pod log tail (borderline: often local)
[x] kube-vip kube-vip/kube-vip 🟢 Apache-2.0 3k control-plane VIP + LB for bare metal — shipped; runs as root (NET_ADMIN/NET_RAW for netlink+ARP)

Tier 3 — high demand, heavier builds (deliberate; fills thin DB/proxy/app categories)

Real container demand, but C/Rust/C++/Node — each is its own effort, not a crank. Sequence by effort. Fills the catalog's thinnest categories (databases, proxies, apps).

Image Upstream License Build 🐳 pulls Notes
[x] pgbouncer pgbouncer/pgbouncer 🟢 ISC C 20M 4k Postgres pooler (thin DB cat) — shipped; underscore tags (tag-rewrite)
[x] unbound NLnetLabs/unbound 🟢 BSD C 12M 5k DNS resolver — shipped; --sbindir=/usr/bin, builtin evloop, local-data smoke test
[ ] varnish varnishcache/varnish-cache 🟢 BSD C 21M 4k Deferred → see below. varnishd compiles VCL with cc at runtime → needs gcc+binutils+headers in prod (breaks the shell-less/minimal thesis).
[ ] apisix apache/apisix 🟢 Apache-2.0 C/Lua/OpenResty 37M 17k API gateway (harder: OpenResty)
[x] vaultwarden dani-garcia/vaultwarden 🟡 AGPL-3.0 Rust 304M 64k self-hosted Bitwarden — shipped
[ ] kvrocks apache/kvrocks 🟢 Apache-2.0 C++ 3.5M 4k Redis-on-RocksDB
[x] patroni patroni/patroni 🟢 MIT Python 9k Postgres HA — shipped; first Python-daemon pattern
[ ] woodpecker woodpecker-ci/woodpecker 🟢 Apache-2.0 Go+frontend 2.7M 7k CI server (has web UI)
[x] zipkin openzipkin/zipkin 🟢 Apache-2.0 JVM-jlink 60M 17k distributed tracing — shipped; Spring Boot fat jar EXPLODED so its 157 bundled jars are scannable/patchable

Deferred / heavier efforts (own project, not a batch)

Image Reason
varnish varnishd compiles VCL → C → .so by invoking cc at runtime (VCC_CC="exec cc … -fpic -shared -o %o %s"), on every config load. A working prod image must therefore ship gcc + binutils + C headers + varnish's headers — a permanent compiler/attack-surface that breaks the "no compiler, minimum packages" thesis (Chainguard ships the toolchain for the same reason). Revisit only as a deliberate, documented exception, or pick a compiler-free HTTP-cache alternative.
kubescape Go, but a large build — needs ~40 G local disk freed (stale /tmp/bubblewrap-guest-*). Recipe already generated in batch-b; onboard once disk allows.
pomerium 1.6B pulls but //go:embeds an arch-specific Envoy binary (data plane). Needs an Envoy-fetch step + cross-arch handling — Envoy-image-class effort, not a clean crank.
cmctl cert-manager's makefile-modules/klone build; no clean -X version injection. Needs its own investigation of the version mechanism.
cert-manager (core) Multi-image (controller + webhook + cainjector + startupapicheck + acmesolver). High demand, in-cluster. Mirror the upstream image split — own effort after Tier 1.
flux (controllers) source/kustomize/helm/notification-controller, separate repos. Multi-image, in-cluster. Own effort.
uptime-kuma 166M pulls (MIT) but Node/yarn frontend → the bwrap frontend quagmire (see grafana). Backend-from-source + prebuilt frontend assets, own effort.
argocd Embeds a yarn-built React UI and repo-server needs git/helm/kustomize at runtime (fights distroless). Grafana-bucket effort.

Deprioritized — popular but low container demand (laptop / CLI / embedded)

k9s (terminal UI) · dive (image inspector) · age (local crypto CLI) · duckdb (embedded lib, not a server) · zig/erlang/lua (languages — official base images exist). Add later only on explicit demand.

Avoided — non-OSS licenses (🔴)

MongoDB / Elasticsearch (SSPL) · CockroachDB / Dragonfly (BSL). Use permissive forks if a real need appears (e.g. kvrocks/valkey cover the Redis/BSL gap).

Existing 🔴 exposures to resolve (pre-existing, tracked separately)

  • redis-slim declares SSPL-1.0 but 8.x is tri-licensed → re-declare AGPL-3.0-only.
  • consul 2.0.0 = BUSL-1.1 (no OSS fork) → keep-vs-drop decision pending.

Road to 100 — remaining batches (F–K), policy × usage × license

Ordered by our proven build templates first (fastest impact-per-effort), then demand. Policy filter = buildable under the shell-less/minimal thesis: ✅ static-Go binary · clean C daemon (no runtime compiler — the varnish lesson) · JVM-jlink · interpreter · binary-repackage. Excluded from the 100-path: frontend-in-bwrap (grafana/argocd/uptime-kuma), runtime-compiler (varnish), tangled force-bump graphs, huge-disk C++ (clickhouse), and 🔴 SSPL/BUSL/EULA. This section is a completed build log — the 100 target was reached and the catalog now stands at 108.

Batch F — static Go (the crank) → 88 [DONE]

Image Upstream License Build notes
external-dns kubernetes-sigs/external-dns v0.21.0 🟢 Apache main ., CGO=0, -X …/pkg/apis/externaldns.Version
velero vmware-tanzu/velero v1.18.2 🟢 Apache main ./cmd/velero, CGO=0, …/pkg/buildinfo.Version
kaniko GoogleContainerTools/kaniko v1.24.0 🟢 Apache main ./cmd/executor (binary executor → /kaniko/executor), …/pkg/version.version
step-ca smallstep/certificates v0.30.2 🟢 Apache main ./cmd/step-ca, CGO=0 (drops pkcs11 KMS), -X main.Version; server
skopeo containers/skopeo v1.23.0 🟢 Apache module go.podman.io/skopeo; CGO=0 + -tags containers_image_openpgp drops libgpgme/btrfs

Batch G — clean C daemon (pgbouncer/unbound template) → 93 [DONE]

Image Upstream License Build notes
dnsmasq Wolfi dnsmasq 2.93 🟡 GPL-2.0-or-later apko-only — Wolfi packages it, so no melange recipe and no versions.yaml row (classified wolfi-rolling). Flag-driven: the package ships no config and apko cannot author file contents, so --conf-file= disables config lookup and --log-facility=- moves logs off syslog.
keepalived acassen/keepalived 2.4.3 🟡 GPL-2.0-or-later source-built; --disable-dbus --disable-snmp --with-init=none, --sbindir=/usr/bin (usrmerge). Needs CAP_NET_ADMIN/NET_RAW/NET_BROADCAST at runtime — still runs as nonroot 65532. github-tags needs strip-prefix: 'v', not strip-v (that key is only read by other source types).

Fills thin Infrastructure.

Batch H — JVM-jlink (kafka/zookeeper template)

cassandra (🟢 wide-column DB, Java-17 + jamm agent) · solr (🟢 search, Java-21) · pulsar (🟢 messaging, Java-21) · flink (🟢 stream processing, Java-21) — all DONE. All Apache. Fills thin DB/search/messaging.

Batch I — Rust (qdrant/vector precedent, own effort each)

vector (🟢 MPL-2.0, observability pipeline) · vaultwarden (🟡 AGPL-3.0, Bitwarden server; ~304M pulls) — both DONE.

Batch J — Erlang / Python

patroni (🟢 MIT, Postgres HA — new Python-daemon pattern) — DONE. couchdb (🟢 Apache, Erlang — rabbitmq precedent) remaining.

Batch K — own-effort revisit

cmctl (🟢 Apache — revisit klone build) · + one of {cert-manager core (multi-image) / pomerium (Envoy fetch) / apisix (OpenResty)}.

Batches H–K name more candidates than the 4 slots left to 100; the last slots go to whichever land first.

Beyond the 100-path (deliberate exceptions, not scheduled): varnish (runtime cc), grafana/argocd/uptime-kuma (frontend-in-bwrap), clickhouse/kubescape (disk-cap). See the deferred table above.

Wave 2 — beyond 108 (demand-ranked, re-surveyed 2026-08-30)

Candidate survey after the 100 target was met. Same lens as above (license · container demand · use case), with one new input: many of these already have a Wolfi package, which moves them from "own effort" into the cheapest bucket we have (apko-only, dnsmasq/vector precedent — no melange recipe, no versions.yaml row, just an autoupdate-coverage.yaml classification).

Pull counts are Docker Hub pull_count read on 2026-08-30; treat as order-of-magnitude (cumulative, CI/bot-inflated).

Tier W1 — apko-only from an existing Wolfi package (cheapest crank)

Verified present in the Wolfi x86_64 APKINDEX on 2026-08-30. (v) = versioned package ⇒ wolfi-versioned; (r) = unversioned/rolling ⇒ wolfi-rolling.

Image Wolfi package License 🐳 pulls Category Notes
[ ] wordpress wordpress (r) + wordpress-oci-entrypoint 🟡 GPL-2.0-or-later 1.5B Apps Largest single demand number in the survey. Pairs with our php/httpd; Wolfi ships the OCI entrypoint too. Fills the thinnest category (Apps, 6).
[ ] sonarqube sonarqube (r) 🟡 LGPL-3.0 1.2B K8s, CI & IaC Community Build. Heavier than it looks — bundles Elasticsearch and needs an external Postgres; smoke test = boot + /api/system/status.
[ ] nextcloud nextcloud-server-33 (v) 🟡 AGPL-3.0 1.0B Apps AGPL precedent already set (loki/tempo/mimir/minio/vaultwarden). PHP-FPM.
[ ] maven maven-3.9 (v) 🟢 Apache-2.0 767M Languages & Runtimes Build-tool image on top of our java. Near-zero effort, very high demand.
[ ] kong kong (r) + kong-entrypoint 🟢 Apache-2.0 359M Web Servers & Proxies The Wolfi package removes the OpenResty/Lua build problem that keeps apisix deferred. Fills a thin category (8).
[ ] neo4j neo4j-2025.12 (v) 🟡 GPL-3.0 322M Databases Graph DB — a category we have zero coverage of. GPL precedent: dnsmasq, keepalived.
[ ] gradle gradle-9 (v) 🟢 Apache-2.0 300M Languages & Runtimes Same shape as maven.
[ ] perl perl (r) 🟡 Artistic-1.0 / GPL-1.0+ 255M Languages & Runtimes Trivial; the last mainstream scripting runtime we're missing.
[ ] couchdb couchdb-3.3 (v) 🟢 Apache-2.0 205M Databases Already named in Batch J as an Erlang source build — the Wolfi package makes it a W1, not an own-effort.
[ ] argo-cd argo-cd-3.2 + -repo-server (v) 🟢 Apache-2.0 148M K8s, CI & IaC Re-opens a deferred item. Wolfi packaging removes the yarn-frontend build; the repo-server's runtime git/helm/kustomize needs still apply, so this is multi-image, not one.
[ ] rust rust-1.92 (v) 🟢 MIT OR Apache-2.0 143M Languages & Runtimes Toolchain image (builder-shaped, like our go).
[ ] erlang erlang-28 (v) 🟢 Apache-2.0 65M Languages & Runtimes Natural companion to rabbitmq.
[ ] meilisearch meilisearch (r) 🟢 MIT 51M Databases Rust search engine; complements opensearch/solr at a much smaller size.
[ ] temporal temporal (r) 🟢 MIT 47M Apps Durable-execution server. Wolfi also has temporal-ui-server-oci-entrypoint if we want the UI later.

Also packaged in Wolfi, parked deliberately: argo-workflows (🟢 Apache — take with argo-cd or not at all), harbor-2.14-* (🟢 Apache, but 5+ images — own effort like cert-manager), mattermost-11.4 (⚠️ mixed AGPL + Mattermost Source Available License in the same tree — needs a license read before onboarding), apisix-ingress-controller (🟢 Apache — note Wolfi packages the controller, not apisix itself, so the Tier-3 apisix entry above is unchanged).

Wave 3 survey — verified 2026-09-07

Re-checked every W1/W2 candidate against the live Wolfi APKINDEX (120,016 packages) and a Chainguard free-tier probe, after the W1 table was found wrong twice: it listed a gitlab-runner-18.11 package that was actually gitlab-agent-* (a different product), and a bare-name APKINDEX grep misses versioned packages entirely. Method that actually works — check three things, not one:

grep -x  "P:<name>"        APKINDEX   # exact package name
grep -E  "^P:<name>-[0-9]"  APKINDEX   # versioned package (gitlab-runner-19.3)
grep -E  "^p:(.* )?<name>=" APKINDEX   # a DIFFERENT package that provides the name

The Chainguard probe (validated against known-free nginx/python/static, which all return 200) is an anonymous pull-token request against cgr.dev/v2/chainguard/<name>/manifests/latest.

Already free from Chainguard — dropped from the plan. These were the roadmap's "near-zero effort, very high demand" picks, and there is no reason to publish a second free hardened build of something already free elsewhere:

Image 🐳 pulls Why dropped
wordpress 1.5B cgr.dev/chainguard/wordpress returns 200 anonymously
maven 767M free
gradle 300M free
rust 143M free

Gated on Chainguard and packaged in Wolfi — the cheap, high-demand bucket. Pull counts read from Docker Hub on 2026-09-07:

Image 🐳 pulls License Category Note
sonarqube 1.3B 🟡 LGPL-3.0 K8s, CI & IaC Heavier than the packaging suggests: bundles Elasticsearch, needs an external Postgres
nextcloud 1.1B 🟡 AGPL-3.0 Apps nextcloud-server-33 (+ -apache2-config). PHP-FPM
kong 360M 🟢 Apache-2.0 Web Servers & Proxies The Wolfi package removes the OpenResty/Lua build that keeps apisix deferred
neo4j 323M 🟡 GPL-3.0 Databases Graph DB — a category with zero coverage today
perl 255M 🟡 Artistic/GPL Languages & Runtimes Trivial; the last mainstream scripting runtime missing
couchdb 205M 🟢 Apache-2.0 Databases
erlang 66M 🟢 Apache-2.0 Languages & Runtimes Companion to rabbitmq
meilisearch 52M 🟢 MIT Databases Rust search engine, much smaller than opensearch/solr
temporal 48M 🟢 MIT Apps Durable execution; fills the thinnest category
argo-cd · cert-manager · keda · karpenter · harbor · zot 🟢 Apache-2.0 K8s, CI & IaC All in Wolfi, all gated on Chainguard. No pull counts exist — they publish to quay/ghcr/registry.k8s.io, so demand cannot be ranked the way the rest of this table is

The finding that shapes the next wave: the source-built well has run dry at the top of the demand curve. Every remaining high-demand candidate is either Wolfi-packaged (so apko-only), blocked by a frontend build (alloy, authelia, woodpecker), or already free from Chainguard. What is left to build from source is genuinely hard and much lower demand: influxdb (1.2B, but that number is v1/v2 — current upstream is a Rust rewrite), kvrocks (3.5M, C++), apisix (37M, OpenResty), emqx (Erlang, licence read needed), timescaledb (split licence), pgadmin4 (Python + prebuilt frontend), fluentd (Ruby).

So Wave 3 is a decision, not a queue: continue insisting on source builds and accept far lower demand per unit of effort, or take the apko-only route for things Chainguard gates. Note that apko-only is not a weaker product here — the Wolfi package is free, Chainguard's image is not, so a hardened signed SBOM-attested rebuild of a gated image is exactly the gap this catalogue exists to fill. It also keeps full auto-update coverage via autoupdate-coverage.yaml, which check-autoupdate enforces.

Tier W2 — source-built Go (the proven crank; no Wolfi package)

Image Upstream License 🐳 pulls Category Notes
[x] gitlab-runner gitlab-org/gitlab-runner 🟢 MIT 3.6B K8s, CI & IaC Shipped — source-built. Wolfi does ship it as the versioned gitlab-runner-19.3, which p:-provides the bare name, so provider-priority: 100 was mandatory; without it apko silently installed Wolfi's binary over ours. versions.yaml uses type: json against the GitLab tags API.
[x] buildkit moby/buildkit 🟢 Apache-2.0 1.8B K8s, CI & IaC Shipped — source-built. provider-priority: 100 needed too (Wolfi's buildkitd provides buildkit). buildkitd is the only CGO_ENABLED=1 build in the catalogue, to keep upstream's seccomp tag; libseccomp-static keeps it static. Runs as root — it refuses unprivileged uids, and the rootless path needs setuid newuidmap Wolfi does not ship setuid.
[ ] alloy grafana/alloy 🟢 Apache-2.0 388M Observability Blocked — frontend-in-bwrap, verified 2026-09-06. internal/web/ui/dist is NOT committed; assets_builtin.go is behind //go:build embedalloyui whose go:generate runs npm ci && npm run build, and the !embedalloyui fallback serves from a runtime filesystem path that will not exist in the image. So there is no UI-bearing build without npm in the sandbox. Demand is real (inherits EOL promtail 2.8B + grafana-agent 470M) — reconsider if we ever take on a frontend build pattern.
[ ] authelia authelia/authelia 🟢 Apache-2.0 81M Web Servers & Proxies Blocked — frontend-in-bwrap, verified 2026-09-06. internal/server/public_html contains only 3 files (index.html + the openapi pair); the React bundle is built from web/ by pnpm and is not in the tarball. Same blocker as alloy.
[x] syncthing syncthing/syncthing 🟢 MPL-2.0 346M (two repos) Infrastructure Shipped — confirmed the easiest build in this table. CGO_ENABLED=0 picks the pure-Go modernc.org/sqlite driver v2.x needs; the web GUI is .gitignore'd upstream and regenerated by script/genassets.go, which is plain Go — no npm step. Built -tags noupgrade like upstream's own container.
[x] nsq nsqio/nsq 🟢 MIT 63M Caches, Queues & Messaging Shipped — was indeed about an hour. Ships all nine upstream binaries for drop-in parity with nsqio/nsq; nsqadmin's web assets are committed pre-built and pulled in via go:embed. Note upstream is quiet (v1.3.0, Dec 2023), so its auto-update loop will be idle by design.

Tier W3 — heavier / needs a decision

Image Why it's not W1/W2
influxdb 1.1B pulls on library/influxdb, but that number is v1/v2 (Go, MIT); current upstream is InfluxDB 3 Core in Rust (🟢 Apache-2.0), no Wolfi package, and our time-series slot is already served by prometheus/mimir/victoria-metrics/thanos. Worth it only if we want line-protocol coverage.
emqx 47M pulls, but the repo is NOASSERTION (Apache core + BSL-ish enterprise pieces) and it's a large Erlang build. License read required first.
timescaledb 123M pulls, but the tree is split Apache-2.0 / TSL (source-available) — an Apache-only build is possible and is the only version we could ship. Own effort.
pgadmin4 444M pulls (🟢 PostgreSQL license), Python + prebuilt frontend. Plausible via pip wheels; needs its own investigation.

Confirmed avoid (🔴 non-OSS) — checked in this survey

terraform (492M) · vault (554M) · nomad · boundary · waypoint — all BUSL-1.1; already covered by our opentofu and openbao. elasticsearch / kibana / logstash — Elastic License; covered by opensearch. redpanda (35M) — BSL. n8n — Sustainable Use License. No change to the policy.

Suggested first PR out of this survey

Six images, all W1, no new build template, spanning four categories: gitlab-runner · wordpress · maven · gradle · kong · perl — the two largest demand numbers we're missing, the two cheapest Languages entries, and the first addition to Web Servers & Proxies since oauth2-proxy.

Execution model

  • One PR per tier group (~6–8 images). Each image: 10 registration points (see docs/onboarding.md) incl. a validated cron-enabled versions.yaml row — the check-autoupdate gate blocks the PR otherwise.
  • Build and prod+dev smoke-test every image locally before push (make <img>, make test-<img>, and assemble+test the -dev variant). Never push a failing image.
  • Registration inserts use Python (newline-safe), not bash $(...) (which strips newlines and glues YAML lines — same failure family as the batch-b dropped paren).