ethereum-optimism / ethereum-optimism/optimism
ops: docker buildx bake op-deployer / cannon cannot be built without a private-registry login
- Dominant language
- Go
- Stars
- 6.5k
- Forks
- 4k
- Avg merge
- 2d 15h
- Merged PRs (30d)
- 145
Description
## Symptom
```
docker buildx bake op-deployer
docker buildx bake cannon
```
fail locally with an authentication error: both targets base on `dhi.io/alpine-base:3.23`, a private hardened registry that requires a `docker login` most contributors do not have.
## Where
[`ops/docker/op-stack-go/Dockerfile`](https://github.com/ethereum-optimism/optimism/blob/68b009443f67cf830990e913bcbde8536cb15327/ops/docker/op-stack-go/Dockerfile#L288):
```dockerfile
FROM dhi.io/alpine-base:3.23 AS cannon-target
```
and [line 363](https://github.com/ethereum-optimism/optimism/blob/68b009443f67cf830990e913bcbde8536cb15327/ops/docker/op-stack-go/Dockerfile#L363):
```dockerfile
FROM dhi.io/alpine-base:3.23 AS op-deployer-target
```
These are the only two `dhi.io` references in the whole repo. Every other stage in the file resolves from a public registry — `alpine:3.21` (`builder_foundry`, `musl-libs`), `golang:1.26.5-alpine3.23`, `golang:1.26.5-bookworm`, `rust:1.95`.
## How it got there
`git log -S` pins it to one commit — `10ec3c55bc`, *"chore(docker): switch op-stack-go runtime base to chainguard/static (#20904)"*, which moved the shared base from `alpine:3.20` to distroless `chainguard/static`. Two stages genuinely could not live on distroless-static (cannon ships musl-linked multicannon embeds; op-deployer ships a `forge` binary and sets `FORGE_ENV=alpine`), so they had to stay on a real alpine. Instead of getting their own publicly-defaulted ARG they were hardcoded to the private registry. **Both stages built from public `alpine:3.20` before that commit** — this is a regression for local builds.
## The pattern already exists
The same file already has two publicly-defaulted base ARGs, including one added for exactly this situation — a stage with different runtime needs:
https://github.com/ethereum-optimism/optimism/blob/68b009443f67cf830990e913bcbde8536cb15327/ops/docker/op-stack-go/Dockerfile#L8-L15
`TARGET_BASE_IMAGE` is used by fourteen targets and `CHALLENGER_TARGET_BASE_IMAGE` by one.
## Fix assessment
Add a third ARG beside them and point both stages at it:
```dockerfile
# cannon ships musl-linked multicannon embeds and op-deployer ships forge, so
# neither can run on the distroless-static TARGET_BASE_IMAGE.
ARG ALPINE_TARGET_BASE_IMAGE=alpine:3.23
```
**~5 lines in one file** (3 added near line 15, 2 modified). Optionally +8 lines to declare the variable in `docker-bake.hcl` and add it to the `cannon` / `op-deployer` `args` maps — but an ad-hoc `--set '*.args.ALPINE_TARGET_BASE_IMAGE=…'` covers the override case without the HCL churn.
**Does the public base lack anything?** No:
* Neither stage runs `apk add` after its `FROM`, so there is no package-availability question (contrast `op-challenger-target`, which does and therefore genuinely depends on its base's apk repos).
* `USER 0` on both stages becomes a harmless no-op — plain alpine already runs as root; DHI images default to non-root, which is why those lines are there. They stay correct either way.
* op-deployer makes outbound HTTPS RPC calls and installs no certs itself, relying on the base bundle; alpine's minirootfs ships `ca-certificates-bundle`. Both stages ran on public `alpine:3.20` for months until #20904, so runtime parity is established historically.
* `3.20 → 3.23` is the only genuinely new variable, and it matches the alpine the Go builder stage already uses (`golang:1.26.5-alpine3.23`), so the musl the cannon embeds were linked against lines up.
**Risk: low, and lower than it looks.** These bake targets no longer build any production image. `cd560bfbfe` (*"ci: make apko images canonical, disable docker builds (#22257)"*) deleted `.github/workflows/build-images.yaml`; production cannon and op-deployer images now come from apko/melange (`melange/op-deployer.yaml`, `melange/multicannon.yaml`). There is no `docker login`, no `dhi` reference and no `buildx bake` invocation anywhere in `.circleci/` or `.github/` — the only remaining callers are the developer recipes in the root `justfile`, none of which pass a `*_BASE_IMAGE` arg. So the hardening requirement is satisfied by the apko pipeline, and defaulting the new ARG publicly while leaving it overridable costs nothing.
🤖 *Co-created with Claude Fable 5*
Contributor guide
Research direction
Start in ops/docker/op-stack-go/Dockerfile, especially the existing base-image ARGs and the cannon-target and op-deployer-target stages. Run docker buildx bake op-deployer and docker buildx bake cannon before and after the change, using an optional bake argument if needed. Done means both targets build without requiring a private-registry login while their base image remains overridable.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- docker, dockerfile
- Domain
- build-system, devops
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 88/100