CLI quick start
On this page
Get up and running with pgroles in a few minutes.
Prerequisites
- PostgreSQL 16, 17, or 18 — the versions supported and tested in CI
- A disposable database named
mydb, with a table inpublicand an administrator connection for this exercise
Installation
Download the binary for your platform from the latest stable release, then verify pgroles --version. See installation for containers and Cargo.
Use pgroles generate --database-url ... > pgroles.yaml first, then refine the generated flat manifest into profiles and schema bindings.
Create a manifest
Before typing any YAML, here is the role graph the manifest below describes — a single login role with read-only access to one schema.
The role graph you are about to build
One login role, analytics, granted read-only access to the public schema in mydb.
Role
analytics
Analytics read-only role
is granted
Lets the role open a connection to the database.
Lets the role reach the objects that live inside the schema.
Read-only access to every table currently in the schema.
No membership edges yet
This manifest declares no memberships, so analytics holds every privilege directly. When you do add one, the edge runs member -> role: the member inherits the privileges of the role it is granted, never the other way around.
If schema USAGE, table SELECT, and membership still feel like separate pieces, start with The permission chain, then return here to apply the model.
Create a file called pgroles.yaml:
roles:
- name: analytics
login: true
comment: "Analytics read-only role"
grants:
- role: analytics
privileges: [CONNECT]
object: { type: database, name: mydb }
- role: analytics
privileges: [USAGE]
object: { type: schema, name: public }
- role: analytics
privileges: [SELECT]
object: { type: table, schema: public, name: "*" }
Validate the manifest
Check the manifest is valid without connecting to a database:
pgroles validate
Manifest is valid.
1 role(s) defined
3 grant(s) defined
0 default privilege(s) defined
0 membership(s) defined
Plan changes
See what SQL would be generated against a live database:
pgroles diff --mode additive --database-url postgres://localhost/mydb
This shows the exact SQL statements needed to converge the database to match your manifest.
The diff command (also available as plan) is read-only. It connects to your database to inspect the current state but does not execute any changes.
Apply changes
When you're happy with the plan, apply it:
pgroles apply --mode additive --database-url postgres://localhost/mydb
Or preview without executing:
pgroles apply --mode additive --database-url postgres://localhost/mydb --dry-run
Using environment variables
Instead of passing --database-url every time, set the DATABASE_URL environment variable:
export DATABASE_URL=postgres://localhost/mydb
pgroles diff --mode additive
pgroles apply --mode additive
Verify the result
Run the same additive diff again; it should print -- No changes needed. This proves convergence within additive mode, which leaves undeclared access untouched. Continue with staged adoption before enabling revocations on an existing database.