database-pg driver
What it does
Each tenant gets its own PostgreSQL database named tenant_<uuid> (configurable via tenantDatabasePrefix). Connections are independent; nothing is shared at the database level.
Requirements
- The Lucid template connection role must have the
CREATEDBprivilege. CREATE DATABASEcannot run inside a transaction. The driver runs it outside one; your hooks must too.destroycallspg_terminate_backendon every active session before issuingDROP DATABASE IF EXISTSto avoid the classic "database is being accessed by other users" failure.
Configuration
ts
isolation: {
driver: 'database-pg',
tenantDatabasePrefix: 'tenant_', // optional; defaults to tenantSchemaPrefix
templateConnectionName: 'tenant',
}Provision flow
- Validate the tenant id (
assertSafeIdentifier). CREATE DATABASE "tenant_<uuid>"on the template connection (no transaction).- Register a per-tenant Lucid connection pointed at the new database.
- Run migrations against it.
Destroy flow
pg_terminate_backendon every backend withdatname = 'tenant_<uuid>'(excluding the current process).DROP DATABASE IF EXISTS "tenant_<uuid>".- Close and unregister the Lucid connection.
Trade-offs
| Pro | Con |
|---|---|
| Per-tenant credentials and roles | Separate connection pool per tenant; costlier |
| Tenant data lives in different files / WAL | Can't JOIN across tenants for reporting |
| Easy to replicate or relocate one tenant | Migrations run N times instead of once |
pg_dump per tenant is a single-database dump | Tenant counts in the thousands strain the connection budget |
When this driver shines
- Regulated industries where data residency or contractual separation is non-negotiable.
- Tenants with vastly different sizes; putting the largest tenant on its own database means it can be relocated to a bigger machine without touching the others.
Operational notes
- Backups should iterate tenants and call
pg_dumpper database, not per schema; Lasagna'stenant:backupalready does this when the active driver isdatabase-pg. - Health checks (
tenant:doctor) include a connectivity probe per tenant. With many tenants this becomes a non-trivial pass; use--tenant=<id>to limit during incidents.