← All articles

GitHub Actions: seven patterns for genuinely fast pipelines

15 July 20242 min read

Clean cache, parallel jobs, smart matrix. From 14 minutes down to 3 on a real build of ours.

"CI is too slow" is the most frequent complaint in growing teams. On our projects we cut pipelines from 14 minutes to 3 by applying these seven patterns.

1. npm/pnpm/yarn cache

actions/setup-node has built-in cache: 'pnpm'. For projects with 600+ deps: 3 minutes → 35 seconds on install.

2. Next.js / Vite build cache

Cache .next/cache or node_modules/.vite:

- uses: actions/cache@v4
  with:
    path: .next/cache
    key: nextjs-${{ hashFiles('**/package-lock.json') }}-${{ hashFiles('**/*.{ts,tsx}') }}

3. Parallel jobs, not serial

Lint, type-check, test, build are independent. Stop chaining them. Separate jobs running in parallel: −50% wall time.

4. Test sharding

Vitest and Jest support --shard. With a 4-shard matrix, an 8-minute suite runs in 2.

5. Skip useless jobs with paths-filter

If a PR only touches docs/, why run the whole CI? With dorny/paths-filter we filter early and skip unnecessary jobs.

6. Self-hosted runner for heavy builds

For projects with builds above 10 minutes on standard runners, a self-hosted runner (even Hetzner CX31 at €12/mo) can halve the time. Annual cost < one month of GitHub Pro extra minutes.

7. Per-branch concurrency lock

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

Successive pushes cancel previous builds — less waste, fresher deploys.

Bonus

Always measure before optimising. The Actions panel shows step-by-step time. Often the problem is 1 step out of 12 — not 12 steps to shorten.