Skip to content
Vibe Coders

Bolt.new in Production: The Gap Nobody Warns You About

Bolt.new's preview is a browser WebContainer, not a real server. Here is the export-and-redeploy workflow, what breaks in the switch, and the three production toggles Bolt does not surface.

By WitsCode10 min read

You prompted Bolt, watched it scaffold a full app in ninety seconds, clicked the live preview, and posted a loom. The reactions rolled in. Then you clicked the deploy button, grabbed the production URL, and opened it on your phone. The first page loaded. The second threw a 500. The login worked once and then forgot you. By the time you were in the Vercel dashboard trying to remember which environment variable Bolt had set for you, the dopamine had worn off and you were asking a question Bolt's marketing never quite answered. Why does the demo feel so close to production when the deployed app feels so far from it.

The short version is that Bolt.new is not lying to you. It is showing you a convincing illusion of a server, and that illusion is a genuinely different runtime from the one your users will hit. This piece walks through what Bolt is actually doing in the preview, what happens during the export workflow, and the three production toggles Bolt does not surface that cause most failures after deploy.

What Bolt is actually doing in the preview

Bolt.new is built on top of StackBlitz WebContainers. A WebContainer is Node.js compiled to WebAssembly and executed inside your browser tab. When you watch Bolt run npm install, spin up a dev server, and hand you a preview URL, none of that is happening on a remote server. It is happening in the same browser process that is rendering the Bolt UI. The preview URL is a service worker that intercepts your requests and proxies them to the in-tab Node process.

The preview is not a server. It is a browser pretending to be a server. It looks like Node, it mostly behaves like Node, and then in three specific places it behaves like something else.

First, it runs inside a sandbox with a synthetic filesystem. No native binaries. Anything that compiles C or C++ at install time, which includes popular packages like sharp for image processing, bcrypt for password hashing, better-sqlite3 for embedded databases, canvas for server-side rendering, puppeteer and playwright for browser automation, will either fail to install or fall back to a shim that silently does nothing useful.

Second, its network stack is the browser's network stack. When your server code calls fetch, that fetch goes through the browser. Cookies, CORS, TLS, and origin rules follow browser semantics. In production, server-to-server fetches do not care about CORS because CORS is a browser protection. Your code can work perfectly in preview and then break on a real server, or work on a real server and fail in preview, purely because the network layer is pretending to be something it is not.

Third, its lifetime is your tab. Every in-memory variable, every interval timer, every module-level singleton, every file written to the virtual filesystem, disappears when you refresh. This hides a trap. Most Bolt-generated apps use in-memory state as a placeholder: a users array in a module, a Map keyed by session id, a JSON file on disk. In preview you rarely reload, so these feel like they persist. In production, your serverless function cold-starts between requests and wipes everything.

The preview is not production. It is a demo environment with a server-shaped opening. Bolt does not go out of its way to hide this, but it also does not slap a warning on the share button.

The export workflow and where it quietly loses fidelity

Bolt gives you three exit paths. You can download a ZIP of the project. You can click its GitHub integration and have Bolt create a repo under your account and push the code. Or you can click its Netlify integration and have Bolt deploy directly to a Netlify site. The first two hand you a repo and leave you with a real developer's workflow. The third hides the details and works fine for static frontends and breaks for anything interesting.

However you export, here is what comes with you and what does not.

What comes with you is the source code. Components, routes, API handlers, styles, package.json, and usually a lockfile. What does not come with you is the environment. Variables you set in Bolt's secrets UI are not written into the exported repo, which is correct, but it means your production deploy boots with an empty process.env. Any value Bolt was quietly substituting during the preview is now undefined: the database connection string, the API keys, the OAuth client secrets, the webhook signing keys, all of them.

The runtime assumptions also do not come with you. WebContainer runs one specific Node version. Vercel defaults to whatever it defaults to this quarter. Netlify defaults to something else. Render and Fly are based on whatever container image they picked. Your package.json usually does not pin a Node version because Bolt does not write one, so the app gets built and run against a different Node than the one that ran the preview. Most of the time this is fine. The times it is not fine are the ones that cost you three hours.

The install strategy does not come with you either. WebContainer reinstalls packages every time it boots, so the idea of a cache is moot. On a real platform, cache is the difference between a thirty second deploy and a three minute deploy, and the cache is keyed off a lockfile Bolt may or may not have committed in the shape your platform expects.

Finally, the browser-ness of the preview does not come with you. Anywhere your code implicitly relied on the preview running in the same tab as the UI, on the host matching, on fetch behaving like a browser, you now have a semantic mismatch waiting to bite. This rarely shows up at build time. It shows up the first time a real user loads the app from a real IP on a real device.

WebContainer versus a real server, the concrete list

It helps to enumerate the specific failure modes, because they repeat.

Native modules go first. If your package.json mentions sharp, bcrypt, better-sqlite3, canvas, puppeteer, playwright, or anything with node-gyp in its install script, confirm the target platform can build it. Vercel's serverless functions have size limits and a restricted build environment. Netlify Functions are similar. Edge runtimes cannot run any of these. The fix is usually a pure-JS alternative (bcryptjs instead of bcrypt) or moving that code to a long-running container on Render, Fly, or Railway.

State persistence goes second. The users array in memory. The rate limiter keyed to a module-level Map. The session store that is a plain object. All of these need to move to a real store before your first real user arrives. The cheap drop-ins are Upstash Redis for key-value state, Neon or Supabase or Turso for relational data, and a hosted queue for jobs. This is not optional. The cold-start problem alone will make your app look broken.

Process model goes third. In production, your app is not one Node process. It is many. Each Vercel serverless invocation is its own isolate. Each Fly machine is its own container. If two requests can land on two different instances, and one of them set a variable the other expects to read, you have a bug. The fix is to externalise any state the requests share.

CORS and origin handling go fourth. If your frontend and your API are on different domains in production, which they often are when you deploy a monorepo to one platform and a backend service to another, you now have a CORS policy to configure. In preview you did not, because WebContainer's proxy made it all one origin. Add the headers, test from a different domain, do not just trust curl.

Localhost references go fifth. Search the exported repo for localhost, 127.0.0.1, and :3000. Bolt's preview URL is sometimes a synthetic localhost and sometimes a WebContainer subdomain, and generated code can end up with either hardcoded in config, API base URLs, or OAuth callback URLs. Replace with environment-driven values.

Toggle one, Node version pinning

Bolt does not write a Node version into your project. Not in package.json, not in a .nvmrc, not in a platform-specific config file. When you import the repo into Vercel or Netlify or Render, the platform picks a Node version based on its own default, which changes over time and is not the version Bolt ran in preview.

For most apps this is invisible. For any app that touches native fetch, ESM resolution edge cases, the crypto webcrypto surface, or any of the recently-added Node APIs, the mismatch is real. Code that ran on Node 20 in preview may crash on Node 22 because a dep was never updated for it, or vice versa.

Fix it deliberately. Add an engines field to package.json specifying the Node major version. Commit a .nvmrc with the same version so local dev and CI agree. On Vercel, set the Node version in Project Settings or use the runtime config. On Netlify, set NODE_VERSION in the environment or in netlify.toml. On Render, pin the runtime in the service config. The goal is that your preview Node, your local Node, your CI Node, and your production Node are all the same major version, and you know which one.

Toggle two, build cache and install strategy

Bolt's preview reinstalls node_modules from scratch every session because the WebContainer is ephemeral. That has no equivalent on a real platform, where cold installs blow your deploy budget and sometimes resolve to different transitive versions than the preview.

Three things to get right. First, commit the lockfile Bolt used. If Bolt used npm, commit package-lock.json. If it used pnpm, commit pnpm-lock.yaml. If it used bun, commit bun.lockb. Without a lockfile, npm ci refuses to run and the platform falls back to npm install, which means your first deploy and your second deploy can resolve different versions of the same dependency.

Second, add a packageManager field to package.json that matches the lockfile Bolt produced, so the platform's auto-detection picks the right tool. Vercel reads it. Netlify respects it when set correctly. This alone removes a whole category of "works in preview, fails in deploy" surprises.

Third, verify that the cache actually hits on the second deploy. Look at the build log. If it says it installed packages from scratch both times, your lockfile is missing or the cache key is wrong. A healthy second deploy should skip most of the install step and take a fraction of the time.

Toggle three, edge runtime versus Node runtime

This is the trap that catches the most people shipping Next.js apps out of Bolt.

Next.js route handlers and middleware can run on two runtimes. The Node runtime is the familiar one, full Node stdlib, decent cold starts, deployed as serverless functions. The Edge runtime is V8 isolates, no Node APIs, extremely fast cold starts, deployed globally. The choice is controlled by a tiny export const runtime = 'edge' at the top of a file.

Bolt's preview is always Node, because WebContainer is Node. A route handler that imports fs, uses crypto.createHash, or pulls in a Node-only dep works fine in preview. If Bolt or a template set that handler's runtime to edge, the build will succeed and the request will throw at runtime. The error message is often cryptic because the Edge runtime does not give you a stack in the expected Node format.

The inverse happens too. A route that could run on Edge, a simple redirect handler or a geolocation-aware response, gets left on the Node runtime by default and pays for the privilege in cold-start latency.

Audit every route handler and middleware file for a runtime export. If you see edge, open the file and confirm that nothing in it or its imports touches a Node-only API. If you cannot guarantee that, change it to nodejs or delete the export. If you are on Vercel and want to know exactly which routes are which, the build output summary lists them.

This toggle does not exist on every platform. It is mostly a Next-on-Vercel concern. But if you are deploying a Next app and you have not looked at it, look at it.

A production checklist before your first real user

Before you send the link to anyone who matters, walk the repo Bolt exported with this list in hand. Confirm the package manager and commit the lockfile. Pin the Node version in three places: package.json engines, .nvmrc, and the platform config. Audit every dependency for native builds and either replace them or move to a runtime that supports them. Find every in-memory store and replace it with a hosted one. Search for hardcoded URLs. Migrate every environment variable from Bolt's secrets UI into the platform dashboard and set the same values for preview and production scopes. Confirm CORS between your frontend and your API. Read every route handler's runtime declaration. Trigger a second deploy and verify the build cache hit.

None of these tasks are hard. All of them are the reason your Bolt app is not ready for users yet, and none of them are handled by the deploy button.

Where WitsCode takes over

Bolt is an excellent way to get to eighty percent. It is faster than any other tool for turning a prompt into something you can click. What it is not is a production engineering team. The last twenty percent, the part where a browser illusion becomes a real server under real load, is not a feature gap in Bolt. It is a category of work a tool cannot do for you because it requires reading your specific code, your specific platform choice, and your specific risk tolerance.

That is the work WitsCode does. You bring us the Bolt export. We audit the repo for WebContainer-isms, pin the runtime, commit the lockfile, migrate the secrets, swap the in-memory stores for real ones, pick the right platform and runtime per route, and set up a CI pipeline so the next time you re-export from Bolt, the production pass is not a full redo. The vibe coder keeps shipping vibes. The last mile becomes someone else's problem.

→ WitsCode Bolt deployment rescue. If your Bolt app ships fine in preview and breaks in production, that is the gap we close.

Get weekly field notes.

Practical writing on shipping products, straight to your inbox. No spam.

Need help with this?

MVP Development

We design and build web apps, MVPs, and SaaS products. Talk to us about what you are working on.

Talk to us

Want to discuss vibe coders for your business?

Start a project and we'll talk through where you are, what's working, and the highest-leverage moves for the next 90 days.