Connect Django to Neon Serverless Postgres
Use Neon serverless PostgreSQL with Django: pooled versus direct connection strings, cold starts after scale-to-zero, transaction-mode pooling settings, branches for preview environments, and running migrations safely.
Problem statement
Neon is serverless PostgreSQL: compute scales with load and can suspend to zero when idle, storage is separate, and you can create instant copy-on-write branches of a database. For Django, that means cheap databases for side projects and staging, and per-pull-request preview databases with real data shapes. It also brings two behaviours to plan for: the first query after a suspended compute wakes up takes longer, and the pooled endpoint uses PgBouncer in transaction mode, which needs specific Django settings.
- Copy both connection strings from the Neon console: pooled (host contains
-pooler) and direct. - App traffic: pooled endpoint with
CONN_MAX_AGE = 0andDISABLE_SERVER_SIDE_CURSORS = True. - Migrations and admin tasks: direct endpoint.
- Add
sslmode=require(Neon requires TLS). - For production, disable scale-to-zero or set a suspend timeout that avoids cold starts during business hours.
- Create a Neon branch per preview environment and delete it when the pull request closes.
Step-by-step solution
1. Two connection strings
# pooled, for the app
postgresql://app:pw@ep-cool-name-123456-pooler.eu-central-1.aws.neon.tech/appdb?sslmode=require
# direct, for migrations
postgresql://app:pw@ep-cool-name-123456.eu-central-1.aws.neon.tech/appdb?sslmode=require
2. Django settings for the pooled endpoint
DATABASES = {"default": env.db("DATABASE_URL")}
DATABASES["default"].update({
"CONN_MAX_AGE": 0,
"CONN_HEALTH_CHECKS": True,
"DISABLE_SERVER_SIDE_CURSORS": True,
})
The pooler runs PgBouncer in transaction mode, so server-side cursors (used by .iterator()) and other session state do not survive between transactions. For a long-running server with few processes, you can use the direct endpoint with persistent connections instead.
3. Run migrations on the direct endpoint
DATABASE_URL="$DIRECT_DATABASE_URL" python manage.py migrate --noinput
Migrations can take session-level locks and run long transactions, which belong on a direct connection.
4. Plan for cold starts
When scale-to-zero suspends the compute after inactivity, the next connection waits for it to start, typically under a second but enough to notice on a first page view or to trip short connection timeouts. For production, either disable autosuspend on the primary compute or set a longer suspend delay. Set Django's connect timeout generously ("OPTIONS": {"connect_timeout": 10}) so a waking compute does not cause errors.
5. Branches for preview environments
Neon branches are instant copies of a database at a point in time, sharing unchanged storage. In CI, create one per pull request:
neonctl branches create --name pr-$PR_NUMBER --parent main --project-id $NEON_PROJECT
neonctl connection-string pr-$PR_NUMBER --pooled
Run migrations against the branch and point the preview deployment at it. Delete the branch when the pull request closes. If production data contains personal information, branch from an anonymised staging branch instead of production.
Verification check: the app works through the pooled URL including a view that uses .iterator(), migrations succeed on the direct URL, and after a suspend period the first request succeeds (slower) rather than failing.
Explanation
Neon separates PostgreSQL compute from storage. Storage is a distributed, versioned layer that makes branches cheap (copy-on-write) and lets compute nodes start and stop independently. The pooled endpoint puts PgBouncer in front of compute so that serverless functions, which open many short connections, do not exhaust PostgreSQL's connection limit. Django runs unchanged on PostgreSQL semantics; the settings adjustments come from transaction pooling and from compute that may need a moment to wake.
Edge cases / notes
- Extensions. Common extensions (
pg_trgm,pgvector,postgis) are supported; enable them withCREATE EXTENSION. - Region. Place the Neon project in the same region as your app servers for low latency.
- Read replicas are separate computes on the same storage; add them as a second Django database.
- Point-in-time restore uses history retention; set it to match your recovery needs.
Next steps
- Wire branches into previews with Django Preview Environments for Pull Requests.
- Another hosted Postgres option: Connect Django to Supabase Postgres.
FAQ
Does Django work with Neon?
Yes. Neon is PostgreSQL; use Django's PostgreSQL backend with Neon's connection string.
Which Neon connection string should Django use?
The pooled one for app traffic and the direct one for migrations.
What about Neon cold starts?
A suspended compute adds a short delay to the first connection. Disable or lengthen autosuspend for production.
Can I use Neon branches for preview environments?
Yes. Create a branch per pull request, migrate it, and delete it when the PR closes.