Free — no signup required

Caching: Speeding Up the Process

3 min read

Artifacts vs. Caching — A Critical Distinction

Both artifacts and caches involve saving files, but they serve opposite purposes and are frequently confused.

Artifact Cache
Purpose Pass essential output forward within a run Speed up future runs
Scope Within a single pipeline run Across multiple pipeline runs
If lost Pipeline fails Pipeline runs slower
Contents Build outputs, test reports, binaries Dependencies, compiled libraries

The Dependency Download Problem

Every pipeline run typically needs to download project dependencies: node_modules for JavaScript, .m2/repository for Maven, vendor/ for Go, or pip packages for Python. On a cold run, this can take 2–5 minutes and consume hundreds of megabytes of bandwidth. Multiply that by 50 pipeline runs per day across a team, and you're wasting hours of developer time and significant network costs.

A cache stores these downloaded directories between runs:

  1. Run 1 (cache miss): The pipeline checks for a cached copy of node_modules. None exists. It runs npm install, downloading all dependencies. At the end, it saves node_modules to the cache, keyed by a hash of package-lock.json.
  2. Run 2 (cache hit): The pipeline checks for a cache. The package-lock.json hash matches. It restores node_modules from cache in seconds. npm install either skips entirely or runs in milliseconds.
  3. Run 3 (cache invalidation): A developer updates package-lock.json. The hash no longer matches. The pipeline treats this as a cache miss, downloads fresh dependencies, and saves a new cache entry.

The cache key — typically a hash of your dependency manifest file — is what makes this safe. If dependencies change, the cache is automatically invalidated.

Docker Layer Caching

When building Docker images, each instruction in a Dockerfile (RUN, COPY, ADD) creates an immutable layer. Docker can reuse a cached layer if the instruction and all preceding layers are unchanged.

The practical implication: structure your Dockerfile so that slow, rarely-changing steps (like RUN npm install) come before fast, frequently-changing steps (like COPY . .). If you copy your source code first and then install dependencies, every code change invalidates the dependency layer and forces a full reinstall.

# Good: dependencies installed before source code is copied
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./        # Only changes when deps change
RUN npm ci                   # Cached unless package*.json changes
COPY . .                     # Changes on every commit — but layer above is cached
RUN npm run build

In CI/CD environments, you can pass --cache-from to docker build to pull a previously built image and use its layers as a cache source, even across different runner machines.

Interview Tip

A common interview question is: "Your Docker builds are taking 8 minutes in CI. How would you speed them up?" The expected answer covers two things: (1) restructure the Dockerfile to put slow, stable layers (dependency installs) before volatile layers (source code copies), and (2) use --cache-from with a registry-stored image to share layer cache across ephemeral runners. Mentioning both layer ordering and cross-runner cache sharing signals senior-level understanding.

This is one of 18 chapters

Every chapter is free — Kubernetes, Terraform, SRE, distributed systems, and more — with fast daily review built in.

Sign up free