Skip to content
Latchkey

How to Parallelize a Monorepo Build in CI

A monorepo that builds every package in one serial job wastes the whole point of independent packages. Fanning them out cuts wall-clock time dramatically.

Parallelizing a monorepo means running independent packages concurrently while honoring the dependency graph. Combined with affected detection, only the changed slice of the graph runs at all.

1. Let the task runner parallelize

Nx and Turborepo schedule tasks across the dependency graph and run independent ones concurrently.

.github/workflows/ci.yml
- run: npx turbo run build --concurrency=10

2. Fan out across jobs with a matrix

Generate a matrix of affected projects and run each in its own job for true machine-level parallelism.

.github/workflows/ci.yml
jobs:
  affected:
    runs-on: ubuntu-latest
    outputs:
      projects: ${{ steps.list.outputs.projects }}
    steps:
      - id: list
        run: echo "projects=$(npx nx show projects --affected --json)" >> $GITHUB_OUTPUT
  build:
    needs: affected
    strategy:
      matrix:
        project: ${{ fromJson(needs.affected.outputs.projects) }}
    runs-on: ubuntu-latest
    steps:
      - run: npx nx build ${{ matrix.project }}

3. Respect the dependency graph

A package cannot build before its dependencies. Let the task runner topologically order tasks; do not hand-build a matrix that ignores the graph.

4. Scale runners without per-machine cost surprise

Fanning out multiplies concurrent runner usage. Latchkey managed runners are about 69 percent cheaper than hosted runners, so wide parallelism does not blow up the bill. Check the impact with the cost calculator at /learn/github-actions-cost-calculator.

Key takeaways

  • Let the task runner schedule independent packages concurrently.
  • Fan affected projects across a job matrix for machine-level parallelism.
  • Honor the dependency graph; do not parallelize past it.

Related guides

Run this faster and cheaper on Latchkey managed runners. Start free →