PostgreSQLV · Authentication, Roles and TLSAuthorisation
Roles, membership and inheritance
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
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.
$ 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.
$ 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 afterSET ROLE.set_option— may the member runSET ROLEto 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.
$ 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_filesThe ones that replace a superuser grant most often:
| Role | Grants | Instead of |
|---|---|---|
pg_monitor | Read every statistics view | Superuser for monitoring |
pg_read_all_data | SELECT on everything | Superuser for a reporting tool |
pg_signal_backend | Cancel and terminate other sessions | Superuser for an on-call role |
pg_maintain | VACUUM, ANALYZE, REINDEX, CLUSTER on any relation | Superuser for a maintenance job |
pg_use_reserved_connections | Use the reserved slots | Superuser for emergency access |
pg_checkpoint | Run CHECKPOINT | Superuser 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
- Own objects with a
NOLOGINrole. No credential should exist that can drop the schema. - Grant privileges to groups, never to login roles. The review question becomes answerable.
- Check
inherit_optionbefore concluding a privilege is missing. ANOINHERITmembership looks identical to no membership until you look. - Use the predefined roles rather than superuser.
pg_monitor,pg_signal_backendandpg_use_reserved_connectionscover incident response between them. - Treat
pg_read_all_dataand the server-file roles as broad. The first reads every table in the cluster; the others are host access. - Record
session_useras well ascurrent_userin any audit trail.SET ROLEchanges 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
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?
Q2. Which set of predefined roles gives an on-call engineer what Part IV established as the incident-response minimum, without granting superuser?
Q3. Which statements about roles and membership are correct? Select all that apply.
Q4. Granting a privilege directly to a login role is a reasonable fix when a group membership is not delivering it.
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.
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.