Shared Databases
Multi-tenant database access — provision isolated databases on a shared PostgreSQL cluster.
Overview
Shared Databases let you sell cheap, isolated database access to end-clients on a single DigitalOcean Managed PostgreSQL cluster. Each client gets a dedicated logical database with its own PostgreSQL role and connection pool — completely isolated from other tenants.
Clients connect directly to PostgreSQL using the credentials returned at provisioning time. Each tenant's role is restricted to its own database with REVOKE ALL FROM PUBLIC, so there is no risk of cross-tenant data access.
Architecture
┌─────────────┐ ┌───────────────────┐ ┌───────────────────┐
│ End-Client │────▶│ PgBouncer Pool │────▶│ PostgreSQL │
│ (credentials)│ │ (per-tenant) │ │ Cluster (shared) │
└─────────────┘ └───────────────────┘ └───────────────────┘Tiers
| Tier | Max Connections | Storage Limit | Pool Size |
|---|---|---|---|
| MICRO | 2 | 2 GB | 3 |
| STARTER | 3 | 5 GB | 5 |
| GROWTH | 10 | 20 GB | 15 |
| SCALE | 25 | 50 GB | 30 |
Management Endpoints
These endpoints require session authentication and project membership.
List tenant databases
GET /v1/projects/:projectId/databases/sharedReturns all active shared database tenants for the project.
Get tenant detail
GET /v1/projects/:projectId/databases/shared/:tenantIdReturns tenant details including connection info.
List tier prices
GET /v1/projects/:projectId/databases/shared/pricesReturns the recurring monthly list price for each tier, sourced from Stripe. Amounts are in minor units (e.g. cents) alongside an upper-case ISO currency code.
Response:
{
"success": true,
"data": [
{ "tier": "MICRO", "monthly": 500, "currency": "USD" },
{ "tier": "STARTER", "monthly": 900, "currency": "USD" },
{ "tier": "GROWTH", "monthly": 2900, "currency": "USD" },
{ "tier": "SCALE", "monthly": 9900, "currency": "USD" }
]
}Provision a tenant database
POST /v1/projects/:projectId/databases/sharedBody:
| Field | Type | Required | Description |
|---|---|---|---|
dbName | string | yes | Logical database name (lowercase, letters/numbers/underscores) |
tier | string | no | MICRO, STARTER (default), GROWTH, or SCALE |
poolMode | string | no | TRANSACTION (default), SESSION, or STATEMENT |
The platform automatically selects an available shared PostgreSQL cluster. The prorated amount is charged to the project's payment method off-session — there is no checkout redirect.
Response: Returns tenant credentials including dbPassword, connectionUri, and optional poolConnectionUri. The password is shown once — store it securely. If the cluster is still being set up, returns { tenantId, status: "PROVISIONING" } instead — poll the detail endpoint until the status becomes ACTIVE.
If the project has no payment method, the request returns 402 with a code of ORG_PAYMENT_METHOD_REQUIRED (you are the biller — add a card and retry) or BILLER_PAYMENT_METHOD_REQUIRED (ask your biller to add one). See Billing → Deploy Payment Errors.
Rotate password
POST /v1/projects/:projectId/databases/shared/:tenantId/rotate-passwordGenerates a new password for the tenant's database role and returns the new credentials. The old password stops working immediately.
Update tier
PATCH /v1/projects/:projectId/databases/shared/:tenantId/tierBody:
| Field | Type | Required | Description |
|---|---|---|---|
tier | string | yes | MICRO, STARTER, GROWTH, or SCALE |
Deprovision tenant
DELETE /v1/projects/:projectId/databases/shared/:tenantIdRevokes database access and removes the connection pool. The logical database is retained for data recovery.
Connecting as a Client
Clients connect directly to their PostgreSQL database using any standard PostgreSQL client or ORM. Use the credentials returned during provisioning.
Connection string
postgresql://<dbUser>:<dbPassword>@<host>:<port>/<dbName>?sslmode=requirePool connection string
When a PgBouncer pool is configured, clients can also connect through the pool for better connection management:
postgresql://<dbUser>:<dbPassword>@<host>:<poolPort>/<poolName>?sslmode=requireExample (Node.js)
import pg from "pg";
const pool = new pg.Pool({
connectionString: "postgresql://tenant_abc:secret@host:25060/tenant_abc_db?sslmode=require",
});
const result = await pool.query("SELECT * FROM users WHERE id = $1", [userId]);Security
Each tenant's PostgreSQL role is locked down:
- Database isolation —
REVOKE ALL ON DATABASE ... FROM PUBLICprevents access to other tenants' databases. - Schema isolation —
REVOKE ALL ON SCHEMA public FROM PUBLICwithin each database. - Connection limits — enforced at the PostgreSQL role level per tier.
- SSL required — all connections require
sslmode=require.