Bytecode caching
Persisting V8 bytecode across cold pods with NODE_COMPILE_CACHE.
Scale-to-zero means cold starts. The expensive part of a Next.js cold start is V8 compiling JavaScript from source. knext skips that work on every cold pod after the first by persisting the V8 bytecode cache to a volume.
How it works (Node)
knext runs the standalone server with NODE_COMPILE_CACHE pointing at a directory. On the first
start V8 serializes compiled bytecode to that directory; every later cold pod reads it and skips JS
parsing/compilation.
The runtime image starts the server like this:
# NODE_COMPILE_CACHE: V8 bytecode cache for faster subsequent cold starts.
CMD ["sh", "-c", "export NODE_COMPILE_CACHE=\"${NODE_COMPILE_CACHE:-.next/compile-cache}\"; exec node server.js"]The Dockerfile supplies a fallback path; the operator's externally-injected value wins. When
spec.cache.enableBytecodeCache: true, the operator injects:
NODE_COMPILE_CACHE=/cache/bytecode/latestbacked by a PVC, so the cache survives pod death and is shared across cold starts.
Persistence is the whole point. A pod-local cache directory would be re-primed on every cold
start and save nothing. The cache must live on a PVC (enableBytecodeCache) to pay off across pods.
Node vs Bun
| Runtime | Bytecode mechanism |
|---|---|
| Node (default) | NODE_COMPILE_CACHE — the start script and runtime image set it; the operator mounts a PVC. This is the supported, shipping path. |
| Bun | knext can run the same standalone server.js under Bun (spec.runtime: bun), but there is no Bun --compile / Bun-bytecode path wired in knext. Bun runs the server; it does not get a knext-managed bytecode cache. |
The project's runtime decision keeps Node + NODE_COMPILE_CACHE as the default: a cold-start
benchmark found Bun and Node comparable (scheduling-bound), so there's no Bun-binary rewrite.