Speeding Up Slow CI Pipelines in GitHub Actions
A CI pipeline that takes 25 minutes slows down every pull request. Most pipelines can be made several times faster with a few changes.
1. Cancel outdated runs
concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
When you push again to the same branch, the previous run stops instead of finishing pointlessly.
2. Only run what changed
on:
pull_request:
paths:
- 'api/**'
- 'package-lock.json'
- '.github/workflows/api.yml'
In a monorepo, separate workflows per app with path filters avoid testing the mobile app when only the docs changed. Be careful with required checks: a skipped workflow can block merging, so mark those checks as required only where they always run, or use a small "gate" job.
3. Run independent jobs in parallel
jobs:
lint: { runs-on: ubuntu-latest, steps: [...] }
typecheck: { runs-on: ubuntu-latest, steps: [...] }
unit: { runs-on: ubuntu-latest, steps: [...] }
e2e:
needs: [unit] # only what truly depends on something waits
runs-on: ubuntu-latest
4. Cache everything you download
- Package manager caches via
setup-node,setup-pythonand friends. - Build tool caches (Next.js, Turborepo, Gradle, Cargo).
- Docker layers with
type=ghacaching.
5. Split slow test suites
strategy:
matrix: { shard: [1, 2, 3, 4] }
steps:
- run: npx playwright test --shard=${{ matrix.shard }}/4
6. Bigger or faster runners
Larger GitHub-hosted runners (more CPU cores) can be cheaper overall if they halve the run time of CPU-bound builds.
Measure first
Look at step timings in a few recent runs. Often one step (a full reinstall, an unnecessary build, a slow E2E suite) is most of the time, and fixing it is the biggest win.
Comments
No comments yet. Have a question or a tip? Be the first to share it.