User Guide

Manifest guide

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
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.

Managed owners still need declared privileges

If the role that owns tables or functions is also inside pgroles' managed role set, declare the privileges you want that owner role to keep. PostgreSQL owners effectively have broad privileges on their own objects, and pgroles treats undeclared current privileges as drift in authoritative mode.

Convergent model

pgroles is convergent

The manifest represents the entire desired state. Roles, grants, default privileges, and memberships that exist in the database but are absent from the manifest will be dropped or revoked. Declared schemas are created and their owner may be converged, but schemas are not dropped automatically. Only declare roles and schemas that pgroles should manage.

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