Everything above ends with V8 86,723 compiling source text, and on a cold start that cost is paid for every file in the graph -- in a MERN backend, a large one. The module compile cache writes V8's code cache to disk and reuses it next run. It has been stable since Node 22.8.0 and costs one line.
import { enableCompileCache } from 'node:module';
enableCompileCache('./.cache');
const t = performance.now();
const mod = await import('./big.js');
console.log(Object.keys(mod).length, 'exports in', (performance.now() - t).toFixed(1), 'ms');cold: 4000 exports in 21.5 ms warm: 4000 exports in 7.9 ms
big.js is a generated file with 4,000 exported functions. Compiling it costs 21.5 ms the first time; on the second run V8 deserializes the cached bytecode instead and the import costs 7.9 ms. The saving scales with the amount of JavaScript parsed. Pass no argument and Node picks a directory under the system temp directory; querying it before the cache is enabled returns undefined.
| API or variable | Purpose |
|---|---|
| enableCompileCache([dir]) | Turn the cache on; returns { status, directory } |
| getCompileCacheDir() | The directory in use, or undefined |
| flushCompileCache() | Write pending cache data before exit |
| constants.compileCacheStatus | FAILED, ENABLED, ALREADY_ENABLED, DISABLED |
| NODE_COMPILE_CACHE=dir | Enable it without touching the code |
The environment variable is the practical form for containers: warm the cache during the image build, then set NODE_COMPILE_CACHE_READONLY=1 at runtime so a read-only filesystem does not turn every start into a silent write failure. NODE_COMPILE_CACHE_PORTABLE=1 makes entries independent of the absolute install path, for when the build and run directories differ. Entries are keyed by Node version, architecture and file content and kept in a versioned subdirectory, so an upgrade or a source edit invalidates them instead of serving stale bytecode. If the directory is unwritable, status comes back FAILED and Node compiles normally.