New Database Setup
Use this workflow when standing up a new PostgreSQL database for an application: schemas, roles, privileges, extensions, and the first migrations.
Guideline Routing
Always load these guideline pages:
- House style and Postgres philosophy
- Schema layout and search_path
- Roles, privileges, and row-level security
- Object naming
- Primary keys and row identity
- Standard columns and row lifecycle
Workflow
- Confirm the PostgreSQL version; target PG18+. Record the version assumption where the project documents its stack.
- Create the three-role topology first (
{app}_owneras the migration login,{app}_rw,{app}_ro), per the roles guideline. - Create the database with
UTF8encoding,OWNER {app}_owner(only the database owner holdsCREATEonpublicsince PG15, and migrations need it), and, unless the project has a locale requirement, a deterministic default collation. - Lock down world access per the roles guideline: revoke
CONNECTon the database andCREATEonpublicfromPUBLIC, and grantCONNECTto the three roles. - Apply the grants,
ALTER DEFAULT PRIVILEGES, and standing role timeouts per the roles guideline, and create login roles as members. - Configure the migration tool to connect as
{app}_owner(its only client) and the application as the{app}_rwlogin member; verify the app connection cannot run DDL and that objects created by migrations are owned by{app}_owner. - Install only extensions the project needs now, each in its own migration with a comment saying what uses it (for example
btree_gistthe first time a temporal or exclusion constraint needs it). - Create the shared
set_updated_at()trigger function before the first table migration so tables can attach it immediately. - Write the first table migrations following the schema-design guidelines:
uuidv7()keys, lifecycle columns with triggers, FKs with indexes,NOT NULLdefaults, canonical constraint names. - Seed lookup tables in migrations, not by hand; the table shape lives in enums, domains, and lookup tables.
- Set up the safe-migration guardrails from day one:
lock_timeoutin the migration template and the safe schema migration workflow linked from the project docs, so habits do not change when the database goes live. - Verify the topology before first deploy: connect as each login (owner, app member, read-only member) and confirm it can do exactly what it should (owner: DDL; rw: DML only, no DDL; ro: SELECT only).
Avoid
- Do not develop and deploy as a superuser from the application; the owner login belongs to the migration pipeline alone.
- Do not install extensions speculatively.
- Do not hand-create objects outside migrations, even during setup; the first environment rebuild will miss them.
- Do not defer the role topology “until production”; retrofitting grants across an accumulated schema is the painful version.
- Do not copy configuration from another project without checking the version assumptions and extension list.