How Bolt.new apps in production break
See where Bolt.new apps in production fail first, how to harden security and operations, and when a focused migration is worth the cost.

Table of Contents
A Bolt.new app can be a perfectly sensible production app. The mistake is treating a successful preview as evidence that the system is ready for customers. Preview proves that the happy path renders. Production asks whether the wrong user can read a row, whether a failed payment can be replayed, whether an email reaches a real inbox, and whether someone can restore service when a generated change breaks the database.
I have seen teams spend a week rewriting a generated front end while leaving the exposed authorization rule that actually put the business at risk. The origin of the code is rarely the deciding factor. Operational ownership is. Keep the Bolt stack when you can understand it, test it, observe it, and change it without depending on chat history. Migrate only the component whose limits or failure modes you can name.
A working preview proves less than you think
The first thing that breaks is usually an assumption hidden by the preview environment, not React or the generated CSS. One developer, one browser, a nearly empty database, warm authentication state, and manually entered test data make a forgiving test bench. Customers bring expired sessions, duplicate clicks, slow mobile connections, password managers, corporate email filters, unexpected Unicode, and two tabs editing the same record.
Separate build-time success from runtime readiness. Bolt can publish a site to Bolt Hosting, and its publishing flow performs an automatic security review. That is useful, but a clean deploy and a clean automated review do not prove that your business rules are correct. A scanner cannot infer that an account manager may view every order in one tenant but none in another. It cannot know whether charging twice after a timeout is worse than losing the order.
Run a production rehearsal with a fresh browser profile and accounts for at least two organizations. Create data as user A, then try every read, update, export, and delete path as user B. Repeat with an expired session, a disabled account, and a request submitted twice. Do the test against a staging deployment that uses a separate database, not against the editor preview and not against production.
The distinction teams blur is functional testing versus boundary testing. Functional testing asks whether an authorized person can complete a task. Boundary testing asks whether an unauthorized person, a stale client, or a repeated request can cross a line. Generated apps often pass the first class quickly because that is what the prompt described. The second class needs explicit policies and adversarial tests.
Treat the preview as a design review. Treat staging as an operational rehearsal. If your team cannot state which environment it is looking at, stop adding features until the distinction is visible in domains, credentials, and database projects.
The editor runtime and the deployed runtime also solve different problems. Bolt builds in a browser-based environment backed by StackBlitz technology, while the published application runs on its hosting stack. StackBlitz's WebContainers browser-support manual describes cross-origin isolation, service workers, and browser-specific constraints for the development environment. Those details can explain why a preview behaves differently in Safari or a privacy-restricted browser, but they do not describe the capacity of your deployed server functions. Diagnose the environment that actually failed instead of treating "Bolt" as one black box.
Add a release probe that checks more than the home page. It should create a temporary account or use a controlled test account, read one protected record, call one server function, and verify the expected database write. Run it immediately after deployment and on a schedule. A green build followed by a failing release probe tells you that compilation succeeded while configuration, credentials, redirects, or a dependent service did not.
Authorization fails before the interface does
Database authorization is the most dangerous early failure because the interface can look correct while the data boundary is wrong. Hiding an Edit button does not prevent a caller from sending the underlying request. A client-side role check improves the interface, but only a server function or database policy can enforce permission.
Bolt Database and Supabase both support authentication, server functions, secrets, and row-level controls. Their existence does not make the policy correct. Bolt's Security Audit can identify issues such as missing RLS policies and insecure permissions. Supabase's Production Checklist says to enable row level security on exposed tables and create reasonable policies. I would go further: an enabled policy that uses the wrong ownership column is worse than an obvious missing policy because it creates false confidence.
Suppose every project row has an owner_id, but your product later adds organizations. A generated policy may still compare owner_id with the signed-in user. Teammates then cannot access shared projects, so someone loosens the rule to all authenticated users. The app starts working and silently turns every customer into a member of one global tenant. That repair is common because it fixes the visible symptom.
Write the authorization matrix before asking an agent to alter a policy:
- Public profile: anonymous, members, and tenant admins may read; the service job may read and write.
- Tenant project: members and admins may access their own tenant; the scoped service job may read and write.
- Billing record: members and admins may read their own scope; only the event job writes.
- Audit event: members and admins may read their own scope; the service job only appends.
Then test the database through the same public client the browser uses. A compact policy check can live in CI. The exact command depends on your API, but the output must make subject, action, and result obvious:
node scripts/check-access.mjs
PASS anonymous cannot read tenant_projects
PASS member_a reads tenant_a project
PASS member_a cannot read tenant_b project
PASS tenant_admin cannot update billing_events
PASS webhook_job can append one billing_event
5 passed, 0 failed
Never expose a service-role credential through a VITE_ variable. Build tools intentionally place those variables in browser code. Bolt's database introduction lists environment-variable examples, but the names do not override the browser bundler's behavior. An anonymous public client credential may belong in the browser when RLS protects every path. An administrative credential belongs only in a server function secret store.
Also test object storage, exports, and server functions. Teams secure tables and forget that a predictable file path, an unrestricted export function, or a callable admin function can bypass the intended boundary. Authorization is one system even when the platform presents it across several screens.
Database changes need a history outside the chat
The second fault line is schema change control. A prompt that says "add team invitations" can touch tables, policies, functions, indexes, seed data, and generated types. If the only record of that change is the current database state and a conversation, you cannot reliably reproduce staging, review the SQL, or restore the prior application version.
Use version-controlled, forward-only migrations as soon as the app stores data you care about. Supabase's deployment maturity model is blunt: once an application is live, change the database through migrations rather than the dashboard, and keep local, staging, and production environments separate. That advice applies whether Bolt produced the first SQL or an engineer wrote it by hand.
A migration should encode both the structural change and the failure it prevents. For a new non-null column, do not add the constraint before existing rows have a value:
alter table projects add column tenant_id uuid;
update projects p
set tenant_id = m.tenant_id
from memberships m
where m.user_id = p.owner_id;
alter table projects alter column tenant_id set not null;
create index projects_tenant_id_idx on projects (tenant_id);
That sequence still needs an explicit decision for owners without a membership. If the update leaves rows null, the constraint correctly stops the migration. Silencing that failure with a made-up default tenant corrupts ownership. Production migration work is mostly the handling of old, awkward data, not the syntax of alter table.
Make every production schema change answer four questions: Can the old application run while this migration lands? Can the new application run before it lands? How will you verify row counts and constraints? What is the recovery action if the deploy stops halfway? Rollback often means a new compensating migration, not reversing SQL that may destroy new data.
Backups do not replace this discipline. A backup can recover state after damage. It cannot explain which code expects which schema, and restoring the entire database to fix one bad column may discard valid customer writes made after the backup.
Authentication breaks in inboxes and old sessions
Authentication usually fails outside the developer's own inbox. Sign-up, confirmation, password reset, invitation, provider login, logout, and session refresh are separate flows. Testing one successful login leaves most of the surface untouched.
Default email delivery is suitable for early development, not an unexamined launch. Supabase's Production Checklist recommends custom SMTP so you control deliverability and sender identity. It also warns that corporate email scanners can consume a single-use link before the person clicks it, and that link tracking can rewrite confirmation URLs. Those are concrete failures: the user sees an expired link even though they acted immediately.
Test on more than one email provider and include a corporate mailbox if businesses are your buyers. Verify the sender domain, redirect allowlist, expiry behavior, and the page shown after success or failure. Request two reset emails and click the older one. Open a confirmation link on another device. Remove a user from an organization while they have the app open, then confirm the next privileged request fails on the server.
OAuth adds configuration drift. A callback URL that works on the preview domain may not include the production domain. A permissive wildcard may hide the mistake until you tighten it. Keep an explicit list for local, staging, and production redirects, and remove temporary preview addresses after testing.
Session invalidation deserves its own acceptance test. Disabling an account or changing a role in the database means little if a long-lived token continues to authorize an old claim. Decide whether the server checks current membership on sensitive actions or accepts the token until expiry. Either choice has a cost, but leaving it accidental is not acceptable.
Real traffic exposes concurrency and runtime limits
The hosting stack should stay until a measured workload exceeds it, but you need to measure the whole request path. Front-end assets are rarely the first capacity problem. Slow database queries, unbounded list reads, synchronous third-party calls, image uploads, and server functions that retry badly fail earlier.
Serverless and edge runtimes have hard limits. Netlify documents bundle, memory, CPU, and response timing limits for its edge functions. Bolt Hosting has its own operational model. Do not carry a limit from one provider into a design review for another. Record the current limits for the actual deployment in a short runbook, then design each job to fit or move that job to a queue-backed worker.
A payment or webhook handler must be idempotent. Providers retry after timeouts, and users double-click. Store the provider's event identifier under a unique constraint, then return success for a duplicate after confirming the prior result. Never perform the irreversible action first and write the event record second.
Pagination is another early tell. A generated screen that fetches every row feels instant with twelve records and becomes costly with twelve thousand. Put a deterministic order and a limit in the query. Prefer cursor pagination for a changing feed; offset pagination can skip or duplicate rows when inserts happen between requests.
For Supabase-backed serverless functions, use the connection mode intended for temporary clients. Supabase's connection guide directs serverless and edge traffic to its transaction pooler, while direct connections fit migrations and long-lived backends. Opening direct database connections per invocation can exhaust the database even when CPU and request volume look modest.
Load test behavior, not a vanity request count. Model login bursts, a dashboard that performs several queries, a webhook retry, and two writers updating the same object. Capture p50, p95, error rate, database connections, and slow queries. A test that only downloads the home page tells you whether a CDN can serve static files, which you probably knew already.
Public access turns convenience into a bill
Cost and abuse controls become production concerns the moment an anonymous person can trigger paid work. A friendly interface does not limit a caller to the interface. Someone can script sign-ups, password resets, file uploads, AI requests, search queries, or webhook endpoints and consume quotas much faster than normal users.
Put limits at the narrowest enforceable boundary. A disabled button is not a limit. Enforce request size before parsing a file, user and tenant quotas before starting paid work, and rate limits before calling a third party. For expensive operations, reserve quota atomically so two concurrent requests cannot both pass the same remaining-balance check.
Authentication endpoints need abuse protection too. Supabase documents configurable rate limits for sign-up, OTP, verification, token refresh, and MFA operations, plus CAPTCHA support for sign-up, sign-in, and password reset. Do not copy documented default numbers into your design forever because providers change limits and plans differ. Record the configured values from your own project and test the user message returned when a limit is reached.
Uploads require four decisions before launch: maximum request size, accepted content determined from the bytes rather than the filename, per-tenant storage quota, and deletion behavior. Store uploads under unguessable object names and authorize download at the storage layer. If the product accepts documents for processing, isolate that work from the request and treat parsers as untrusted inputs. A file named invoice.pdf may not be a PDF.
Third-party spend needs a circuit breaker. Set provider budgets and alerts where available, then add an application-side daily ceiling for nonessential work. Decide what degrades when the ceiling trips. Search can return a limited result, an AI feature can pause with a clear explanation, and a marketing email can wait. Login, data export, and account deletion should not depend on the budget for an optional feature.
Track unit economics with application events: tenant, operation, provider, input units, output units, storage bytes, or processing duration. Avoid placing customer content in that record. The purpose is to find a loop, an abusive account, or a feature whose cost exceeds its price before the cloud invoice supplies the first warning.
A useful abuse rehearsal uses a script that sends duplicate requests, oversized payloads, expired credentials, and requests just above the intended quota. Confirm that the rejection happens before the expensive side effect and that retrying does not create more work. Then verify support can distinguish a customer who hit a legitimate limit from an attacker without reading private data.
You cannot operate an error you cannot reconstruct
Observability breaks first in the human sense: the app fails and nobody can explain which user action, deploy, function, or database query caused it. Browser console output and a screenshot from a customer do not form an incident record.
At minimum, collect client errors, server function errors, deployment identifiers, and structured application events. Give every incoming request a correlation ID and pass it into database and third-party logs where possible. Log actor ID, tenant ID, action, result, duration, and a safe object identifier. Do not log access tokens, authorization headers, reset links, raw payment data, or whole request bodies.
Use a small event shape consistently:
{
"level": "error",
"event": "project.create.failed",
"request_id": "req_01J...",
"actor_id": "usr_...",
"tenant_id": "ten_...",
"release": "git_sha",
"duration_ms": 842,
"error_code": "db_timeout"
}
This record answers who, where, what, and which release without copying private content. It also lets you search for all failures in one tenant or after one deploy. Generated prose in an error message is not a stable error code; define codes that alerts and support procedures can use.
Create three alerts before launch: elevated failed requests, a user-facing critical path failing end to end, and database saturation or storage pressure. Tune thresholds after observing normal traffic. Alerts on every exception train people to ignore the channel, while no alert turns customers into monitors.
Finally, rehearse one restore and one rollback. Confirm who has access when the primary developer is asleep, where secrets live, how DNS is controlled, and how to identify the running release. If the answer depends on remembering the right Bolt conversation, you do not yet own the operation.
Source control is the point where the team takes ownership
Connect the project to GitHub before more than one person or one production environment depends on it. Bolt's GitHub documentation says the integration keeps a full history, supports branches, and lets you deploy elsewhere. It also notes that Bolt does not merge branches inside the app, so the team still needs a pull request and review habit in GitHub.
Do not confuse Bolt Version History with team source control. Version History is excellent for restoring an earlier generated state. Git gives you reviewable diffs, branches, tagged releases, automated tests, ownership rules, and a durable path out of the editor. You need both conveniences for different jobs.
Set a modest merge gate:
- A branch contains one coherent change and any schema migration.
- CI installs from the lockfile, builds, runs tests, and scans committed files for secrets.
- Another person reviews authorization, destructive data operations, and new dependencies.
- Staging deploys the exact commit intended for production.
- Production records that commit as its release identifier.
Review generated changes by risk, not by line count. Spend time on policies, server functions, dependency install scripts, data deletion, payment transitions, and error handling. A large styling diff may deserve a quick visual check. A five-line policy change can expose every tenant.
Keep prompts and agent instructions in the repository when they affect repeated work, but do not pretend they replace the resulting code review. The repository is the executable record. Chat is supporting context. A new engineer should be able to clone, install, test, and deploy from documented commands without reconstructing a conversation.
Keep Bolt when the boundaries are boring
Keep the Bolt stack when the application uses supported web technologies, the team owns the repository, and production concerns fit ordinary managed services. A generated origin does not make a React application temporary. Rewriting it for respectability creates new defects and delays the controls the existing app actually needs.
A good keep decision has specific evidence: builds are reproducible from the lockfile; staging and production use separate credentials and databases; authorization tests cover tenant boundaries; migrations are versioned; errors carry release and request IDs; backups and restore procedures match the business's recovery needs; and one person other than the builder can deploy or roll back.
Keep using Bolt for interface work and bounded features if its changes remain reviewable. Move sensitive workflows into clearly named server functions. Add tests around those seams. You can also retain Bolt as an editor while changing hosting or database services because the GitHub repository preserves that option.
The popular recommendation I argue against is "rewrite once you get traction." Traction is when the team has the least spare attention and the most customer data to endanger. Rewrite only when the current architecture blocks a defined requirement and an incremental extraction costs more than replacement. Fear that generated code looks untidy is not such a requirement.
Migrate the bottleneck instead of the brand name
Migrate when a concrete constraint repeats after reasonable repairs. Examples include a required regional or compliance control the provider cannot supply, a background workload that cannot fit the runtime, database access patterns that need another data model, a mobile requirement outside the chosen web architecture, or team workflows that the integration cannot support. Write the requirement and the failed evidence before choosing a destination.
Do not migrate everything at once. If background video processing exceeds a function limit, move that worker and keep the front end. If database policy complexity has become untestable, redesign the data boundary while leaving hosting alone. If deployment governance is the issue, deploy the same repository through a CI system with approvals. Each extraction should reduce one named risk.
Use this decision record for each proposed move:
Constraint: Monthly export exceeds the current function duration limit.
Evidence: 3 of 5 staging runs timed out with production-sized data.
Repair tried: Pagination and streaming reduced memory but not total duration.
Smallest move: Queue export jobs on a worker and store the result.
Success test: 5 production-sized runs complete and retries create one file.
Rollback: Route new jobs to the original function while queued jobs drain.
This format prevents a vague platform debate from turning into a rewrite. It also exposes cases where the team has not tried a smaller repair or does not know how success will be measured.
Migration has a readiness cost. Before moving, export source, schema, migrations, files, secrets inventory, DNS records, and a data reconciliation query. Lower DNS TTL ahead of a domain cutover when you control it. Run old and new paths in parallel where duplicate side effects can be prevented. After the cutover, compare record counts and business totals, not just HTTP status codes.
If you need an outside review, the oleg.is Team & AI Audit is a five-business-day, $5,000 engagement that guarantees at least $50,000 a year in identified savings or it is free. The useful output in this situation is a ranked engineering plan: which generated parts are safe to keep, which controls are missing, and which component earns the cost of migration.
A production-ready Bolt.new app is not one that escaped generation. It is one whose team can explain every trust boundary, reproduce every environment, see every important failure, and replace one component without gambling the whole product. Keep the speed. Add ownership before the first serious customer forces the lesson during an outage.
Frequently Asked Questions
Is Bolt.new suitable for production apps?
Yes, when the generated application uses supported components and your team adds normal production controls. Suitability depends on authorization, deployment, monitoring, recovery, and ownership, not on whether an agent wrote the first version.
What usually breaks first in a Bolt.new app?
Authorization and environment assumptions tend to fail before the interface. Tenant isolation, exposed secrets, email flows, stale sessions, and duplicate requests deserve testing before visual polish.
Should I rewrite a Bolt.new app before launch?
No. Rewrite only when you can name a requirement the current architecture cannot meet and show why a smaller repair fails. A preventive rewrite trades known gaps for a new set of unknown defects.
How do I secure a Bolt.new app with Supabase?
Enable and test RLS for every exposed table, keep administrative credentials in server-side secrets, and test access with accounts from different tenants. Review storage policies and server functions too, because table policies do not protect every route.
Can I move a Bolt.new project to another host?
Yes, if the project is connected to GitHub and the target host supports its framework and runtime needs. Treat the move as a deployment change: reproduce environment variables, redirects, functions, domains, and monitoring before cutover.
Do I need GitHub if Bolt has version history?
You need GitHub once the project requires team review, branches, CI, release identifiers, or deployment outside the editor. Bolt Version History remains useful for restoring generated states, but it does not replace a team source-control workflow.
How should database migrations work for a generated app?
Store ordered SQL migrations with the code, test them against production-shaped staging data, and deploy them through a reviewed process. Design changes so old and new application versions can overlap when possible.
What monitoring does a Bolt.new production app need?
Collect browser and server errors with request, tenant, and release identifiers, then alert on failed critical paths and resource pressure. Keep secrets and private request bodies out of logs.
When should I migrate away from Bolt Hosting?
Move hosting when a verified runtime, regional, compliance, cost, or deployment-governance requirement cannot be met there. Keep the same repository and migrate hosting alone unless another component has its own proven constraint.
How do I test whether my Bolt.new app is ready to launch?
Use a separate staging environment and test cross-tenant access, expired sessions, duplicate submissions, real email delivery, production-sized data, alerts, rollback, and restore. A clean preview is only the beginning of that test.


