Skip to main content

How to speed up CI builds with caching

Cache dependencies and build outputs in CI to cut pipeline time. Covers choosing cache keys, restore keys for partial hits, and avoiding stale state.

Difficulty
Intermediate
Duration
35 minutes
Steps
6

What and why

Most CI time goes to downloading dependencies and recompiling code. Caching stores those artifacts between runs and restores them when inputs are unchanged. Done correctly it cuts pipeline time dramatically; done wrong it serves stale data. This tutorial caches dependencies safely.

Prerequisites

  • A working CI pipeline.
  • A project with a lockfile (such as package-lock.json or go.sum).
  • Basic YAML knowledge.

Steps

1. Identify what to cache

Cache directories that are expensive to recreate and deterministic from inputs: the package cache, node_modules, or a compiler cache. Do not cache final deployable artifacts.

2. Build a stable cache key

The key must change only when inputs change. Hash the lockfile:

key: deps-${{ runner.os }}-${{ hashFiles('**/package-lock.json') }}

A new lockfile produces a new key, forcing a fresh install.

3. Add the cache step

      - uses: actions/cache@v4
        with:
          path: ~/.npm
          key: deps-${{ runner.os }}-${{ hashFiles('**/package-lock.json') }}

On a hit the path is restored before install; on a miss it is saved after the job.

4. Use restore keys for partial hits

          restore-keys: |
            deps-${{ runner.os }}-

If the exact key misses, a prefix match restores a recent cache, so install only fetches the delta.

5. Avoid caching stale state

Never include the cache in your build inputs, and exclude environment-specific files. Always run a clean, lockfile-driven install (such as npm ci) so a corrupt cache cannot poison results.

Verification

Run the pipeline twice. The first run shows a cache miss and saves the cache. The second shows a cache hit and a much faster install step. Compare total job times in the run summary.

Next Steps

Cache compiler outputs (such as the Go build cache) for further gains. Set a cache size budget and let old caches expire. Verify a clean run still works by temporarily disabling the cache.

Prerequisites

  • A working CI pipeline
  • A project with a dependency lockfile
  • Basic YAML knowledge

Steps

  • 1
    Identify what to cache
  • 2
    Build a stable cache key
  • 3
    Add the cache step
  • 4
    Use restore keys for partial hits
  • 5
    Avoid caching stale state
  • 6
    Measure the improvement

Category

CI/CD