Skip to content

Image Tags and Rebuild Policy

Serge Gatezh edited this page Oct 8, 2026 · 3 revisions

Image Tags and Rebuild Policy

What latest means, when it moves, and what to pin instead. Read this before depending on any of these images.


latest moves without your input

Every image publishes :latest, and :latest is repointed on every rebuild. A rebuild happens when:

  1. A pinned tool releases. Renovate opens a version-bump PR, CI verifies it, it auto-merges, and the merge triggers the image build. No human is involved.
  2. A file the image is built from changes on master — the Dockerfile, or a file it copies in (support scripts, managed settings). Each image's build workflow lists them under on.push.paths.
  3. Someone dispatches the workflow manually from the Actions UI.

Case 1 is the one to understand: @anthropic-ai/claude-code publishes roughly twice a day, so claude-code:latest can move more than once in a day with no commit you would recognize as a change.

If you need reproducibility, do not use :latest.


Tag scheme per image

There are two schemes, because there are two build pipelines.

Composed version tags

Image Tags
bun latest, bun<BUN>-alpine — e.g. bun1.3.5-alpine
claude-bun latest, bun<BUN>-slim
hugo-bun latest, hugo<HUGO>-bun<BUN>-alpine — e.g. hugo0.155.1-bun1.3.8-alpine
hugo-bun-node latest, hugo<HUGO>-bun<BUN>-node<NODE>-alpine
ralphex-fe latest, bun<BUN>-hugo<HUGO>

The version tag is derived from the ARGs in the Dockerfile at build time. These tags are not immutable — rebuilding without changing a version republishes the same tag. They identify a toolchain, not a build.

SHA and date tags

Image Tags
claude-code latest, <git-sha>, <YYYYMMDD>
claude-code-sandbox latest, <git-sha>, <YYYYMMDD>

No composed version tag, because the project's .mise.toml decides its toolchain — a version in the image tag would be meaningless.

<git-sha> is the only genuinely immutable tag published by this repo. Pin to it if you need a build that cannot change under you:

// .devcontainer/devcontainer.json
{
  "image": "ghcr.io/gatezh/devcontainers/claude-code:a1b2c3d"
}

What to pin

Need Use
Reproducible builds, CI, anything auditable claude-code:<git-sha>
A stable toolchain, tolerant of rebuilds the composed version tag (hugo0.155.1-bun1.3.8-alpine)
Newest agent tooling, will tolerate surprises :latest

There is no v1-style moving major tag. If you want one, open an issue.


Which tools Renovate tracks

Twelve pinned ARGs across four images. Renovate's only manager is a regex over # renovate: comments in the Dockerfiles, so a version without that comment is never bumped.

Tool Source Images Update delay
@anthropic-ai/claude-code npm claude-code, ralphex-fe none — always latest
rtk-ai/rtk GitHub releases claude-code, ralphex-fe 3 days
umputun/ralphex GitHub releases claude-code, ralphex-fe 3 days
agent-browser npm claude-code 3 days
cli/cli (gh) GitHub releases claude-code, claude-bun 3 days
github/gh-stack GitHub releases claude-code 3 days
cf (Cloudflare CLI) npm claude-code 3 days
docker/cli GitHub tags ralphex-fe 3 days
go Go releases ralphex-fe 3 days
node (24.x only) Node.js releases hugo-bun-node 3 days
bun npm ralphex-fe 3 days
gohugoio/hugo GitHub releases ralphex-fe 3 days

The three-day delay (minimumReleaseAge) is deliberate. These bumps merge without human review and the merge publishes to ghcr.io, so a release is left to soak before adoption. Claude Code is exempt on purpose — it is tracked at latest.

Bumps arrive in two grouped PRs, each carrying whichever tools are currently eligible: devcontainer tools for the first ten rows, and ralphex-fe toolchain for Bun and Hugo, the two versions in ralphex-fe's tag. node is held to 24.x, so a new Node major stays a deliberate change.

Not every baked-in version is tracked: Bun in bun, claude-bun, hugo-bun and hugo-bun-node, and Hugo in the two hugo-bun images, carry no # renovate: comment and move only by hand.

No upstream release means no PR means no rebuild. This replaced an older nightly rebuild cron: images are no longer rebuilt on a timer, so a digest that has not changed means nothing upstream changed.

Base images (node:24-trixie-slim, alpine:3.21) and GitHub Actions pins are not tracked by Renovate — deliberately, to keep the update stream to the tools above.


Verifying what you actually got

# What's the current digest of a tag?
docker buildx imagetools inspect ghcr.io/gatezh/devcontainers/claude-code:latest

# What tool versions are inside?
docker run --rm --entrypoint sh ghcr.io/gatezh/devcontainers/claude-code:latest \
  -c 'claude --version && rtk --version && ralphex --version && agent-browser --version && gh --version && cf --version && mise --version'

Every build also runs a verification step in CI that asserts each tool is present and executable before publishing.


Platform support

All images are built for linux/amd64 and linux/arm64 on native runners — no QEMU emulation, so arm64 builds are tested rather than assumed. Pre-merge CI builds amd64 only; arm64 is built and verified at merge time.


Sources

  • #116 — replaced the nightly rebuild cron with Renovate-driven rebuilds
  • #120 — documented the four agent tools as pinned, not always-latest
  • #121 — first grouped agent-tools bump
  • #126 — made auto-merge actually fire; added the 3-day soak with claude-code exempt
  • #134 — Bun and Hugo bumps for ralphex-fe, as their own group
  • #146 — widened tracking to gh, the docker CLI, Go and Node
  • #167 — gh-stack
  • #204 — cf, tracked through its npm prereleases while it's in beta
  • #205 — claude-code rebuilds when its managed skill or agent-browser config changes
  • Tag schemes are defined in .github/workflows/build-*.yml; the update policy is in .github/renovate.json5

Last verified: 2026-10-08