Deploy

Neon

Neon's HTTP and WebSocket drivers in production, node-postgres against the socket in development, and the migrations applied from CI.

Neon is a Postgres reached through DATABASE_URL, like any other. What sets it apart is its serverless drivers.

Drivers

Neon's serverless drivers (@neondatabase/serverless, through drizzle-orm/neon-http or drizzle-orm/neon-serverless) speak HTTP and WebSocket to Neon's proxy, not the Postgres wire protocol. That is what makes them fast from a serverless function, and why they cannot reach the development socket, which speaks the wire protocol only.

Use node-postgres in development and switch on import.meta.dev, which the bundler replaces with a constant, so only the production driver is in the build:

server/utils/db.ts
import { drizzle as drizzleNeon } from 'drizzle-orm/neon-http'
import { drizzle as drizzlePg } from 'drizzle-orm/node-postgres'

import { relations } from '../database/relations'

export function useDB() {
  const url = process.env.DATABASE_URL!
  return import.meta.dev ? drizzlePg(url, { relations }) : drizzleNeon(url, { relations })
}

Both drivers return the same Drizzle API, so the routes do not know which one they got. If you do not need the serverless drivers, connect to Neon with pg in production too, through its standard connection string, and skip the switch.

Migrations

Neon does not apply migrations: you do. Apply the same files init applies locally, with the production DATABASE_URL, from CI or the build command, as for any Postgres:

.github/workflows/deploy.yml
- run: pnpm drizzle-kit migrate
  env:
    DATABASE_URL: ${{ secrets.DATABASE_URL }}

Give it Neon's direct (unpooled) connection string, as Neon recommends for schema changes: its pooler runs in transaction mode, which session-level statements in a migration can trip on.

Branches

Neon branches a database in seconds, which pairs well with preview deployments: create a branch per pull request in CI, apply the migrations to it, and hand its URL to the preview as DATABASE_URL. Production gets them once the branch is merged.