Any Postgres
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
export default defineNuxtConfig({
modules: ['nuxt-pglite'],
pglite: {
server: {
enabled: false,
},
socket: { port: 5433 }, // exports `DATABASE_URL` in `nuxt dev`
},
})
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.
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"
}
}
- run: pnpm drizzle-kit migrate
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
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.
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.