Query the Tag Matrix Before Composing a Base Image
Before hand-grafting a Golang toolchain onto a custom Alpine base, check Docker Hub's tag matrix. The combination you need is usually already published.
We hand-grafted the Golang toolchain onto alpine:3.20 just to pin a libvips version. Turns out golang:1.25.11-alpine3.22 had been sitting on Docker Hub the whole time. The combination was already published, and we never checked.
An official language image on Docker Hub publishes a matrix (a grid of available version combinations), not a single pairing. Every intersection of golang:<go>-alpine<alpine> is a selectable tag:
curl -s "https://hub.docker.com/v2/repositories/library/golang/tags?page_size=100&name=alpine3.22" \
| jq -r '.results[] | "\(.name) \(.tag_last_pushed[:10])"'
3.20 3.21 3.22 3.23 3.24
go 1.25.11 - - yes yes yes
go 1.25.12 - - NONE yes yes <- window slid
last built 2025-05 2025-12 2026-06 live live
tag_last_pushed is the liveness signal, not tag existence. A date frozen six months ago means a frozen branch: it still pulls, but it will never be rebuilt, and therefore never receives security patches (see An EOL base image silently no-ops security upgrades).
The matrix is a sliding window of roughly 3 base releases. Pin the base side and you eventually freeze the language side, because the trailing edge stops being built.
Two couplings, don’t conflate them: language × base is selectable within the window; base × its system libraries is genuinely welded: no tag changes what apk hands you on a given release.
Check the matrix before hand-composing. The combination you need is usually already published; when it isn’t, that absence is the real justification for a graft.
Sources:
Related: Back to the chain reaction overview, or dive deeper into grafting the Golang toolchain, pinning with an exit plan, or EOL base image risks.