Skip to main content
RunBook Academy

PostgreSQLV · Authentication, Roles and TLSAuthorisation

Roles, membership and inheritance

Intermediate⏱ ~25 minpsql

What you'll learn

  • Distinguish role attributes from role membership
  • Predict whether a member enjoys a granted role automatically or only after SET ROLE
  • Design a group-based privilege structure that survives staff and service changes
  • Use the predefined roles instead of granting superuser

Prerequisites

Verified against PostgreSQL 18.x · PostgreSQL (comparison targets) 17.11, 16.15 · PostgreSQL (support calendar) 18, 17, 16, 15, 14 supported · pgBackRest 2.59.1 · PgBouncer 1.25.2 · Patroni 4.1.5 · Ubuntu (host baseline) 26.04 LTS · 2026-08-27

Not yet marked complete on this device.

PostgreSQL has no separate concept of a user and a group. There are only roles, and a role that can log in is what everyone calls a user. That uniformity is convenient and it means the distinctions that matter are elsewhere: in a role’s attributes, and in its memberships.

Attributes versus membership

Attributes are properties of the role itself: whether it can log in, create databases, create roles, bypass row-level security, or is a superuser.

Membership is a relationship between roles, and it is how privileges on tables and schemas are actually delivered.

Read-only / Safefive roles: three groups and two service accounts
$ psql -U postgres -c "SELECT rolname, rolcanlogin AS login, rolinherit AS inherit FROM pg_roles WHERE rolname LIKE 'app%' OR rolname LIKE 'svc%' ORDER BY rolname"
  rolname  | login | inherit
-----------+-------+---------
app_owner | f     | t
app_ro    | f     | t
app_rw    | f     | t
svc_rpt   | t     | f
svc_web   | t     | t
(5 rows)

app_owner, app_rw and app_ro have no LOGIN, so nothing can connect as them. They exist to hold privileges. svc_web and svc_rpt can log in and hold almost no privileges of their own.

Inheritance is a property of the grant

This is the part that surprises people. Whether a member automatically enjoys the privileges of a role it belongs to is recorded per grant, not on either role.

Read-only / Safetwo memberships, two different inherit options
$ psql -U postgres -c 'SELECT r.rolname AS member, g.rolname AS member_of, m.admin_option, m.inherit_option, m.set_option FROM pg_auth_members m JOIN pg_roles r ON r.oid=m.member JOIN pg_roles g ON g.oid=m.roleid ORDER BY 1'
 member  | member_of | admin_option | inherit_option | set_option
---------+-----------+--------------+----------------+------------
svc_rpt | app_ro    | f            | f              | t
svc_web | app_rw    | f            | t              | t
(2 rows)

Three independent options:

  • inherit_option — does the member get the privileges automatically? If false, it holds them only after SET ROLE.
  • set_option — may the member run SET ROLE to become the group at all?
  • admin_option — may the member grant this membership to others?

Since PostgreSQL 16 these are set per grant, so the same role can inherit one membership and not another:

GRANT app_ro TO svc_rpt WITH INHERIT FALSE;   -- must SET ROLE to use it
GRANT app_rw TO svc_web WITH INHERIT TRUE;    -- automatic
GRANT app_rw TO lead    WITH ADMIN OPTION;    -- may grant it onward

A structure that survives change

The pattern that works, and the reasoning behind each layer:

-- 1. An owning role that holds the objects. No login, ever.
CREATE ROLE app_owner NOLOGIN;

-- 2. Access groups describing what may be done, not who may do it.
CREATE ROLE app_rw NOLOGIN;
CREATE ROLE app_ro NOLOGIN;

-- 3. Login roles for each service, holding no privileges of their own.
CREATE ROLE svc_web LOGIN PASSWORD 'set-a-real-secret';
CREATE ROLE svc_etl LOGIN PASSWORD 'set-a-real-secret';

-- 4. Membership connects them.
GRANT app_rw TO svc_web;
GRANT app_ro TO svc_etl;

Why each layer earns its place:

Objects owned by a role that cannot log in means no credential exists that owns the schema. Compromising a service account does not give the ability to drop tables, because ownership lives elsewhere.

Groups named for capability means privileges are granted once, to app_rw, rather than repeated per service. Adding a service is one GRANT, and removing one is one REVOKE with no chance of leaving stray privileges behind.

Login roles that hold nothing directly means the review question is “which groups is this member of”, which is answerable, rather than “what has been granted to this role across every schema”, which is not.

The test of the structure is a personnel or service change. If adding a new reporting service requires granting privileges on tables, the structure has been bypassed somewhere.

Predefined roles instead of superuser

PostgreSQL ships roles that grant specific administrative capabilities. Using them is how you avoid handing out superuser for a narrow need.

Read-only / Safethe predefined roles in PostgreSQL 18
$ psql -U postgres -tAc "SELECT string_agg(rolname, ', ' ORDER BY rolname) FROM pg_roles WHERE rolname LIKE 'pg\_%'"
pg_checkpoint, pg_create_subscription, pg_database_owner,
pg_execute_server_program, pg_maintain, pg_monitor, pg_read_all_data,
pg_read_all_settings, pg_read_all_stats, pg_read_server_files,
pg_signal_autovacuum_worker, pg_signal_backend, pg_stat_scan_tables,
pg_use_reserved_connections, pg_write_all_data, pg_write_server_files

The ones that replace a superuser grant most often:

RoleGrantsInstead of
pg_monitorRead every statistics viewSuperuser for monitoring
pg_read_all_dataSELECT on everythingSuperuser for a reporting tool
pg_signal_backendCancel and terminate other sessionsSuperuser for an on-call role
pg_maintainVACUUM, ANALYZE, REINDEX, CLUSTER on any relationSuperuser for a maintenance job
pg_use_reserved_connectionsUse the reserved slotsSuperuser for emergency access
pg_checkpointRun CHECKPOINTSuperuser for a backup script

An on-call role built from these covers most incident work:

CREATE ROLE oncall LOGIN PASSWORD 'set-a-real-secret';
GRANT pg_monitor, pg_signal_backend, pg_use_reserved_connections TO oncall;

That role can read every statistic, terminate a blocking session and connect when the cluster is saturated — the three things Part IV established as the incident-response minimum — and it cannot read application data, cannot change configuration, and cannot drop anything.

Three of those roles deserve a caution. pg_read_all_data and pg_write_all_data grant access to every table in every database in the cluster, which is broader than most reporting needs. pg_execute_server_program, pg_read_server_files and pg_write_server_files are effectively host access and are covered in the next lessons as superuser-equivalent.

Production discipline

  1. Own objects with a NOLOGIN role. No credential should exist that can drop the schema.
  2. Grant privileges to groups, never to login roles. The review question becomes answerable.
  3. Check inherit_option before concluding a privilege is missing. A NOINHERIT membership looks identical to no membership until you look.
  4. Use the predefined roles rather than superuser. pg_monitor, pg_signal_backend and pg_use_reserved_connections cover incident response between them.
  5. Treat pg_read_all_data and the server-file roles as broad. The first reads every table in the cluster; the others are host access.
  6. Record session_user as well as current_user in any audit trail. SET ROLE changes one and not the other, and the investigation needs both.

Cross-course references

  • Secrets, PKI & Certificate Management — Part XII (Secret management platforms) covers least-privilege policy design, and Part I (Foundations) covers computing blast radius, which is the same reasoning applied to a role.
  • Linux for Production Sysadmins — Part IV (Users) and Part V (Sudo) cover the host equivalents of groups and privilege escalation.
  • Kubernetes for Production Sysadmins — Part LVIII (RBAC) covers the same group-based structure in a cluster authorisation model.

Quiz

Knowledge check · 6 questions

  1. Q1. An application role is a member of app_rw, which holds SELECT and INSERT on every application table. The application still receives 'permission denied for table orders'. What should you check first?

  2. Q2. Which set of predefined roles gives an on-call engineer what Part IV established as the incident-response minimum, without granting superuser?

  3. Q3. Which statements about roles and membership are correct? Select all that apply.

  4. Q4. Granting a privilege directly to a login role is a reasonable fix when a group membership is not delivering it.

  5. Q5. Explain why application objects should be owned by a role that cannot log in, and what test tells you the structure is being bypassed.

  6. Q6. Review the permission model and give the restructuring.

    An audit of a four-year-old database finds 61 login roles, of which 9 are superusers. Privileges have been granted directly to login roles on individual tables; there are no NOLOGIN group roles. Tables are owned by whichever login role happened to create them, spread across 14 different owners. Three of the superusers are shared service accounts whose credentials appear in several application configuration files. Nobody can answer the question 'what can the reporting service read'.

Passing score: 75%. Answers are checked in this browser.