Managing PostgreSQL Users
Quick Start
Pigsty manages users declaratively.
First define users in the inventory.
Then run bin/pgsql-user <cls> <username> to create or modify them.
For the complete user definition reference, see User Configuration. See Access Control for roles and privileges, and Authentication for credential management.
pgsql-user.yml uses name to find a user definition.
The playbook does not rename roles.
To rename a role, create a replacement role.
Migrate ownership, memberships, and client credentials, then validate the change before you remove the old role.
Deletion followed by creation is not a lossless rename.
| Action | Command | Description |
|---|---|---|
| Create User | bin/pgsql-user <cls> <user> | Create new business user or role |
| Modify User | bin/pgsql-user <cls> <user> | Modify existing user properties |
| Delete User | bin/pgsql-user <cls> <user> | Dependency-aware destructive deletion (state: absent) |
Create User
During PostgreSQL cluster creation, the pg_user task automatically creates users defined in pg_users.
To create a user on an existing cluster, add its definition to all.children.<cls>.pg_users.
Then run:
Example: Create business user dbuser_app
Result: Pigsty creates dbuser_app on the primary, sets its password, and grants it the dbrole_readwrite role.
It adds the user to the Pgbouncer pool and reloads Pgbouncer configuration on all instances.
For manual user creation, you must ensure Pgbouncer user list sync yourself.
Modify User
Use the same command as for creation. The playbook is idempotent. If the target user exists, Pigsty changes its properties to match the configuration.
Not directly mutable: name is the identity key in the declarative definition.
The playbook does not rename an existing role.
Create a replacement role, then migrate ownership, privileges, and clients.
Validate the change before you remove the old role.
All other properties can be modified. Common examples:
Modify password: Change the password field.
Pigsty temporarily disables logging during the change to prevent password disclosure.
Modify privilege attributes: Configure boolean flags for user privileges.
Modify expiration: Use expire_in for expiration after N days, or expire_at for a fixed date.
expire_in takes priority.
Each playbook run recalculates its expiration date.
Use it for temporary users that need periodic renewal.
Modify role membership: Use the roles array with simple or extended definitions.
Membership changes are additive: Pigsty keeps existing memberships that the array does not declare.
To revoke a membership explicitly, use state: absent.
Manage user parameters: Use the parameters dictionary for user-level parameters.
Pigsty generates ALTER USER ... SET commands.
Use DEFAULT to reset a parameter.
Connection pool config: Set pgbouncer: true to add the user to the pool.
You can also set pool_mode and pool_connlimit.
Delete User
User deletion terminates sessions, transfers object ownership, revokes grants, and runs DROP ROLE.
This operation is irreversible.
Confirm the exact cluster, role, successor owner, and a recent backup.
Then set the user to state: absent and apply the change.
Config example:
Deletion process: On the primary, the task runs pg-drop-role <user> postgres --force.
It disables login and terminates active sessions.
It transfers database and tablespace ownership, and objects in each connectable database, to postgres.
It runs DROP OWNED to remove grants, revokes role memberships, then runs DROP ROLE.
Before changes, it writes an audit snapshot to /tmp/pg_drop_role_<user>_<timestamp>.log.
Protection: The Ansible task skips postgres and the replication, administration, and monitoring usernames configured in the inventory.
Direct calls to pg-drop-role protect only the fixed default names: postgres, replicator, dbuser_dba, and dbuser_monitor.
The script does not recognize renamed system accounts automatically.
If REASSIGN OWNED fails in a database, pg-drop-role skips DROP OWNED in that database.
However, the procedure across databases is not one transaction.
A failure partway through can leave the role NOLOGIN, transfer only some ownership, or leave dependencies.
The v4.5 Ansible task also uses ignore_errors.
Thus, a successful playbook result does not prove complete deletion.
Afterward, verify role absence, successor ownership, application migration, and the audit log.
In v4.5, pgsql-user.yml reloads Pgbouncer.
However, it does not reliably remove a deleted role from /etc/pgbouncer/userlist.txt.
Check every cluster instance after deletion:
If the exact Pgbouncer entry remains, remove only that line through the change control procedure. Reload Pgbouncer and validate application connections. Do not delete entries with a broad pattern.
Manual Deletion
For manual user deletion, use pg-drop-role script directly:
Common Use Cases
Common user configuration examples:
Basic business user
Read-only user
Admin user (can execute DDL)
Temp user (expires in 30 days)
Role (no login, for permission grouping)
User with advanced role options (PG16+)
Query Users
Common SQL queries for user info:
List all users
View user role membership
View user-level parameters
View expiring users
Connection Pool Management
Connection pool params in user definitions are applied to Pgbouncer when creating/modifying users.
Pigsty adds users with pgbouncer: true to /etc/pgbouncer/userlist.txt.
It sets user-level pool_mode and pool_connlimit in /etc/pgbouncer/useropts.txt.
As the postgres operating system user, use the pgb alias to access the Pgbouncer administration database.
For more operations, see Pgbouncer Management.
Manage Default-User Passwords
For a business user, follow Modify User.
Save the new password in its pg_users definition, preview the playbook for that user, then apply it.
The three default users need extra coordination because other services use their credentials.
| Parameter | Default | Role | Consumers |
|---|---|---|---|
pg_admin_password | DBUser.DBA | dbuser_dba | Admin clients, Pgbouncer, Infra service files, pgAdmin |
pg_monitor_password | DBUser.Monitor | dbuser_monitor | Exporters, Pgbouncer, Grafana data sources |
pg_replication_password | DBUser.Replicator | replicator | Patroni replication and .pgpass files |
These accounts belong to pg_default_roles, not pg_users.
pgsql-user.yml searches only pg_users.
Do not change a default password by overriding pg_users on the command line.
This changes the business-user list for that run and exposes plaintext passwords in shell history.
Rotate one account at a time:
- Save the new parameter in
pigsty.ymlor the active inventory. Never put the plaintext password on the command line. - On the current primary, open interactive
psqlas a superuser. Run\password <username>to enter the secret interactively. - Verify the
-lcluster or node scope, then run the corresponding refresh playbooks below. - Keep the current administration session open. Verify direct PostgreSQL, Pgbouncer, replication, exporters, and Grafana data sources before you change another account.
Refresh every consumer for the account. Replace <cls> and constrain infra to the actual targets:
If replication credentials differ between the role and Patroni nodes, new replication connections fail. Change this credential during a maintenance window and validate it promptly. VIBE or another module may have generated an administration connection string in its workspace context. If so, generate that module’s files again.
In v4.5, env_pgpass adds the new line with lineinfile; it does not remove older lines by username. Because libpq uses the first matching line, inspect every target Infra node after the refresh and remove obsolete entries through controlled editing without printing secrets:
patroni_password protects the Patroni REST API.
It is not a PostgreSQL role password.
After you change it in the inventory, refresh the target PostgreSQL cluster and the Infra administration configuration separately:
Then validate authentication and cluster state with patronictl or pig pg list <cls>.
Was this page helpful?
Thanks—your feedback helps us improve this page.
What got in the way? (optional)