Course topics

By WebNest Studio Engineering Team

PostgreSQL Tutorial

Roles and Permissions in PostgreSQL

PostgreSQL uses a single unified concept called a "role" to represent both users and groups — there is no separate USER object distinct from ROLE. A role can log in (acting as a user), can be granted to other roles (acting as a group), or both. Managing roles and the privileges granted to them is how PostgreSQL controls who can do what to which objects.

Why it matters

Every connection to PostgreSQL acts as a role, and what that role may do is the main safeguard for your data. An application that connects as a superuser turns any bug or injection flaw into total loss. Granting each role only what it needs is one of the most effective security measures there is.

Creating Roles

CREATE ROLE role_name LOGIN PASSWORD 'secret'; creates a role that can connect to the database (the LOGIN attribute is what distinguishes a "user-like" role from a pure group). CREATE ROLE app_readers; without LOGIN creates a group-like role meant only to hold and distribute a set of privileges. Other useful attributes include SUPERUSER, CREATEDB, and CREATEROLE, granted sparingly.

GRANT and REVOKE

Privileges on specific objects are given with GRANT privilege ON object TO role; — for example, GRANT SELECT, INSERT ON orders TO app_writer;. REVOKE removes a previously granted privilege using the same shape: REVOKE INSERT ON orders FROM app_writer;. Common object-level privileges include SELECT, INSERT, UPDATE, DELETE for tables, and USAGE for schemas and sequences (needed alongside table grants for a role to actually reach objects inside a non-public schema or use identity/sequence-backed columns).

Role Inheritance and Group Membership

A role can be granted membership in another role with GRANT group_role TO member_role;. By default, a member role automatically inherits (uses) the privileges of any role it belongs to, without needing to explicitly switch — this is controlled by the INHERIT/NOINHERIT attribute. This makes it practical to define privilege sets once on a group role (like app_readers with SELECT on all app tables) and simply add or remove individual login roles from that group as staff change, rather than re-granting privileges one by one.

Example

Creating a group role, a login role, and granting privileges through membership

SQL
CREATE ROLE app_readers;
GRANT USAGE ON SCHEMA public TO app_readers;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO app_readers;

CREATE ROLE analyst LOGIN PASSWORD 'S3curePass!';
GRANT app_readers TO analyst;

-- 'analyst' can now SELECT from every table in public via inherited membership
Output
CREATE ROLE
GRANT
GRANT
CREATE ROLE
GRANT ROLE

Common Mistakes

  • Granting privileges directly to individual login roles one at a time instead of via a group role, making onboarding/offboarding staff error-prone.
  • Forgetting GRANT USAGE ON SCHEMA when granting table privileges in a non-public schema — without schema USAGE, table grants alone still leave the role unable to see the objects.
  • Creating a role with NOLOGIN and then being confused why a client cannot connect as that role — NOLOGIN roles are meant purely as privilege groups.
  • Over-granting SUPERUSER to application roles for convenience, bypassing all permission checks and defeating the purpose of fine-grained GRANTs.

Exercise

This exercise uses PostgreSQL roles, so it needs a PostgreSQL server. Create a group role readonly that cannot log in and may only read every table in the public schema. Then create a login role report_user with a password and make it a member of readonly, so that it receives those permissions through membership.

Expected result: connected as report_user, SELECT works on the tables of the public schema and INSERT, UPDATE and DELETE are refused.

SQL · starter code
-- TODO: group role readonly, without login

-- TODO: let readonly use the public schema and read all its tables

-- TODO: login role report_user with a password

-- TODO: make report_user a member of readonly
Show hints
  • A role needs USAGE on a schema before it can reach the tables inside it.
  • Membership is granted with GRANT readonly TO report_user.
Show solution
SQL · solution
CREATE ROLE readonly NOLOGIN;

GRANT USAGE ON SCHEMA public TO readonly;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO readonly;

CREATE ROLE report_user LOGIN PASSWORD 'use-a-long-random-password';

GRANT readonly TO report_user;

Quick quiz

1. In PostgreSQL, what is the difference between a user and a role?

  • A. They are stored in different databases
  • B. Users cannot own tables
  • C. Roles cannot have passwords
  • D. A user is simply a role that has the LOGIN attribute
Show answer

D. CREATE USER is the same as CREATE ROLE with LOGIN.

2. Which statement takes a permission away?

  • A. REVOKE
  • B. DENY
  • C. REMOVE
  • D. DROP
Show answer

A. REVOKE reverses a GRANT.

3. Does GRANT SELECT ON ALL TABLES IN SCHEMA public cover tables created afterwards?

  • A. Yes, automatically
  • B. No; it applies to the tables that exist now. ALTER DEFAULT PRIVILEGES covers future ones
  • C. Only for views
  • D. Only after a restart
Show answer

B. Forgetting this is a common reason a read-only account cannot see a new table.

Interview questions

How do you set up permissions for an application in PostgreSQL?

Show answer

Create group roles for each kind of access, such as read-only and read-write, and grant privileges on the schema and its tables to those groups. Create a separate login role for each application or person and make it a member of the appropriate group. The application's role should have only the rights it needs, never superuser or ownership of the schema. ALTER DEFAULT PRIVILEGES extends the grants to tables created later.

What is the principle of least privilege?

Show answer

Every account should have the minimum permissions needed for its task and no more. A reporting tool gets read-only access to the tables it reports on; a web application gets data access but no right to alter the schema. If an account is compromised, by a leaked password or an injection flaw, the attacker is limited to what that account could do, which contains the damage.

Key Points to Remember

  • PostgreSQL uses one unified "role" concept for both users (LOGIN roles) and groups.
  • GRANT and REVOKE control specific privileges (SELECT, INSERT, USAGE, etc.) on specific objects.
  • Role membership (GRANT role TO role) combined with INHERIT lets member roles automatically use a group's privileges.
  • Schema-level USAGE is required in addition to table-level grants for non-public schemas.

Practice in the playground

Write and run your own code for this topic, or start from the exercise or an example. Runs your SQL on SQLite in your browser. Standard SQL works; a few features that exist only in PostgreSQL or MySQL do not. Your code for each topic is kept in this browser.

Run the examples

Change an input, predict the result, then compare it with the output. Explain why the result changes.

These examples need a project environment on your computer, so they can be copied but not run here. Codelab runs Python, FastAPI backends with SQLite, plain Java 17 and HTML/CSS/JavaScript.

WebNest Studio

Written by

WebNest Studio Engineering Team

Practical lessons from developers working with Java, Python, React, databases and production systems.

Led by Vansh Duggal, Founder, WebNest Studio · Full-stack & software engineering