Skip to content

CI: cache Docker layers and parallelize image builds#43

Merged
PeterRounce merged 1 commit into
mainfrom
claude/ci-docker-cache
Jun 17, 2026
Merged

CI: cache Docker layers and parallelize image builds#43
PeterRounce merged 1 commit into
mainfrom
claude/ci-docker-cache

Conversation

@PeterRounce

Copy link
Copy Markdown
Member

Problem

The docker CI job rebuilt both images from scratch on every run. GitHub runners are ephemeral and the job used plain docker build with no persistent layer cache, so every PR redid:

  • the webproxy xcaddy build (~11 min) — recompiling Caddy + the caddy-ratelimit plugin, even though that 5-line Dockerfile almost never changes;
  • the card image's go mod download, npm ci, and apt-get install layers (work the build/frontend gate jobs already did);
  • and the two images built serially in one job.

The --mount=type=cache mounts in both Dockerfiles are dead weight in CI — they only live for one BuildKit instance, which dies with the runner.

Changes

  • GHA BuildKit layer cache via docker/setup-buildx-action + docker/build-push-action@v6 with cache-from/to: type=gha. Unchanged layers (xcaddy build, go mod download, npm ci, apt) are reused across runs.
  • Split into parallel jobs docker-card and docker-webproxy, each with its own cache scope so they don't evict each other in the 10 GB GHA cache.
  • docker-webproxy drops needs: — it's pure Caddy, independent of the Go/frontend gates, so it starts at t=0 instead of after they finish.
  • Pinned webproxy base images to caddy:2.11.4(-builder) (were the floating caddy:builder/caddy:latest) for stable cache keys and reproducibility.
  • Bumped version 0.22.10.22.2.

Push/login behaviour is unchanged: images still push to Docker Hub only on push to main, and release now waits on both new jobs (needs: [docker-card, docker-webproxy]).

Effect

A typical PR (webproxy untouched, card source changed) drops the Docker phase from ~15 min to roughly the card compile alone (~2–3 min), with webproxy a near-instant cache hit running in parallel.

Verification

  • ci.yml parses as valid YAML.
  • Config-only change; no application code touched.

🤖 Generated with Claude Code

The `docker` job rebuilt both images from scratch every run on ephemeral
runners with no persistent layer cache. The webproxy xcaddy build (~11 min)
and the card image's go mod download / npm ci / apt layers were redone on
every PR.

- Split into parallel `docker-card` and `docker-webproxy` jobs.
- Use docker/build-push-action@v6 with GHA layer cache (type=gha) under
  distinct scopes so the two images don't evict each other.
- docker-webproxy drops `needs:` — it's pure Caddy, independent of the Go
  and frontend gates, so it starts at t=0.
- Pin webproxy base images to caddy:2.11.4(-builder) for stable cache keys.
- Bump version 0.22.1 -> 0.22.2.

Effect: unchanged webproxy is a near-instant cache hit; a typical PR's
Docker phase drops from ~15 min to roughly the card compile alone.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@PeterRounce PeterRounce merged commit becbfb3 into main Jun 17, 2026
9 checks passed
@PeterRounce PeterRounce deleted the claude/ci-docker-cache branch June 17, 2026 12:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant