A two-hour detour into database connection pooling
CreatorOS runs on Vercel's serverless functions talking to a Neon Postgres database. Every function invocation can open its own database connection, and under real concurrent load — a bunch of people hitting the app at once — that adds up fast. We'd seen a handful of intermittent 503s during a QA pass and suspected exactly this: connection exhaustion, not a code bug.
The fix is well-known: point the app at Neon's pooled connection endpoint instead of the direct one, so many function invocations share a small set of real database connections instead of each opening its own. We made the switch. The next deploy immediately broke.
Chasing the wrong fix
The error was P1001, "can't reach database server," against the pooled endpoint during migrations. This is a known interaction — pooled connections (via PgBouncer) don't always play well with running schema migrations, because migrations sometimes need session-level features a pooled connection doesn't give you. The standard fix is a separate `directUrl` — one URL for the app's normal queries (pooled), a different one just for migrations (direct, unpooled).
So that's what we tried. Except our Prisma version (7.8.0) had quietly removed support for `directUrl` living in the schema file at all — it now wants connection URLs configured in a separate `prisma.config.ts`, and even that config's types didn't expose a `directUrl`-equivalent option. We reverted the change and were back to a broken deploy with no obvious next step.
The fix that had already worked
We added a `DIRECT_URL` environment variable anyway, more as a hedge than a real plan, and kicked off a manual redeploy. It went through clean — seven migrations applied, no error. Turns out the original failure almost certainly wasn't a fundamental "pooled connections can't run migrations" wall at all. Neon's databases spin down when idle and take a moment to wake up; the first deploy probably just hit that cold start. The retry worked because the database was already warm, not because of anything we'd changed.
We kept the pooled connection for the app itself (the actual fix for the actual problem) and left the unused `DIRECT_URL` env var in place, harmless, to delete later. Nothing about this was dramatic, but it's a good reminder that a plausible-sounding root cause and the real root cause aren't always the same thing — and that "redeploy and see" is sometimes a legitimate diagnostic step, not just an act of hope.
Want to see what we’ve actually built?
Try CreatorOS free