Choosing Between CircleCI Native Caching and Docker Layer Caching
Decide when to use CircleCI save_cache versus Docker Layer Caching. Compare trade-offs, see a config example, and validate cache hits with concrete checks.
12 Oct 2026, 23:50 UTC

The decision: dependency downloads or image rebuilds?
When a CircleCI pipeline feels slow, the instinct is often to "add caching." But CircleCI offers two distinct mechanisms that solve different problems. Native caching (save_cache and restore_cache) persists directories such as node_modules or .m2 between jobs. Docker Layer Caching (DLC) reuses layers from a previously built Docker image so that docker build can skip unchanged instructions. Choosing the wrong mechanism adds upload and restore overhead without meaningfully reducing build time.
Constraints that drive the choice
Before editing your .circleci/config.yml, identify what actually dominates your build duration and which executor you are using.
- Executor type: Native caching works in the Docker, machine, and macOS executors. DLC depends on Docker being available and is commonly used with the machine executor or a Docker executor configured with
setup_remote_docker. - Bottleneck: If
npm ci,pip install, ormvn dependency:go-offlineconsumes most of the job time, native caching is the direct fix. Ifdocker buildrepeatedly reinstalls OS packages or recompiles unchanged source, DLC is the better fit. - Cache stability: Native cache keys are usually tied to a lock file checksum, so they change only when dependencies change. DLC layers are invalidated whenever an earlier Dockerfile instruction changes, which can discard many downstream layers at once.
Native caching vs DLC at a glance
| Aspect | Native caching (save/restore) | Docker Layer Caching (DLC) |
|---|---|---|
| Primary target | Language package directories | Docker image layers |
| Key mechanism | Explicit keys (lock file checksums) | Instruction hashes |
| Executor requirements | All standard executors | Docker (Machine or Remote Docker) |
| Invalidation trigger | Change in checksum template | Change in Dockerfile instruction |
| Main overhead risk | Large archive transfer times | Early Dockerfile changes invalidate all |
Trade-offs in practice
Native caching gives you precise control. You decide exactly which paths to persist and which checksum triggers a refresh. The cost is operational: every save uploads a tarball, and every restore downloads it. A bloated node_modules directory can make the cache step slower than the install it was meant to replace.
DLC shifts that burden to the Docker daemon. When a layer is cached, docker build prints CACHED next to the instruction and moves on immediately. The trade-off is granularity: reordering COPY commands or changing a base image tag can invalidate a long chain of layers. DLC also does not help if the build is cold—the first run after enabling it still executes every instruction.
Implementation: using both where they fit
The following configuration shows a job that uses native caching for Node.js dependencies and then builds a Docker image with DLC. Replace cimg/node:20.5 and ubuntu-2204:current with the image tags your project actually uses.
version: 2.1
jobs:
build:
machine:
image: ubuntu-2204:current
steps:
- checkout
- restore_cache:
keys:
- v1-node-{{ checksum "package-lock.json" }}
- v1-node-
- run:
name: Install dependencies
command: npm ci
- save_cache:
key: v1-node-{{ checksum "package-lock.json" }}
paths:
- node_modules
- run:
name: Build application image
command: docker build -t myapp:$CIRCLE_SHA1 .
In this example, the restore_cache step looks for an exact checksum match first. If none exists, it falls back to the most recent v1-node- cache. The save_cache step then writes a new archive keyed to the current lock file. Because the job runs on the machine executor, Docker is available locally and DLC can reuse layers from prior builds of the same Dockerfile.
If you prefer the Docker executor, add setup_remote_docker before the build step. Note that DLC behavior and availability can vary by CircleCI plan and executor configuration, so confirm the current requirements in the CircleCI documentation before relying on it for critical paths.
Validation and limitations
After committing the configuration, verify that caching is actually helping rather than hurting.
- Inspect the job UI: Expand the
Restore cacheandSave cachesteps and compare their timestamps. If a large cache takes longer to transfer than the command it protects, narrow the cached paths or split the cache. - Check the logs: Look for
Attempting to restore cachemessages to confirm a hit. For DLC, look forCACHEDin thedocker buildoutput. - Compare cold and warm runs: Trigger a build after clearing caches, then trigger another. The warm build should show reduced time in the install or build steps, not just in the cache steps themselves.
Both mechanisms have hard limits. Native caches are subject to storage quotas and eviction policies, so a rarely used cache may disappear before the next build. DLC does not persist indefinitely and is most effective for frequently built images with stable base layers. If your Dockerfile changes often in its early instructions, DLC will frequently miss, and you may see better results by pinning base images and ordering COPY commands from least to most frequently changing.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.