EcoInfra 3/8: Build-time energy, the invisible kilowatts in your CI
CI is the invisible second runtime of your code, and almost nobody measures it.
EcoInfra 3/8: Build-time energy, the invisible kilowatts in your CI

CI is the invisible second runtime of your code, and almost nobody measures it. A pull request, merged 50 times a day, rebuilt 8 times by branch protection, with a cold cache and a :latest tag base image, can burn more energy than the application it ships will ever consume in a week of production.
This article will spot some rules, and map those rules onto eco-design axis 2, build-time energy.
1. The big picture

Runner energy ≈ wall-time × runner TDP, per Cloud Carbon Footprint methodology. Same green build, 25× less electricity.
2. Why build-time energy is important
GitHub publishes billions of Actions-minutes per month.
The CNCF Sustainability TAG flagged CI runners in 2023 as the largest unmeasured energy line in the cloud-native stack (CNCF Sustainability Whitepaper). Three independent drivers compound:
- **Cache misses: **30 to 70 % of CI wall-time is dependency download when caches are missing or invalidated (GitHub Docs — caching).
- **Redundant runs: **pushing 4 commits in 10 minutes triggers 4 full pipelines unless you opt into concurrency control.
- **Mutable tags: **every silent base-image republish (
ubuntu:latest,node:lts,tj-actions/changed-files@v35) invalidates the local layer cache and forces a cold build (Sysdig 2024).
And the supply-chain angle reinforces the eco-design angle: the March 2025 tj-actions/changed-files attack (GitHub Security Lab post-mortem, StepSecurity report) injected a credential-stealing payload into thousands of pipelines for 24 hours. Every unpinned action is both a security risk and a cache-miss waiting to happen.
3. The levers
Lever A: Order Dockerfile instructions for cache hits
BuildKit invalidates every layer downstream of a changed input. Putting COPY . . before RUN pip install means each code edit re-downloads the full dependency tree. Move volatile inputs last.

Expected gain: cache hit-rate goes from ~40 % to ~85 % on cached PRs, build wall-time −30 to −70 % (Docker BuildKit cache).
Lever B: Pin the base image by digest / forbid
:latest

Pinned digests mean the same input, the same cache, every build (OCI Image Spec). The official Docker best-practices guide is explicit about it.
Lever C: Cache your dependencies
Here some hints for the common builders, actions/cache, setup-node cache key, Maven's ~/.m2, Gradle's ~/.gradle/caches, pip's ~/.cache/pip, npm/pnpm's store.
Caching is the cheapest CI optimisation on the planet:

GitHub’s own benchmark cites 30 to 60 % build-time reduction on hot-path repos (GitHub Docs).
Lever D: Skip redundant jobs

Add paths-filter so doc-only PRs don't rebuild the JVM, skip drafts unless ready-for-review. CI-minutes typically drop −20 to −50 % with zero functional cost.
Lever E: Pin the runner & prefer ARM

ubuntu-latest quietly migrates between LTS releases and invalidates every runner-local cache.
GitHub announced ARM64 runners on Actions in June 2024 (GitHub blog); they are 37 % more energy-efficient than x86 on equivalent workloads (Cloudflare ARM, AnandTech's Graviton3 deep dive).
Lever F: Pin third-party actions to full SHA

Recommended by GitHub’s own hardening guide and by NIST SP 800–218 SSDF. Same lever covers eco and supply-chain.
4. The decision tree
Is the workflow rebuilt on every commit?
├── YES → add `concurrency.cancel-in-progress` (Lever D)
│ then `paths-filter` so docs/markdown changes don't rebuild code
└── Does it pull dependencies from the network?
├── YES → `actions/cache` or `setup-*` cache key (Lever C)
└── Does it build a Docker image?
├── YES → fix layer order (Lever A) → pin base by digest (Lever B)
└── Is the runner `ubuntu-latest`?
├── YES → pin to `ubuntu-22.04-arm` (Lever E)
└── Are 3rd-party actions pinned to SHA? (Lever F)
├── NO → pin them, today, before the next supply-chain incident
└── DONE
You don’t have to do all six on day one. Do Lever D first (it’s a 3-line YAML edit and reaps the most CO₂ per second of effort), then lever C, then lever E, then the Dockerfile-side levers when the next image refresh comes around.
5. What it’s worth, in numbers
| Lever | Typical reduction | Source |
|------------------------|-----------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Layer order fix (A) | Cache hit-rate +30 to +80 % | [Docker BuildKit](https://docs.docker.com/build/cache/) |
| Pin base by digest (B) | Cache miss rate −50 to −90 % | [OCI Image Spec](https://github.com/opencontainers/image-spec), [Sysdig 2024](https://sysdig.com/2024-cloud-native-security-and-usage-report/) |
| Dependency cache (C) | Build wall-time −30 to −60 % | [GitHub Docs](https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows) |
| Cancel-in-progress (D) | CI minutes −20 to −50 % | [GSF Patterns](https://patterns.greensoftware.foundation/) |
| ARM runners (E) | Runner energy −30 to −40 % | [GitHub blog 2024](https://github.blog/2024-06-03-arm64-on-github-actions-powering-faster-more-efficient-build-systems/), [Cloudflare 2018](https://blog.cloudflare.com/arm-takes-wing/) |
| Pin actions to SHA (F) | Cache stable + 0 supply-chain re-builds | [GH-Hardening](https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions) |
Stack all six on a mid-sized monorepo, and you typically divide CI-minutes by 3 to 5. That is the largest single eco-design lever after region selection (see article 7).
6. Beyond static rules: orchestrate measurement
Tools like creedengo-infra flags the source-side patterns through CI workflows, and layer order / pinning. For the byte-level evidence, you need runtime observation. Here some useful companions:
a- **eco-ci**: Green Coding Berlin’s energy estimator for GitHub Actions runners, exposes joules per job in the workflow summary.
b- **Kepler**: kernel-level eBPF energy attribution; runs on the CI cluster, exports Prometheus metrics per pod.
c- **Cloud Carbon Footprint: the methodology behind every “**CI runner = X gCO₂” estimation in this article.
Wire them once, leave them running, and the dashboard becomes the regression test.
7. Want to learn more?
- Docker: BuildKit cache and Dockerfile best practices
- GitHub: Caching dependencies and Security hardening
- GitHub Blog (Jun 2024): ARM64 on GitHub Actions
- Cloudflare (2018): ARM takes wing — 57 % more requests per watt
- GitHub Security Lab (Mar 2025): tj-actions/changed-files supply-chain attack]()
- CNCF Sustainability TAG: Cloud Native Sustainability Whitepaper
- Green Software Foundation : Patterns Catalog
Next in the series EcoInfra 4/8: Runtime energy, what your pods do when nobody’s watching.
메타데이터
- post_id
- 5aa04cfefbef
- slug
- ecoinfra-3-8-build-time-energy-the-invisible-kilowatts-in-your-ci-5aa04cfefbef
- url
- https://medium.com/just-tech-it-now/ecoinfra-3-8-build-time-energy-the-invisible-kilowatts-in-your-ci-5aa04cfefbef
- canonical_url
- https://medium.com/just-tech-it-now/ecoinfra-3-8-build-time-energy-the-invisible-kilowatts-in-your-ci-5aa04cfefbef
- author_url
- https://medium.com/@moustaphadaka
- status
- ok
- fetched_at
- 2026-06-12 18:14:10