Neon
Serverless Postgres — Connect widget, pooled vs direct, scale-to-zero cold start, neon_superuser for pgvector.
Neon separates storage from compute: branches are cheap copy-on-write metadata, and idle
compute scales to zero. Wire protocol is plain Postgres — drivers.store.sql stays
postgres.
The one rule
Copy the connection string from Neon's Connect widget. Prefer the pooled hostname for app
traffic and the direct hostname for migrations — never invent the -pooler suffix by hand.
Find credentials (current console)
- Open console.neon.tech → your project.
- On the Project Dashboard, click Connect.
- In Connect to your database, pick Branch, Compute, Database, and Role.
- Toggle Connection pooling on for the pooled string (hostname gains
-pooler), or off for the direct string. - Copy the connection string into
DATABASE_URL(add?sslmode=requireif missing).
# Runtime (pooled) — ep-….…-pooler.…neon.tech
DATABASE_URL=postgresql://neondb_owner:…@ep-xxxx-pooler.region.aws.neon.tech/neondb?sslmode=require
# Migrations / pg_dump / LISTEN — same host without -pooler
# OKE_STORE_SQL_URL=postgresql://neondb_owner:…@ep-xxxx.region.aws.neon.tech/neondb?sslmode=requireNo images["store.sql"] pin — Neon runs the server.
Pooled vs direct
| Use | Which string | Why |
|---|---|---|
| App / serverless / Drizzle runtime | Pooled (-pooler) | PgBouncer transaction mode; up to 10k client connections |
oke db migrate / pg_dump / logical replication / LISTEN | Direct (no -pooler) | Session features break under transaction pooling |
Do not also put PgDog in front of Neon — one pooler only.
Consequence: seeding or migrations through the pooled URL often fail with prepared-statement errors; switch those jobs to the direct URL.
pgvector and roles
CREATE EXTENSION vector; works on every plan — no add-on. Roles created in the Neon
Console / CLI / API get neon_superuser membership and can install supported extensions.
Roles you create only with raw SQL (CREATE ROLE) do not get neon_superuser — they
hit permission errors on extension install. Create app roles in the Console (or grant
deliberately) when you need pgvector setup from that role.
Free tier / production limits
| Limit | Free plan |
|---|---|
| Scale to zero | After 5 min idle — cannot disable |
| Compute | 100 CU-hours / project / month |
| Storage | 0.5 GB / project |
| Autoscaling | Up to 2 CU |
Hitting CU-hours or storage suspends compute until the next billing period or an upgrade. Always-on production needs Launch/Scale with scale-to-zero disabled.
Query performance
Create the extension — do not set command
Neon already preloads pg_stat_statements. CREATE EXTENSION is enough. There is no oke
command overlay and no online_advisor shim.
| Step | What you do |
|---|---|
| Preload | Neon compute (already on) |
| Create | CREATE EXTENSION IF NOT EXISTS pg_stat_statements |
| Console | Store → SQL band → Performance |
Scale-to-zero wipes statement stats when compute sleeps. Index Advisor is not in the default Neon catalog — Performance shows a CTA, not a failing Enable.
Real gotcha — cold start
After scale-to-zero, the first query pays a wake-up (hundreds of ms to a few seconds). Health checks that expect sub-100ms on an idle Free project will flap. Paid plans can disable scale-to-zero; Free cannot.
Troubleshooting
Too many direct connections, or pooler server pool exhausted for your compute size.
Move runtime traffic to the -pooler hostname; keep a small number of direct admin
sessions. Neon reserves connections for the superuser account on each compute size.
Free (and any scale-to-zero) compute drops pg_stat_statements memory when the
endpoint sleeps. Re-run traffic after wake — this is not a Console bug.
The role is not a Console-created / neon_superuser member. Re-run as neondb_owner (or
another Console role), or grant appropriately — raw SQL roles start with public-schema
privileges only.
Learn more
- Postgres (image) — self-hosted peer
- Store · Index —
pgvectorusage - PgDog — when you self-host pooling instead