Optimizing TanStack Start for Cloud Run
TanStack Start runs on Nitro, and Nitro's default Node output can split server code across many files. On Google Cloud Run, that file fan-out can add seconds to cold starts. Bundling the server and pre-warming Node's compile cache reduced first render time on this site.
Ben Houston • June 20, 2026 • 4 min read
I have been using TanStack Start for this website, and I continue to like the stack. I wrote earlier about why TanStack Start appealed to me, especially its explicit routing model, type safety, and clean server function story.
I also moved this site to Google Cloud Run because I like the deployment model: container in, managed HTTPS, autoscaling out, and scale-to-zero when traffic is quiet.
Those two choices fit together well. TanStack Start's server runtime is Nitro, and Nitro's default Node server output can make Cloud Run cold starts much slower than they need to be.
File Fan-Out#
Cloud Run containers run in a sandboxed environment. Filesystem access still works from the application's point of view, but it is not the same as reading from a local NVMe drive on your workstation. Each open, read, and stat during startup costs more.
Node.js server startup means synchronously loading a large dependency graph. If the server is split across many generated chunks and traced node_modules files, startup becomes a long sequence of small filesystem reads.
On a local machine, that file fan-out is often invisible. On Cloud Run, it can dominate the cold start path.
Nitro's Default Output#
Nitro is TanStack Start's server runtime layer, and its default Node server preset produces a portable .output directory. That portability helps, but the default output may not bundle the server into one file.
In practice, the generated server entry can be a small stub that imports additional server chunks and traced dependencies from .output/server/node_modules. That default preserves compatibility, but Cloud Run makes those small file accesses expensive.
On Cloud Run, reduce the number of server-side files Node has to touch during startup.
Bundle the Server#
For a TanStack Start app using Nitro, the key option is:
nitroV2Plugin({ preset: 'node-server', inlineDynamicImports: true, });
In this project, I also inline some pure JavaScript dependencies through Nitro's externals.inline option. I do not recommend bundling everything. Test native packages such as sharp, and instrumentation-heavy packages such as Sentry/OpenTelemetry, in the final container.
Node starts faster when the startup path touches fewer files.
Then Pre-Warm Node's Compile Cache#
Once you bundle the server output, Node's compile cache is worth using. I wrote about the small CLI I built for this in node-prewarm: CLI for Node 25's Compile Cache.
The basic Docker pattern is:
ENV NODE_COMPILE_CACHE=/app/.node_compile_cache RUN npx node-prewarm "node .output/server/index.mjs" --port 8080
node-prewarm starts the built server during the Docker build, waits until it is listening, then shuts it down cleanly. That gives Node a chance to populate the compile cache before the deployed container receives its first real request.
Pre-warming helps most after bundling. If your server still touches thousands of files during startup, the compile cache adds more filesystem reads and makes things worse in a sandboxed environment. Bundle first, prewarm second.
The Results#
On this blog system, Cloud Run first render time changed by almost an order of magnitude:
- Unbundled server files: first render was
12.4s. - Bundled server output: first render dropped to
1.64s. - Bundled server output plus Docker build
node-prewarm: first render dropped again to1.4s.
Bundling the server files made the main difference. Pre-warming the compile cache provided a smaller improvement after bundling fixed the file fan-out problem.
If you only add compile cache to an unbundled app, you optimize the CPU side while making the Cloud Run filesystem side worse.
Practical Guidance#
For TanStack Start on Cloud Run, my current checklist is:
- Use Nitro's
inlineDynamicImports: truefor the Node server build. - Inline pure JavaScript dependencies when it reduces runtime file fan-out.
- Keep native and instrumentation-sensitive packages external unless tested.
- Add
node-prewarmduring the Docker build after the production server is built. - Measure first render time on Cloud Run, not just local startup time.
Local benchmarks can mislead here. On my machine, many small files are cheap to read. On Cloud Run, reducing file count took first render from 12.4s to 1.64s.
TanStack Start works well for the kind of React applications I like to build. If you are deploying to Cloud Run, bundle the Nitro server output before judging the framework's cold start behavior.