Draft, not yet published
Bolt.new database connection limits and what they mean for your launch
7 July 2026· 5 min read · by Stackbastion
Your Bolt.new app works great while you’re building it. Then you post it somewhere, 30 people show up at once, and suddenly half of them see spinning loaders or a “too many connections” error. You didn’t change anything. So what broke?
Almost always it’s the database, not your code. Specifically, the number of connections your database allows at the same time. This is one of the least visible limits in any AI-built app, and it’s the one that bites hardest exactly when you don’t want it to: on launch day.
Why this happens on Bolt.new
Bolt.new generates a working app and wires it to a database for you, usually a hosted Postgres like Supabase or Neon. That’s convenient. The part nobody explains is that Postgres doesn’t hand out connections for free.
Every Postgres instance has a hard cap called max_connections. On small hosted tiers it’s often set between 20 and 60. That number is not “20 users.” It’s 20 open connections at the database level, and a single web request can hold one connection for its whole duration. Worse, many serverless and framework setups open a fresh connection per request instead of reusing a pool.
Here’s the trap. In the Bolt editor, it’s just you. One or two connections, everything’s fine. The moment real traffic arrives, each concurrent request grabs a connection. Ten users clicking around can easily open 30 to 50 connections between them if there’s no pooling. You hit the cap, Postgres rejects the next connection, and users get errors that look random.
A few things make this worse on AI-generated stacks specifically:
- New client per request. Generated code often creates a database client inside the request handler instead of once at startup. Each call opens a new connection.
- Serverless multiplies it. If you deploy to a serverless platform, every function instance keeps its own connections. Traffic spikes spin up many instances, each holding connections open.
- No pooler in front. The free database tier gives you a direct connection string. There’s usually a separate pooled string that most people never switch to.
How to fix it
The fix is a connection pooler sitting between your app and Postgres. The pooler keeps a small set of real database connections and shares them across many app requests. Your app thinks it has lots of connections; the database only sees a handful.
Step 1: find out what your limit actually is. Connect to your database with psql (the connection string is in your provider’s dashboard) and run:
SHOW max_connections;
Then see how close you get under load. Watch active connections in real time:
SELECT count(*) AS active, state
FROM pg_stat_activity
GROUP BY state;
If active climbs toward max_connections when a few people are using the app, that’s your problem confirmed.
Step 2: switch to the pooled connection string. If you’re on Supabase, they run PgBouncer for you. In the dashboard the direct string uses port 5432; the pooled (“Transaction”) string uses port 6543. Use the pooled one in your app’s environment variable:
# Direct (avoid for a web app under load)
postgresql://user:[email protected]:5432/postgres
# Pooled through PgBouncer (use this)
postgresql://user:[email protected]:6543/postgres?pgbouncer=true
Neon has an equivalent: use the host that includes -pooler in the domain.
Step 3: cap the pool inside your app too. Even with a pooler, your app should reuse one client, not make a new one per request. If your generated code uses pg in Node, create the pool once at module load and set a sane max:
import { Pool } from "pg";
// Create once, reuse everywhere. Do NOT put this inside a request handler.
export const db = new Pool({
connectionString: process.env.DATABASE_URL,
max: 10, // never let one instance hog the whole database
idleTimeoutMillis: 30_000,
});
Step 4: if you use Prisma, point the datasource at the pooled URL and add a connection limit in the string:
DATABASE_URL="postgresql://user:pass@host:6543/postgres?pgbouncer=true&connection_limit=5"
Step 5: retest under load. Simulate 50 concurrent users hitting your busiest endpoint. A simple tool like hey works:
hey -n 500 -c 50 https://yourapp.example.com/api/dashboard
Watch pg_stat_activity while it runs. Connections should plateau at your pool max, not climb to the ceiling. If it holds steady and requests succeed, you’re in good shape.
Or, we do it for you
Connection limits are the kind of thing you find out about at the worst possible moment, and the fix touches your database, your env vars, and your app code all at once. If you want a second pair of eyes on your setup regardless of what you built it with, get a free production audit and we’ll tell you exactly where it’ll break before your users do.
FAQ
How many database connections do I actually need?
Fewer than you’d think. A pooled setup with a max of 10 to 20 real connections can serve hundreds of concurrent users, because most requests hold a connection for only a few milliseconds. The goal isn’t more connections, it’s reusing a small number efficiently.
Does upgrading my database plan fix this?
Partly. A bigger plan raises max_connections, so you buy headroom. But if your app opens a new connection per request with no pooler, you’ll hit any ceiling eventually, just later. Pooling fixes the root cause; a bigger plan only delays the symptom.
Why does it work in the Bolt editor but not in production?
In the editor you’re the only user, so you never open more than one or two connections. Production is many users at once, and without a pooler each concurrent request competes for the same tiny connection budget. The editor simply never exercises the limit.
Is PgBouncer the same as connection pooling in my code?
They solve the same problem at different layers. Your app’s pool reuses connections within one running instance. PgBouncer reuses them across many instances and requests before they ever reach Postgres. For a real launch you want both: an app-side pool with a low max, sitting behind PgBouncer.