List PostgreSQL Roles and Their Privileges
How to List PostgreSQL Roles and Privileges
Which accounts can log in, which can create databases or other roles, and which are quietly superusers? PostgreSQL answers all three from pg_roles, a view over the role catalog that every user can read. Joined to pg_auth_members it also shows who belongs to which group, and the privilege-inquiry functions turn that into a list of who can touch a given table.
Purpose and Overview
PostgreSQL uses one object, the role, for both users and groups. A role with the LOGIN attribute is what other systems call a user account. A role without it is usually a group that collects privileges, which are then passed to login roles by granting membership. Roles belong to the whole cluster, not to one database, so a single audit covers every database on the server.
A role audit has two halves. The first is attributes: superuser, create-database, create-role, replication, bypass-row-security, connection limit and password expiry. The second is membership: which roles hold which other roles, and with what options. The queries below return both, then go one step further to the privileges a role ends up with on actual objects.
Sample Code
All roles with their attributes:
1SELECT
2 rolname AS role_name,
3 rolsuper AS superuser,
4 rolinherit AS inherits,
5 rolcreaterole AS can_create_roles,
6 rolcreatedb AS can_create_db,
7 rolcanlogin AS can_login,
8 rolreplication AS replication_role,
9 rolbypassrls AS bypass_rls,
10 rolconnlimit AS connection_limit,
11 rolvaliduntil AS password_expiry
12FROM pg_catalog.pg_roles
13WHERE rolname !~ '^pg_'
14ORDER BY rolsuper DESC, rolcanlogin DESC, rolname;
Role membership on PostgreSQL 16 and later:
1SELECT
2 r.rolname AS member,
3 g.rolname AS member_of,
4 b.rolname AS granted_by,
5 am.admin_option,
6 am.inherit_option,
7 am.set_option
8FROM pg_catalog.pg_auth_members am
9JOIN pg_catalog.pg_roles r ON r.oid = am.member
10JOIN pg_catalog.pg_roles g ON g.oid = am.roleid
11JOIN pg_catalog.pg_roles b ON b.oid = am.grantor
12ORDER BY g.rolname, r.rolname;
On PostgreSQL 15 and earlier, inherit_option and set_option do not exist; drop those two columns and the rest runs unchanged.
Login roles that can read one table:
1SELECT rolname
2FROM pg_catalog.pg_roles
3WHERE rolcanlogin
4 AND has_table_privilege(rolname, 'public.orders', 'SELECT')
5ORDER BY rolname;
Superusers and replication roles:
1SELECT rolname, rolsuper, rolreplication
2FROM pg_catalog.pg_roles
3WHERE rolsuper OR rolreplication
4ORDER BY rolname;
Notes: pg_roles works on all supported versions. Replace public.orders with a table in the database you are connected to. The first query hides the predefined pg_ roles; remove the WHERE clause to see them.
Code Breakdown
The queries read three catalog objects and one function family.
pg_roles
pg_roles is a publicly readable view of pg_authid with the password field blanked out. It is safe to query as any role, and it carries an oid column for joins to pg_auth_members. The pg_authid catalog itself holds the password hash and is not publicly readable.
Attribute columns
rolsuper— true for a superuser, who bypasses all permission checks except the right to log in.rolcreaterole— the role can create more roles. In PostgreSQL 16 and later it can alter and drop only roles it holds withADMIN OPTION, which it receives automatically for roles it creates.rolcreatedb— the role can create databases.rolcanlogin— the role can be the initial session identity; false marks a group role.rolreplication— the role can initiate replication connections and create and drop replication slots.rolbypassrls— the role bypasses every row-level security policy.rolconnlimit— maximum concurrent connections for this role;-1means no limit.rolvaliduntil— password expiry time, used only for password authentication; null means no expiry.
pg_auth_members
Each row says that member belongs to roleid. The grantor column names the role that made the grant. From PostgreSQL 16 the row also records inherit_option (the member automatically uses the group's privileges) and set_option (the member can run SET ROLE to the group). admin_option means the member can grant the membership to others.
has_table_privilege
has_table_privilege(role, table, privilege) answers the question the documentation phrases as "Does user have privilege for table?" The privilege can be SELECT, INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER or, on recent versions, MAINTAIN. The function page does not say how it treats membership, so for a role that gets access through a group, confirm the result by comparing it with pg_has_role and the inherit_option of the membership.
Key Role Concepts
Attributes are not inherited
Role attributes such as CREATEDB are not passed on by membership. A member of a role that has CREATEDB cannot create databases, even when the membership has the inherit option. The member can do so only after SET ROLE to that role, when the membership allows it. Privileges on objects behave differently: they are inherited.
Inheritance changed in 16
Before PostgreSQL 16, inheritance was an attribute of the member role: NOINHERIT applied to all of its memberships. From 16 it is decided per grant, with GRANT ... WITH INHERIT TRUE or FALSE, and the role attribute only sets the default for new grants. The inherit_option column reflects the per-grant value, so audits on 16 should read it rather than rolinherit.
Predefined roles
Roles named pg_... are built in and carry specific powers. pg_signal_backend arrived in PostgreSQL 9.6. pg_monitor, pg_read_all_settings, pg_read_all_stats and pg_stat_scan_tables came in PostgreSQL 10. pg_read_all_data and pg_write_all_data came in 14, and pg_use_reserved_connections in 16. The file-access roles pg_read_server_files, pg_write_server_files and pg_execute_server_program can be used to reach superuser-level access, so treat membership in them like superuser.
The bootstrap superuser
Every cluster has one superuser created by initdb. It takes the name of the operating system user that ran initdb unless another name is given, and is often called postgres.
Practical Applications
Superuser audit
Run the superuser query on a schedule and compare it with the approved list. Any new name is a finding. To demote an account that no longer needs the power:
1ALTER ROLE report_user NOSUPERUSER;
Building group roles
Grant privileges to a group role and add users to it, so a new hire is one statement:
1CREATE ROLE readonly NOLOGIN;
2GRANT USAGE ON SCHEMA public TO readonly;
3GRANT SELECT ON ALL TABLES IN SCHEMA public TO readonly;
4GRANT readonly TO alice;
GRANT ... ON ALL TABLES names the tables in the schema, and it also covers views and foreign tables. For tables created later, set a default:
1ALTER DEFAULT PRIVILEGES FOR ROLE app_owner IN SCHEMA public
2 GRANT SELECT ON TABLES TO readonly;
Default privileges apply to objects created by the named role (or the current role if FOR ROLE is omitted), so name the role that creates the tables. They do not change privileges on objects that already exist. On PostgreSQL 14 and later, granting pg_read_all_data is a shorter route when read access to everything is acceptable.
Who can read this table
The table-privilege query above is the check to run before a data-protection review. Superusers pass every check, so they appear in the result whatever the grants say. Adding AND NOT rolsuper removes them, but the remaining rows are not only roles with an explicit grant: they also include roles that reach the table through group membership or through a predefined role such as pg_read_all_data.
Database-level rights
1SELECT datname, datacl, datconnlimit
2FROM pg_database
3ORDER BY datname;
datacl holds the access privileges on each database and datconnlimit its connection limit (-1 means no limit). An empty privileges entry means the object has default privileges, which always include all privileges for the owner.
Version Compatibility
pg_roles and the rol... columns listed are stable across supported versions. PostgreSQL 16 is the line that matters for audits: membership gained the inherit_option and set_option columns, CREATEROLE was restricted to roles the creator administers through ADMIN OPTION, and the inheritance default moved from the role to the grant.
On 16 and later a CREATEROLE role can alter and drop only roles it holds ADMIN OPTION on, which by default means the roles it created. It cannot create REPLICATION users or grant or revoke the REPLICATION or BYPASSRLS privilege, and it has no power over superusers. On 15 and earlier, CREATEROLE could change many aspects of any non-superuser role. The predefined roles listed above set the minimum version for each grant.
Best Practices
- Keep superusers few — list them on a schedule and demote any that are not required.
- Own and use are separate — the role that owns tables should not be the role applications log in with, because an owner can grant itself everything on its own objects.
- Use group roles — grant to groups and add members, and set default privileges for future tables.
- Prefer predefined roles —
pg_monitorandpg_read_all_dataare narrower than superuser for monitoring and read access. - Test absence of access — loop
has_table_privilegeover login roles to check that a role cannot do what it should not. - Set expiry deliberately —
rolvaliduntilends password logins only; it does not affect certificate or other non-password methods.
Related books recommended
Links go to Amazon. As an Amazon Associate, Postgres Scripts earns from qualifying purchases; your price is unchanged.
- Book: PostgreSQL: Up and Running, by Regina Obe & Leo Hsu. The practical PostgreSQL reference. (View on Amazon)
- Book: The Art of PostgreSQL, by Dimitri Fontaine. Writing better SQL for PostgreSQL. (View on Amazon)
- Book: PostgreSQL Administration Cookbook. Day-to-day DBA administration reference. (View on Amazon)
References
- PostgreSQL: Database Roles — Overview of roles, attributes, membership and predefined roles.
- PostgreSQL: pg_roles — Columns of the role view and its relation to
pg_authid. - PostgreSQL: pg_auth_members — Membership catalog with
admin_option,inherit_optionandset_option. - PostgreSQL: Role Attributes — What each attribute grants, including the limits of
CREATEROLE. - PostgreSQL: Predefined Roles — Built-in
pg_roles and the access each carries. - PostgreSQL 16 Release Notes — Role inheritance,
CREATEROLEand reserved-connection changes. - PostgreSQL: Privileges — Privilege types, default privileges and how access privilege entries are read.
- PostgreSQL: pg_database — Database catalog with the
dataclanddatconnlimitcolumns. - EDB: Auditing Users and Roles in PostgreSQL — Checking which roles hold a privilege on a table with
has_table_privilege, and combining privileges across inherited roles withpg_has_role.
Posts in this series
- How Many Connections Can Your PostgreSQL Database Handle?
- PostgreSQL Backend Connections via pg_stat_database
- pg_blocking_pids — Find Blocking Queries in PostgreSQL
- List PostgreSQL Databases by Size with Access Check
- Assess PostgreSQL Database Sizes Quickly and Easily
- Unveiling Your PostgreSQL Server - A Diagnostic Powerhouse
- Keep Your PostgreSQL Database Clean, Identify Idle Connections
- Query the PostgreSQL Configuration
- pg_is_in_recovery — Monitor PostgreSQL Standby Status
- ALTER SEQUENCE RESTART WITH in PostgreSQL — Examples
- Monitor Running Queries in PostgreSQL using pg_stat_activity
- Monitor PostgreSQL Active Sessions with pg_stat_activity
- PostgreSQL Error Handling Settings via pg_settings
- PostgreSQL File Location Settings Query via pg_settings
- Inspect PostgreSQL Lock Management Settings with pg_settings
- PostgreSQL Logging Configuration Query via pg_settings
- Monitor PostgreSQL Memory Settings with pg_settings
- PostgreSQL Table Row Count Estimates with SQL
- List PostgreSQL Tables by Size with SQL
- PostgreSQL WAL Settings Query Guide
- log_parser_stats, log_planner_stats, log_executor_stats — PostgreSQL
- PostgreSQL SSL Settings Query Guide
- PostgreSQL Resource Settings Query Guide
- PostgreSQL Replication Settings Query Guide
- PostgreSQL Query Planning Settings Query Guide
- PostgreSQL Preset Options Settings Query Guide
- PostgreSQL Miscellaneous Settings Query Guide
- Count PostgreSQL Sessions by State with SQL
- Kill Idle PostgreSQL Sessions with SQL
- GRANT SELECT on All Tables in PostgreSQL — with Examples
- pg_stat_user_tables — Find Insert-Only Tables in PostgreSQL
- Detect Soft Delete Patterns in PostgreSQL
- List PostgreSQL Object Comments with SQL
- List Foreign Key Constraints in PostgreSQL
- List PostgreSQL Enum Types and Their Values with SQL
- List All Views in a PostgreSQL Database with SQL
- Find PostgreSQL Tables Without a Primary Key
- List PostgreSQL Partitioned Tables with SQL
- List All Schemas in Your PostgreSQL Database
- pg_stat_database — Query PostgreSQL Database Statistics
- List PostgreSQL Roles and Their Privileges
- Scrubbing Email PII in PostgreSQL for GDPR Compliance
- List Installed Extensions in PostgreSQL
- List PostgreSQL Collations with pg_collation and ICU
- PostgreSQL Replica Identity for Logical Replication
- Monitor PostgreSQL Vacuum Progress with pg_stat_progress_vacuum
- Monitor PostgreSQL Wait Events Using pg_stat_activity
- Monitor PostgreSQL Replication Lag with pg_stat_replication
- List PostgreSQL Wait Events with the pg_wait_events View
- PostgreSQL Column-Level Permissions Audit Query
- List All PostgreSQL Triggers with Their State
- timestamptz and tzdata: Avoid Shifted PostgreSQL Timestamps
- Inspect PostgreSQL Sequences with the pg_sequences View
- List PostgreSQL Functions with pg_proc
- Query PostgreSQL Tablespace Info with pg_tablespace
- Audit PostgreSQL Authentication with pg_hba_file_rules
- List PostgreSQL Materialized Views with pg_matviews
- Audit Row-Level Security Policies with pg_policies