Deploy

Netlify

Netlify Database in production, the socket's netlify preset in development, and the committed migrations applied by Netlify on deploy. This site is the worked example.

Netlify Database is a platform that applies the migrations itself, and sets its own variable rather than DATABASE_URL. This documentation site runs on it, so it is the worked example: the PGlite in dev, Postgres in prod setup, with the socket's netlify preset standing in for Netlify Database in development, plus an in-browser instance for the examples:

docs/nuxt.config.ts
export default defineNuxtConfig({
  modules: ['nuxt-pglite'],
  pglite: {
    client: { enabled: true }, // the interactive examples
    server: {
      enabled: false, // nothing of PGlite on the server
    },
    socket: { port: 5456, provider: 'netlify' }, // `NETLIFY_DB_URL` while `nuxt dev` runs
  },
})

How it fits together

Netlify Database is a Postgres that Netlify provisions for the site. It sets NETLIFY_DB_URL in builds and functions (never DATABASE_URL), and applies the SQL files in netlify/database/migrations itself, right before it publishes a deploy. Each deploy preview gets its own database branch, with the migrations of that deploy applied to it.

nuxt devDeployed on Netlify
NETLIFY_DB_URLthe socket's URL, from the netlify presetthe site's database (or the preview's branch)
NETLIFY_DB_DRIVERserver: pg over the wire protocolNetlify's default: Neon's serverless driver
netlify/database/migrationsapplied by init with applyMigrationsapplied by Netlify before publishing
Applied migrations recorded innetlify.migrations, applyMigrations's defaultnetlify.migrations

The routes only know drizzle-orm/netlify-db. Nothing else changes between development and production.

Try it

The demo below calls /api/visits, a route that uses Drizzle over NETLIFY_DB_URL. In nuxt dev the socket exports that variable and the route reaches PGlite; on the deployed site Netlify sets it, and the route tells you when the database is not enabled.

What ends up in the build

Server outputClient output
PGlite engine and WebAssemblyabsent (server.enabled: false)present, loaded in a worker on first use
server/pglite.config.tsabsent: only the socket reads it, in nuxt devabsent
app/pglite.config.tsabsentpresent
Your driver (drizzle-orm/netlify-db, pg, @neondatabase/serverless)presentabsent

Steps

Write the schema and generate migrations

server/database/schema.ts
import { pgTable, serial, timestamp } from 'drizzle-orm/pg-core'

export const visits = pgTable('visits', {
  id: serial('id').primaryKey(),
  createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
})
server/database/relations.ts
import { defineRelations } from 'drizzle-orm'

import * as schema from './schema'

export const relations = defineRelations(schema)

drizzle-kit 1.0.0-rc.4 or later writes its migrations where Netlify looks for them:

drizzle.config.ts
import { defineConfig } from 'drizzle-kit'

// Netlify applies `out` on deploy and `init` applies it locally: no `push`/`migrate`.
export default defineConfig({
  out: './netlify/database/migrations',
  schema: './server/database/schema.ts',
  dialect: 'postgresql',
  dbCredentials: {
    // the development socket, for `studio` and `check` while `nuxt dev` runs
    url: 'postgres://postgres@127.0.0.1:5456/postgres',
  },
})
Terminal
npx drizzle-kit generate --name init

Commit the generated netlify/database/migrations/<timestamp>_init/ directory: the deploy is what applies it.

Never run drizzle-kit push or drizzle-kit migrate against the site's database. Netlify records the files it applied in netlify.migrations; a schema pushed or migrated by another tool is one it does not know about, and the next deploy's migrations run against it.

Apply them in development

The socket's instance applies the same files from init, with the same bookkeeping as Netlify:

server/pglite.config.ts
import { fileURLToPath } from 'node:url'

import { applyMigrations } from 'nuxt-pglite/migrations'

const migrations = fileURLToPath(new URL('../netlify/database/migrations', import.meta.url))

export default definePGliteServerConfig({
  init: (pg) => applyMigrations(pg, migrations),
})

Query through drizzle-orm/netlify-db

Called with no connection, drizzle() from drizzle-orm/netlify-db reads NETLIFY_DB_URL, and picks pg when NETLIFY_DB_DRIVER is server, which is what the socket exports, or Neon's serverless driver otherwise, which is what Netlify runs. It needs pg and @neondatabase/serverless installed, and throws when NETLIFY_DB_URL is unset, so this site checks first:

server/utils/db.ts
import { drizzle } from 'drizzle-orm/netlify-db'

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

function createDB() {
  return drizzle({ relations })
}

let db: ReturnType<typeof createDB> | undefined

/** `undefined` without `NETLIFY_DB_URL`, so the routes can say so. */
export function useDB() {
  if (!process.env.NETLIFY_DB_URL) {
    return undefined
  }
  db ??= createDB()
  return db
}

Add a netlify.toml

Netlify detects Nitro, which picks its netlify preset by itself. In a monorepo, point the build at the app's directory:

netlify.toml
# Netlify applies `netlify/database/migrations` itself before publishing: no migration command.
[build]
  base = "docs"
  command = "pnpm build"
  publish = "dist"

[build.environment]
  NODE_VERSION = "24"

publish is relative to base: the netlify preset writes the static files to dist and the server to .netlify/functions-internal. For an app at the root of its repository, drop base.

Enable Netlify Database on the site

Netlify provisions the database for sites that depend on @netlify/database; otherwise, enable it from the site's Data & Storage → Database page in the Netlify UI. Once enabled, NETLIFY_DB_URL is set for builds and functions with nothing to configure.

Deploy

Push to the branch Netlify builds, or run netlify deploy --build. Netlify applies the pending migrations before publishing, and a failing one blocks the publish.

Without the database enabled, the site still deploys and every page works; the routes that need the database answer with a "no database configured" message naming NETLIFY_DB_URL. The demo above shows which case this deployment is in.

Netlify also refuses migrations edited or removed after they were applied. Locally, applyMigrations does the same, so the mistake shows up in nuxt dev rather than in a failed deploy.