Deploy

Any Postgres

Deploy against a Postgres reached through DATABASE_URL, on any host Nitro supports, with the migrations applied from the build or CI.

The default pattern: the development socket exports DATABASE_URL in nuxt dev, your host's environment sets it in production, and you apply the migrations before the new code goes live.

DATABASE_URL

nuxt.config.ts
export default defineNuxtConfig({
  modules: ['nuxt-pglite'],
  pglite: {
    server: {
      enabled: false,
    },
    socket: { port: 5433 }, // exports `DATABASE_URL` in `nuxt dev`
  },
})
server/utils/db.ts
import { drizzle } from 'drizzle-orm/node-postgres'

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

export function useDB() {
  return drizzle(process.env.DATABASE_URL!, { relations })
}

Set DATABASE_URL in your host's environment to any Postgres reachable from it: Supabase, Railway, a managed instance or your own server, scoped to the server runtime (and to the build, if it migrates). Use the pooled connection string when your provider has one: serverless functions open many short-lived connections.

The same works on any host Nitro supports (Vercel, a Node server, a container, …): the build contains your driver and reads DATABASE_URL, the host provides it. Only the build settings differ; see Nuxt's deployment guide.

Migrations

Keep the migration files where drizzle-kit writes them, and apply them locally from init with applyMigrations. The real database gets them from you, with the production DATABASE_URL: in the build command, or in a CI job before the deploy.

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

export default defineConfig({
  schema: './server/database/schema.ts',
  out: './server/database/migrations',
  dialect: 'postgresql',
  dbCredentials: {
    // the real database in the build or CI, the development socket otherwise
    url: process.env.DATABASE_URL ?? 'postgres://postgres@127.0.0.1:5433/postgres',
  },
})
{
  "scripts": {
    "build": "drizzle-kit migrate && nuxt build"
  }
}

The build is the simplest place when it runs with the production variables; a CI job keeps the database credentials out of the build and lets the migration fail before anything is deployed.

drizzle-kit migrate keeps its own bookkeeping (drizzle.__drizzle_migrations), separate from the one applyMigrations keeps locally: each database is tracked by the tool that migrates it, from the same files. To use one tool on both sides instead, run applyMigrations with fromPool from a script in the build command or the CI job.

A local Postgres

Nothing stops you from using a real Postgres in development too, from Docker or a local install: set DATABASE_URL (a .env file is enough) and the socket leaves it alone, since each variable is only set while unset. It warns that the variable is taken; turn socket off when you work this way for good. That is a way to check the app against the exact server version you run in production, or to reproduce a bug PGlite does not have.

Terminal
docker run -d --name postgres -p 5432:5432 -e POSTGRES_PASSWORD=postgres postgres:17
echo 'DATABASE_URL=postgres://postgres:postgres@127.0.0.1:5432/postgres' >> .env

init then does not run against that database: apply the migrations to it with drizzle-kit migrate, as in production.