See https://www.postgresql.org/docs/current/user-manag.html
PostgreSQL manages database access permissions using the concept of roles.
Roles englobes the concepts of “users” and “groups”.
In PostgreSQL versions before 8.1, users and groups were distinct kinds of entities, but now there are only roles.
Simply put:
A user is a role that can connect to the database. The role is defined as having
LOGINcapability
You create a new role with:
CREATE ROLE <name>;
To determine existing roles, examine the pg_roles table:
SELECT rolname FROM pg_roles;
And to find only the roles that can login i.e the users:
SELECT rolname FROM pg_roles WHERE rolcanlogin;
On my system this returns
SELECT * FROM pg_roles WHERE rolcanlogin;
-[ RECORD 1 ]--+---------
rolname | alexis
rolsuper | t
rolinherit | t
rolcreaterole | t
rolcreatedb | t
rolcanlogin | t
rolreplication | t
rolconnlimit | -1
rolpassword | ********
rolvaliduntil | [null]
rolbypassrls | t
rolconfig | [null]
oid | 10
-[ RECORD 2 ]--+---------
rolname | spock
rolsuper | f
rolinherit | t
rolcreaterole | f
rolcreatedb | f
rolcanlogin | t
rolreplication | f
rolconnlimit | -1
rolpassword | ********
rolvaliduntil | [null]
rolbypassrls | f
rolconfig | [null]
oid | 82244
The role/user alexis can do anything. it is the superuser!
Whole spock is not allowed to do much.
\du shows a more readable version of the table:
List of roles
Role name | Attributes
-----------+------------------------------------------------------------
alexis | Superuser, Create role, Create DB, Replication, Bypass RLS
spock |
In order to bootstrap the database system, a freshly initialized system always contains one predefined login-capable role.
This role is always a “superuser”, and it will have the same name as the operating system user that initialized the database cluster with initdb. This role is often named postgres.
Note: On macOS, it's designed to run under your user account rather than a dedicated system user. Homebrew installs are generally meant to be single-user focused, avoiding system-wide installations that require root privileges. Which explains why we don't see a postgres user or role in the table
In all cases (Mac, linux or windows), in order to create more roles you first have to connect as this initial role.
Only roles that have the LOGIN attribute can be used as the initial role name for a database connection.
These 2 statements are equivalent.
CREATE ROLE <name> LOGIN;
CREATE USER <name>;
The statement create role <name is equivalent to
CREATE ROLE new_role WITH
NOSUPERUSER
NOCREATEDB
NOCREATEROLE
NOINHERIT
NOLOGIN
NOREPLICATION
CONNECTION LIMIT -1;
The role is not superuser, can't create databases, or roles, can't login and inherit or do replication. But it has no restrictions on the number of connections.
no restrictions on the number of connections ? but it cannot connect!?
Right but a user that could connect can be given, can be associated with this role. In that case the new_role will not limit the number of connections of the user.
In a mixture of roles it is always the most restrictive permission that takes precedence.
These privileges are automatically granted (more in schemas in a mn, hold on!)
GRANT USAGE ON SCHEMA public TO PUBLIC;
GRANT CREATE ON SCHEMA public TO PUBLIC; -- Note: many DBAs revoke this in production
and New roles can:
But new roles CANNOT by default:
publicTo allow role to log in
ALTER ROLE new_role LOGIN;
To allow role to create databases
ALTER ROLE new_role CREATEDB;
To allow role to access specific database
GRANT CONNECT ON DATABASE your_database TO new_role;
To allow role to use a specific schema
GRANT USAGE ON SCHEMA schema_name TO new_role;
For a more secure setup
Revoke public schema creation from PUBLIC (common security practice)
REVOKE CREATE ON SCHEMA public FROM PUBLIC;
``
Revoke public EXECUTE permission on functions (if needed)
```sql
REVOKE EXECUTE ON ALL FUNCTIONS IN SCHEMA public FROM PUBLIC;
Grant specific permissions as needed
GRANT USAGE ON SCHEMA app_schema TO new_role;
GRANT SELECT ON ALL TABLES IN SCHEMA app_schema TO new_role;
Always start by creating a role with minimal required privileges
CREATE ROLE app_user;
GRANT CONNECT ON DATABASE app_db TO app_user;
GRANT USAGE ON SCHEMA app_schema TO app_user;
GRANT SELECT, INSERT, UPDATE ON ALL TABLES IN SCHEMA app_schema TO app_user;
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA app_schema TO app_user;
For future tables:
ALTER DEFAULT PRIVILEGES IN SCHEMA app_schema GRANT SELECT, INSERT, UPDATE ON TABLES TO app_user;
ALTER DEFAULT PRIVILEGES IN SCHEMA app_schema GRANT USAGE, SELECT ON SEQUENCES TO app_user;

superuser status: A database superuser bypasses all permission checks, except the right to log in. This is a dangerous privilege and should not be used carelessly; it is best to do most of your work as a role that is not a superuser. To create a new database superuser, use CREATE ROLE name SUPERUSER. You must do this as a role that is already a superuser.
database creation: A role must be explicitly given permission to create databases (except for superusers, since those bypass all permission checks). To create such a role, use CREATE ROLE name CREATEDB.
role creation: same with creating roles
specify the password: A password is only significant if the client authentication method requires the user to supply a password when connecting to the database. Specify a password upon role creation with CREATE ROLE name PASSWORD 'string'.
connection limit: Connection limit can specify how many concurrent connections a role can make. -1 (the default) means no limit. Specify connection limit upon role creation with CREATE ROLE name CONNECTION LIMIT 'integer'.
CREATE ROLE joe LOGIN; CREATE ROLE admin; CREATE ROLE wheel; CREATE ROLE island; GRANT admin TO joe WITH INHERIT TRUE; GRANT wheel TO admin WITH INHERIT FALSE; GRANT island TO joe WITH INHERIT TRUE, SET FALSE;
Immediately after connecting as role joe, a database session will have use of privileges granted directly to joe plus any privileges granted to admin and island, because joe “inherits” those privileges.
However, privileges granted to wheel are not available, because even though joe is indirectly a member of wheel, the membership is via admin which was granted using WITH INHERIT FALSE.
each object has an owner which has super powers on the object
-- Transfer ownership of specific tables ALTER TABLE trees OWNER TO trees_admin; ALTER TABLE maintenance_logs OWNER TO trees_admin;
https://www.postgresql.org/docs/current/predefined-roles.html
Predefined Roles
| Role | Allowed Access |
|---|---|
| pg_read_all_data | Read all data (tables, views, sequences), as if having SELECT rights on those objects, and USAGE rights on all schemas, even without having it explicitly. This role does not have the role attribute BYPASSRLS set. If RLS is being used, an administrator may wish to set BYPASSRLS on roles which this role is GRANTed to. |
| pg_write_all_data | Write all data (tables, views, sequences), as if having INSERT, UPDATE, and DELETE rights on those objects, and USAGE rights on all schemas, even without having it explicitly. This role does not have the role attribute BYPASSRLS set. If RLS is being used, an administrator may wish to set BYPASSRLS on roles which this role is GRANTed to. |
Create a basic user with login privilege
CREATE USER john WITH PASSWORD 'secure123';
Create a user with additional attributes
CREATE USER sarah WITH
PASSWORD 'secure456'
VALID UNTIL '2025-12-31'
CONNECTION LIMIT 5;
Create a superuser (be careful with this in production!)
CREATE USER admin_user WITH
SUPERUSER
CREATEDB
CREATEROLE
LOGIN
PASSWORD 'very_secure789';
Modify existing user attributes
ALTER USER john WITH CONNECTION LIMIT 2;
ALTER USER john VALID UNTIL '2024-12-31';
Set user-specific configurations
ALTER USER sarah SET search_path TO tree_inventory, public;
ALTER USER sarah SET timezone = 'Europe/Paris';
Schemas in PostgreSQL are like folders that help organize database objects (tables, views, functions, etc.).
Schemas are important to manage and secure access to important resources.
see https://neon.tech/postgresql/postgresql-administration/postgresql-schema
By default, objects go into the 'public' schema
CREATE TABLE trees (
id SERIAL PRIMARY KEY,
species VARCHAR(100),
height NUMERIC(5,2)
);
Create schemas for different aspects of tree management
CREATE SCHEMA tree_maintenance;
CREATE SCHEMA tree_statistics;
Then create tables in specific schemas:
CREATE TABLE tree_maintenance.inspections (
tree_id INTEGER REFERENCES public.trees(id),
inspection_date DATE
);
"$user", publicso your user can only see objects that are in
A superuser can of course access all schemas.
Check current search path
SHOW search_path;
-- Modify search path
SET search_path TO tree_maintenance, public;
-- Now you can write queries without schema qualification
SELECT * FROM inspections; -- looks in tree_maintenance first, then public
There are a few shemas besides the public one in your PostgreSQL server:
SELECT schema_name
FROM information_schema.schemata;
returns:
schema_name
--------------------
information_schema
pg_catalog
pg_toast
public
information_schema: Contains standardized views providing information about the database
information_schema are actually views that query data from pg_catalog.pg_catalog: Contains PostgreSQL-specific system tables and views
pg_toast: TOAST (The Oversized-Attribute Storage Technique) schema, handles storage of large field values. TOAST is PostgreSQL's strategy for handling large field values that won't fit into the standard page size (typically 8KB).
https://neon.tech/postgresql/postgresql-administration/postgresql-schema