Skip to content
Latchkey

How to Speed Up Jest in CI

Jest is fast locally but stumbles in CI when worker counts and caching are wrong. A few flags recover most of the gap.

Jest parallelizes across worker processes and caches transformed modules. In CI the defaults often misjudge the host, so setting workers, sharding, and persisting the cache matter.

1. Set workers explicitly

CI runners report misleading CPU counts. Pin --maxWorkers to the real core count to avoid oversubscription thrash.

.github/workflows/ci.yml
- run: npx jest --maxWorkers=4 --ci

2. Shard across machines

Jest supports native sharding to split the suite across runners.

.github/workflows/ci.yml
strategy:
  matrix:
    shard: [1/3, 2/3, 3/3]
steps:
  - run: npx jest --shard=${{ matrix.shard }} --ci

3. Persist the Jest cache

Cache the transform cache directory so transpilation is reused across runs.

.github/workflows/ci.yml
- uses: actions/cache@v4
  with:
    path: /tmp/jest-cache
    key: jest-${{ hashFiles('package-lock.json') }}
- run: npx jest --cacheDirectory=/tmp/jest-cache --ci

4. Run only affected tests on PRs

Use jest --changedSince=origin/main on PR runs so the suite scales with the diff, not the repo. Keep the full run on main.

Measure before you optimise

Pipeline optimisation usually targets the step people assume is slow. Get the real per-step timings first, because the answer is frequently dependency install or a cold cache rather than the build itself.

Terminal
# per-job timings for the last 20 runs
gh run list --limit 20 --json databaseId,conclusion,createdAt,updatedAt \
  --jq '.[] | "\(.conclusion)\t\(.createdAt)\t\(.updatedAt)"'

# per-step timing inside one run
gh run view <run-id> --log | grep -E "^\S+\s+.*Run |##\[group\]" | head -40

Key takeaways

  • Pin --maxWorkers to the real core count to avoid oversubscription.
  • Use --shard to spread tests across runners.
  • Persist the Jest transform cache and run --changedSince on PRs.

Frequently asked questions

How do I speed Up Jest in CI?
Jest parallelizes across worker processes and caches transformed modules. In CI the defaults often misjudge the host, so setting workers, sharding, and persisting the cache matter.
1. Set workers explicitly?
CI runners report misleading CPU counts. Pin --maxWorkers to the real core count to avoid oversubscription thrash.
2. Shard across machines?
Jest supports native sharding to split the suite across runners.
3. Persist the Jest cache?
Cache the transform cache directory so transpilation is reused across runs.
4. Run only affected tests on PRs?
Use jest --changedSince=origin/main on PR runs so the suite scales with the diff, not the repo. Keep the full run on main.

Related guides

References

Run this faster and cheaper on Latchkey managed runners - self-healing included. Start free → 30-day trial · No credit card