Serverless Postgres skill for Codex

Pooled at runtime, direct for migrations, branched per PR. Codex wires serverless apps to Neon or Supabase with two connection strings: a transaction-mode pooled URL for runtime and a direct URL for migrations. It picks a driver that fits the runtime, turns off prepared statements where the pooler cannot handle them, sizes timeouts for scale-to-zero wake-ups, and sets up a database branch per pull request without copying unmasked production data into previews.

  • Databases
  • $serverless-postgres
  • 156 lines
  • 2 files
  • ~1,950 tokens
  • MIT
  • Updated

Install

curl -fsSL codexskills.dev/install.sh \  | sh -s -- serverless-postgres

Installs to ~/.agents/skills/serverless-postgres/ for every project on this machine.

104-line POSIX script. No sudo, no telemetry. Read install.sh

Use it in Codex

Start a prompt with $serverless-postgres, or pick it from /skills. You can also just describe the task: Codex reads the description below and loads the playbook when your request matches.

What Codex reads before loading it

Runs applications on serverless and managed Postgres such as Neon and Supabase - pooled versus direct connection strings, transaction-mode pooler limits on prepared statements and session state, driver choice for Lambda, Vercel and edge runtimes including the Neon serverless HTTP and WebSocket driver, scale-to-zero cold starts and timeouts, database branching for preview environments and CI, and safe migrations through the direct URL. Use when connecting an app on Vercel, Lambda, Cloudflare Workers or similar to Neon or Supabase Postgres, seeing too many connections or prepared statement errors, slow first requests after idle, or setting up a branch per pull request.

Use it when

  • Connecting serverless or edge functions to Neon or Supabase Postgres
  • Errors about connection limits or prepared statements behind a pooler
  • First requests after idle are slow or time out
  • Setting up per-PR or per-test database branches

Use something else when

How to check the result

A burst of concurrent requests against a preview shows no connection or prepared statement errors, the first request after idle finishes within the timeout, and CI migrates and deletes each PR branch.

The mistake it prevents

Pointing migrations at the transaction-mode pooled URL, where session advisory locks fail and the migration tool hangs.

~/.agents/skills/serverless-postgres/SKILL.md

--- name: serverless-postgres description: Runs applications on serverless and managed Postgres such as Neon and Supabase - pooled versus direct connection strings, transaction-mode pooler… license: MIT ---

Done well means: runtime traffic goes through a pooler with a driver configured for it, migrations and session-dependent work use the direct connection, cold starts are either removed for production or absorbed by timeouts and retries, and every preview or CI run gets its own disposable branch that never contains unmasked production personal data.

Inspect first

  1. Read AGENTS.md; find the deploy target (Vercel, Netlify, AWS Lambda, Cloudflare Workers, containers) and runtime (Node, edge/V8 isolate, Deno, Bun).
  2. Find the provider from connection strings in .env.example or config (never print real secrets): Neon hosts end in neon.tech (pooled hosts contain -pooler); Supabase pooler hosts look like *.pooler.supabase.com, direct hosts db.<ref>.supabase.co.
  3. Find the driver and ORM (pg, postgres, @neondatabase/serverless, Prisma, Drizzle, Kysely, SQLAlchemy) and how many clients each instance creates.
  4. Check whether migrations run in CI, at build, or at app start, and which URL they use.

Connection strings

UseNeonSupabase
App runtime in serverless functionsPooled host (-pooler), PgBouncer transaction modeSupavisor transaction mode, port 6543
Long-lived server needing session featuresDirect hostSupavisor session mode (port 5432 on pooler host) or direct
Migrations, pg_dump, LISTEN, advisory locks held across statementsDirect hostDirect host (IPv6 by default; session pooler if IPv4 only)
Edge runtimes without TCP@neondatabase/serverless over HTTP/WebSocketsupabase-js Data API, or an HTTP-capable driver

Keep two variables: DATABASE_URL (pooled, runtime) and DIRECT_URL (or DATABASE_URL_UNPOOLED) for migrations. Point ORM migration config at the direct one (Prisma 7: datasource.url in prisma.config.ts; Drizzle: dbCredentials.url in drizzle.config.ts).

Transaction-mode pooler rules

In transaction mode a client gets a server connection only for one transaction, so anything that assumes the same session across statements breaks:

  • SET without LOCAL, session-level advisory locks, temp tables, LISTEN/NOTIFY, and WITH HOLD cursors do not work reliably. Use SET LOCAL / set_config(..., true) inside a transaction (this matters for RLS tenant context).
  • Named prepared statements: supported only if the pooler is configured for them (PgBouncer 1.21+ with max_prepared_statements). If you see prepared statement "s1" already exists or does not exist, disable them in the driver:
DriverSetting
postgres.jspostgres(url, { prepare: false })
asyncpg / SQLAlchemystatement_cache_size=0 and prepared_statement_cache_size=0
Prisma 6 (built-in engine)?pgbouncer=true on the pooled URL
node-postgres (pg)Unnamed statements by default; avoid passing name in queries

Pooler and pool sizing in depth: $connection-pooling.

Drivers per runtime

  • Node serverless (Lambda, Vercel Node functions): pg or postgres.js against the pooled URL. Create the client outside the handler so warm invocations reuse it; keep the in-process pool small (max: 1-5) because each instance has its own pool.
  • Edge / Workers: @neondatabase/serverless:
import { neon } from "@neondatabase/serverless";

const sql = neon(process.env.DATABASE_URL!);
const rows = await sql`select id, name from projects where org_id = ${orgId} limit 20`;

The neon() HTTP function suits single queries and non-interactive transactions (sql.transaction([...])). For interactive transactions or session features, use its Pool/Client over WebSockets, created and closed within the request on edge runtimes.

  • Cloudflare Workers with TCP: Hyperdrive in front of Postgres pools and caches connections; use it with pg or postgres.js when available to the project.
  • Containers / long-lived servers: standard pool, direct or session-mode URL; the serverless constraints above do not apply.

Cold starts and timeouts

  • Neon computes scale to zero after a period of inactivity (5 minutes by default); the first query then waits for the compute to start, typically a few hundred milliseconds, sometimes more. Supabase free projects pause after longer inactivity and need a manual resume.
  • Production: disable scale-to-zero or raise the suspend timeout if latency matters (plan dependent); keep it on for previews and dev branches to save cost.
  • Set connect timeouts to tolerate a wake-up (for example 10-15 s for pg connectionTimeoutMillis), and retry connection errors once with jitter. Do not retry non-idempotent writes blindly.
  • Do not “keep warm” with cron pings in production as a substitute for configuration; it costs compute and hides the setting.

Branching for previews and CI

Neon branches are copy-on-write copies of a parent at a point in time.

neonctl branches create --name preview/pr-123 --parent main      # needs network + NEON_API_KEY
neonctl connection-string preview/pr-123 --pooled
neonctl branches reset preview/pr-123 --parent                   # refresh from parent (discards branch changes)
neonctl branches delete preview/pr-123

In GitHub Actions, Neon publishes create/delete branch actions; pin them to a current major version and pass the branch URL to migrations and the preview deploy. Supabase branching creates preview projects per Git branch through its GitHub integration, running supabase/migrations and supabase/seed.sql.

Rules:

  • Run migrations against the branch in CI before deploying the preview; a failing migration fails the PR.
  • Delete branches when PRs close; orphaned branches cost storage and compute.
  • A branch of production contains production data. Branch from a masked or seeded parent for previews accessible to more people; never expose real customer data in preview apps.
  • branches reset, branches delete, and anything touching the production branch require explicit user confirmation naming the branch.

Migrations

  • Run through the direct URL, from one place (CI job or release step), never from every function instance at cold start.
  • Set lock_timeout (for example SET lock_timeout = '5s') in migrations so a blocked DDL fails fast instead of queueing every request behind it.
  • Large-table changes follow expand/contract ($zero-downtime-migrations).

Gotchas

  • Using the pooled URL for migrations: tools that rely on session advisory locks (Prisma Migrate, many others) hang or fail.
  • Creating a new client per request in Node functions exhausts connections under burst traffic.
  • Supabase direct connections are IPv6; IPv4-only platforms need the session pooler or the IPv4 add-on.
  • Provider connection limits scale with compute size; check them before raising pool sizes.
  • Copying a production connection string into .env files committed to the repo; keep real URLs in the platform’s secret store.

Verification

  • Load test or burst (for example 100 concurrent requests) against a preview: no too many connections or prepared statement errors.
  • First request after idle completes within the configured timeout; measure it.
  • CI log shows migrations applied to the PR branch and the branch deleted on close.
  • select count(*) from pg_stat_activity on the direct connection stays within limits under load.

Output

**Provider/runtime:** <Neon | Supabase> on <Vercel | Lambda | Workers | ...>
**URLs:** runtime=<pooled, mode>, migrations=<direct>
**Driver config:** <driver, pool max, prepare setting, timeouts>
**Cold start policy:** <scale-to-zero on/off per environment, measured wake time>
**Branching:** <tool, naming, data source (masked/seeded), cleanup>

What the zip contains

SKILL.md plus the interface file below. Nothing runs on install.

agents/openai.yamlgenerated from this page

Gives the Codex app a display name, a one-line description and a starter prompt for this skill.

interface:
  display_name: "Serverless Postgres"
  short_description: "Pooled at runtime, direct for migrations, branched per PR"
  default_prompt: "$serverless-postgres Our Vercel functions hit too many connections on Neon, fix the connection setup"

About this skill

Written and maintained by the Codex Skills editors to the same editorial standard as every skill here: a selective description, a real workflow, known pitfalls and a way to verify the work. First published , last updated . MIT licensed. Something wrong or out of date? Tell us.

esc
Just launched

Codex Skills Pro is live

79 premium skills for Codex, each with scripts, references and an evidence report that proves the result.

Bought one by one$882.21
All 79 skills$49
94% off

That's $0.62 a skill, paid once.

Get all 79 for $49Or pick single skills from $4.99 →

Secure checkout by Creem