Manifest guide

On this page

A pgroles manifest declares the desired state of PostgreSQL access control: who can log in, which group roles exist, what they can access, and what future objects should inherit.


The shape of a manifest

Most manifests combine four ideas:

  • roles define PostgreSQL roles and login attributes
  • grants define access to current database objects
  • default_privileges define access for objects created later
  • memberships connect login roles to group roles

For larger databases, profiles and schemas remove repetition by expanding one privilege template across many schemas. For field-by-field details, use the manifest reference.

A complete application example

default_owner: app_migrator

profiles:
  app_writer:
    grants:
      - privileges: [USAGE]
        object: { type: schema }
      - privileges: [SELECT, INSERT, UPDATE, DELETE]
        object: { type: table, name: "*" }
      - privileges: [USAGE, SELECT, UPDATE]
        object: { type: sequence, name: "*" }
    default_privileges:
      - privileges: [SELECT, INSERT, UPDATE, DELETE]
        on_type: table
      - privileges: [USAGE, SELECT, UPDATE]
        on_type: sequence

  readonly:
    grants:
      - privileges: [USAGE]
        object: { type: schema }
      - privileges: [SELECT]
        object: { type: table, name: "*" }
    default_privileges:
      - privileges: [SELECT]
        on_type: table

schemas:
  - name: app
    owner: app_migrator
    profiles: [app_writer, readonly]

roles:
  - name: app_migrator
    login: true
  - name: app_runtime
    login: true
  - name: analyst
    login: true

memberships:
  - role: app-app_writer
    members:
      - name: app_runtime
  - role: app-readonly
    members:
      - name: analyst

This produces two schema-scoped group roles:

  • app-app_writer, with read/write table access and sequence access in the app schema
  • app-readonly, with read-only table access in the app schema

The login roles (app_runtime, analyst) get access through memberships rather than direct grants. This keeps runtime connection strings stable while letting you change profile grants in one place.

How the sections connect

SectionDefinesReferenced by
role_patternNaming convention for generated roles, with per-schema overridesprofiles, schemas
default_ownerDefault object-creator role for future grantsdefault_privileges, profiles, schemas
profilesReusable schema-relative grant templatesschemas[].profiles
schemasManaged schemas and profile bindingsprofiles, generated roles, generated grants
rolesExplicit PostgreSQL rolesgrants, memberships, default_privileges
grantsCurrent object privilegesrole names and object targets
default_privilegesFuture object privilegesowner roles, grantee roles, schemas
membershipsRole inheritance edgesparent roles and member roles
retirementsSafe role removal workflowroles omitted from desired state

Choosing direct grants or profiles

Use direct top-level grants for one-off roles or database-level privileges:

roles:
  - name: analytics
    login: true

grants:
  - role: analytics
    privileges: [CONNECT]
    object: { type: database, name: mydb }
  - role: analytics
    privileges: [USAGE]
    object: { type: schema, name: reporting }
  - role: analytics
    privileges: [SELECT]
    object: { type: table, schema: reporting, name: "*" }

Use profiles when the same access shape repeats across schemas:

profiles:
  reader:
    grants:
      - privileges: [USAGE]
        object: { type: schema }
      - privileges: [SELECT]
        object: { type: table, name: "*" }

schemas:
  - name: inventory
    profiles: [reader]
  - name: catalog
    profiles: [reader]

Declared vs referenced schemas

Declared schemas can be created and have their owner converged by pgroles. Referenced-only schemas must already exist:

schemas:
  - name: app_managed
    owner: app_owner
    profiles: []

grants:
  - role: reporting
    privileges: [USAGE]
    object: { type: schema, name: app_managed }

  - role: reporting
    privileges: [SELECT]
    object: { type: table, schema: existing_warehouse, name: "*" }

In this example:

  • app_managed is declared under schemas:, so pgroles can create it and set its owner.
  • existing_warehouse is only referenced from a top-level grant, so it must already exist before apply runs.

Wildcard grants and future objects

Use name: "*" to grant on all current objects of a type in a schema. pgroles expands relation wildcards safely by object type, so table, view, and materialized_view privileges do not bleed across each other.

Pair wildcard grants with default_privileges when the same role should access future objects created by the migration or owner role. Wildcards cover existing objects at reconcile time; default privileges cover objects created later.

Convergent model

Next: use the manifest reference for exact field names, defaults, and bundle-mode details.