Webpack Filesystem Cache: Taming Rebuild Latency Without Breaking Correctness
Webpack 5's filesystem cache can cut rebuild latency, but it is a correctness-sensitive build input. Learn how to configure it safely with buildDependencies and cache naming.
26 Apr 2026, 14:47 UTC

You save a file, wait for the terminal to settle, and watch webpack re-run the same loaders over modules that haven't changed. On a large application, that redundant work turns a small edit into a coffee break. Webpack 5's filesystem cache exists to skip it, but the cache is not a simple speed toggle. It is a correctness-sensitive build input. Treat it carelessly and you risk stale chunks, hidden configuration drift, and CI failures that only appear after the cache is warm. Treat it deliberately and rebuilds get faster without sacrificing confidence in the output.
Why Rebuilds Re-Do Work Webpack Could Reuse
Webpack builds a module graph: a map of every file your entry point depends on, plus the loaders and plugins that transform them. A loader is a function that turns a file into a module webpack understands—for example, transpiling TypeScript or compiling Sass. A plugin hooks into the build lifecycle to emit assets, optimize bundles, or inject environment variables.
By default, when you rebuild, webpack re-processes modules through those loaders and plugins even if the source file is identical to the last run. The filesystem cache changes that. It persists the results of loader and plugin work to disk so that subsequent builds can reuse them instead of recomputing. The benefit is proportional to how expensive your transforms are and how many modules remain unchanged between builds.
The Configuration Surface That Controls Reuse
Enabling the cache is one line, but the surrounding options decide whether reuse is safe. The core shape is cache: { type: 'filesystem' }. Around that, four fields matter most:
- cacheDirectory: where cache entries live. The default is inside
node_modules/.cache/webpack, which is convenient but easy to delete accidentally. - name: a string or function that separates cache entries. If you build in both development and production mode, or ship multiple packages from one repo, distinct names prevent incompatible cache reuse.
- version: a string you control. Bump it when the webpack version, mode, or dependency set changes in a way that makes old entries unsafe.
- buildDependencies: an object telling webpack which external files affect the build. The most important key is
config, an array of file paths. If a listed file changes, webpack invalidates the affected cache.
Persisted reuse depends on more than just the source files. Webpack considers its own version, the build mode, the resolved dependency tree, and the identity of the config files and toolchain inputs. If any of those drift without the cache knowing, you can get a warm build that emits stale output.
A Minimal Config for Persistent Caching
Below is a conservative starting point. It assumes webpack 5.x; because cache options are version-sensitive, verify the exact field names against your installed webpack version with npx webpack --version before relying on them.
// webpack.config.js
const path = require('path');
const pkg = require('./package.json');
module.exports = (env, argv) => {
const isProduction = argv.mode === 'production';
return {
mode: isProduction ? 'production' : 'development',
cache: {
type: 'filesystem',
cacheDirectory: path.resolve(__dirname, '.webpack-cache'),
// Separate caches by package version and mode
name: `app-${pkg.version}-${isProduction ? 'prod' : 'dev'}`,
// Bump manually when toolchain changes make old entries unsafe
version: 'v1',
buildDependencies: {
// Invalidate when this config or generated configs change
config: [
__filename,
path.resolve(__dirname, 'generated-config.js'),
],
},
},
// ...entry, output, module.rules, plugins...
};
};
Run these commands from your project root, where package.json lives. They require Node.js and a locally installed webpack.
- Cold build:
rm -rf .webpack-cache && npx webpack --mode production. Expected check: the build completes and.webpack-cachecontains populated entries. - Warm build:
npx webpack --mode production. Expected check: wall-clock time is lower than the cold run, and cache entries are reused rather than regenerated. - Config invalidation: edit
webpack.config.jsand rebuild. IfbuildDependencies.configincludes that file, affected cache is invalidated. - Toolchain invalidation: change a tracked input such as the lockfile or a loader version, update the
nameorversionfield accordingly, and rebuild. Expected check: the cache naming strategy forces new entries instead of reusing incompatible ones.
Do not assume a specific speedup. Build time depends on module count, loader cost, disk speed, and whether the cache was warm. Measure cold versus warm wall-clock time in your own repository.
Where It Breaks Down
Persistent cache consumes disk. In a monorepo with multiple packages and modes, the .webpack-cache directory can grow large. Keep it out of version control; add it to .gitignore.
In CI, the cache must be saved and restored between jobs. Conceptually, the cache key should change whenever webpack, loaders, plugins, lockfiles, or config inputs change. If the key is too broad, you restore stale entries. If it is too narrow, you never get a warm build. Provider-specific syntax varies, but the principle is the same: the key is a hash of every input that can affect the build.
The biggest correctness risk is incomplete buildDependencies. If a generated file or environment-dependent config changes but is not listed, webpack may reuse cache entries that assume the old input. That is configuration drift hidden by a warm cache.
Not all loaders and plugins participate safely. Caching helps most when transforms are deterministic: same input, same output. A custom loader that reads the network, the current time, or random values may produce different results on every run and should be excluded from caching or given explicit cache settings. Check the documentation for your specific loader and plugin versions to see how they declare cacheability.
Make Cache Invalidation Part of Your Build Contract
Treat the filesystem cache as a versioned build input, not an afterthought. Measure cold and warm builds rather than trusting generic benchmarks. After upgrading webpack, a loader, or a plugin, clear the cache and verify that emitted assets—especially production hashes—match what you expect. If output changes unexpectedly, suspect stale cache before suspecting your source code.
Finally, run both development and production builds from a clean cache and a warm cache, then compare the emitted assets. If they differ, your cache configuration is not yet safe to trust. The goal is not just a faster terminal; it is a faster terminal that still tells the truth.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.