Isolate Tenants with a Schema per Tenant
Give each tenant its own database schema, route connections by tenant, and run migrations across all schemas so isolation does not become a migration burden.
Task
Provision per-tenant schemas and automate migrations to keep tenant data isolated.
When to use this
All tenants share one schema, so a single missing WHERE clause can leak another tenant rows, and noisy neighbors affect everyone.
Prerequisites
- A database that supports multiple schemas
- A migration tool that can target each schema
Steps
1. Create a schema per tenant and store the mapping in a registry table.
Expected output: Each tenant has a dedicated namespace.
2. Set search_path on connection checkout based on the resolved tenant.
Expected output: Queries resolve to the right schema without tenant columns.
3. Run migrations once per schema in a controlled loop.
Expected output: All schemas stay on the same version.
4. Verify isolation with a test that queries as one tenant and asserts it cannot read another.
psql -c "SET search_path TO tenant_acme, public; SELECT count(*) FROM orders;"Expected output: Cross-tenant leakage is caught before release.
Troubleshooting
Problem
Migration loop fails midway
Solution
Record per-schema version and resume idempotently; never assume all schemas migrated.
Problem
Connection pool reuse across tenants
Solution
Reset search_path on release, or use a pool per tenant, to prevent bleed between requests.
Limitations
- Schema-per-tenant multiplies migration and connection overhead as tenant count grows.
- Database-level limits on schema and connection counts cap how far this model scales.
Context
Provision per-tenant schemas and automate migrations to keep tenant data isolated. All tenants share one schema, so a single missing WHERE clause can leak another tenant rows, and noisy neighbors affect everyone.
Steps
- Create a schema per tenant and store the mapping in a registry table. Expected: Each tenant has a dedicated namespace.
- Set search_path on connection checkout based on the resolved tenant. Expected: Queries resolve to the right schema without tenant columns.
- Run migrations once per schema in a controlled loop. Expected: All schemas stay on the same version.
- Verify isolation with a test that queries as one tenant and asserts it cannot read another. Expected: Cross-tenant leakage is caught before release.
SELECT set_config('search_path', 'tenant_acme, public', false);
-- all unqualified queries now resolve to tenant_acme
Verify
- Each tenant has a dedicated namespace.
- Queries resolve to the right schema without tenant columns.
- All schemas stay on the same version.
- Cross-tenant leakage is caught before release.
Limits
- Schema-per-tenant multiplies migration and connection overhead as tenant count grows.
- Database-level limits on schema and connection counts cap how far this model scales.