From 77d560ddaece0f30566fef420a8b27b8154e03fd Mon Sep 17 00:00:00 2001 From: Nguyen Minh Phuc Date: Sat, 18 Jul 2026 11:24:44 +0000 Subject: [PATCH] ci: move buildx layer cache to the registry act_runner's cache PVC is 1Gi and also holds .runner, the runner's own registration file. --cache-to type=gha,mode=max for three images is several GB; filling that volume breaks the runner, not just the cache. act_runner also evicts by age with no size cap, so it fills whatever it is given. type=registry has no such limit and lives beside the images it caches. The uv cache still uses the runner's cache service, which is a few hundred MB. Registry login is no longer gated to master: the build now reads and writes the cache on every run. Pushing the release image keeps its own master-only gate. --- .gitea/workflows/ci.yaml | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/.gitea/workflows/ci.yaml b/.gitea/workflows/ci.yaml index 2aec22e..986d012 100644 --- a/.gitea/workflows/ci.yaml +++ b/.gitea/workflows/ci.yaml @@ -211,7 +211,9 @@ jobs: - uses: docker/setup-buildx-action@c47758b77c9736f4b2ef4073d4d51994fabfe349 # v3.7.1 - name: registry login - if: github.ref == 'refs/heads/master' && github.event_name == 'push' + # Not gated to master any more: the build step now reads AND writes the layer cache + # in the registry, so every run needs credentials. Pushing the release image is + # still master-only — that gate lives on the `push by digest` step, where it belongs. uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0 with: registry: ${{ env.REGISTRY }} @@ -226,12 +228,26 @@ jobs: # Loaded locally, not pushed. Trivy scans this exact image next; only then does it # get pushed. The alternative — push, scan, and hope nobody pulled meanwhile — is # how a CRITICAL ends up in the registry with a green checkmark next to it. + # + # The layer cache is `type=registry`, NOT `type=gha`. Two reasons, both hard: + # + # 1. act_runner's cache server is backed by a 1Gi PVC that also holds `.runner`, + # the runner's own registration file. `mode=max` stores every intermediate layer + # of three images — several GB. Filling that volume does not merely lose the + # cache: the runner cannot write its state and has to be re-registered by hand. + # Trading "slow CI" for "broken CI" is not a trade. + # 2. act_runner evicts by AGE, with no size cap in its config. It will fill whatever + # it is given and then wedge. The registry has no such limit and already holds + # the images anyway. + # + # The uv/pip cache still uses the runner's cache service — that one is a few hundred + # MB and fits. run: | docker buildx build \ -f services/${{ matrix.svc }}/Dockerfile \ --build-arg BUILD_SHA=${{ github.sha }} \ - --cache-from type=gha,scope=${{ matrix.svc }} \ - --cache-to type=gha,mode=max,scope=${{ matrix.svc }} \ + --cache-from type=registry,ref=${REGISTRY}/${IMAGE_NS}/svcforge-${{ matrix.svc }}:buildcache \ + --cache-to type=registry,ref=${REGISTRY}/${IMAGE_NS}/svcforge-${{ matrix.svc }}:buildcache,mode=max \ --load \ -t svcforge/${{ matrix.svc }}:ci \ . @@ -281,7 +297,7 @@ jobs: docker buildx build \ -f services/${{ matrix.svc }}/Dockerfile \ --build-arg BUILD_SHA=${{ github.sha }} \ - --cache-from type=gha,scope=${{ matrix.svc }} \ + --cache-from type=registry,ref=${REGISTRY}/${IMAGE_NS}/svcforge-${{ matrix.svc }}:buildcache \ --push \ -t "${IMAGE}:${GITHUB_SHA}" \ .