Skip to main content
By default Tracecat uses one PostgreSQL user for everything. With database roles, each part of Tracecat logs in as its own role, and the admin login (a superuser, or a CREATEROLE user such as the RDS master) stays with the person who manages the database. Setting up roles is provisioning: you run it as the admin, once per install and again before a release that changes roles. Migrations then run as migrator and never need the admin: once the roles are set up, the migrations container refuses any other login.

Set up roles

Run tracecat db roles upgrade as the admin, from the Tracecat image of the release you are about to deploy. It creates the roles, grants their access, and moves an existing install’s objects to tracecat_owner and app_owner. It is safe to rerun.
  • TRACECAT__DB_ADMIN_URI: the admin’s connection URI for the Tracecat database, for example postgresql://postgres:<password>@db.example.com:5432/tracecat.
  • ca.pem: the CA bundle that signed the database’s certificate, so verify-full can check it (for RDS, the global bundle).
  • --login: how app, migrator and agent_runtime log in. password sets the passwords from TRACECAT__DB_MIGRATOR_PASSWORD and TRACECAT__DB_APP_PASSWORD (and TRACECAT__DB_AGENT_RUNTIME_PASSWORD if set). aws_iam grants rds_iam instead, so the roles log in with IAM tokens; leave the password variables unset. azure_entra expects you to create migrator, app and agent_runtime first, each mapped to the identity that logs in as it with pgaadauth_create_principal_with_oid (see Database IAM login); leave the password variables unset.
Then give each part its login:
  1. Point the migrations at migrator: TRACECAT__DB_USER=migrator and TRACECAT__DB_PASS, or TRACECAT__DB_URI.
  2. Point every other service at app.
  3. Deploy.
On a new install, run tracecat db roles upgrade once more after the first deploy. It installs the guard that keeps row-level security on workspace tables, which needs the migrated schema and a superuser (rds_superuser on RDS). Until then, each migration run logs the dynamic-table RLS guard isn't installed. If the admin can’t run commands against the database (no CREATEROLE, or a locked-down managed service), print the SQL and run it yourself:

Before each release

Release notes say when a release changes database roles. Check before you deploy:
status exits with 1 when the database is behind. Run tracecat db roles upgrade from the new release’s image, then deploy. If you deploy first, migrations stop before changing anything:
Releases that only add tables don’t need this step: the migrations grant app and tracecat_readonly access to new tables themselves.

By deployment type

Docker Compose

Compose sets up roles for you, because the postgres superuser’s password is already in .env. Set these in .env, then restart:
On each start, the migrations container runs tracecat db roles upgrade as the admin, the migrations as migrator, then upgrade again (it installs the RLS guard once the schema exists). Until you change TRACECAT__DB_URI, the services keep logging in as the admin. Once the roles exist, move them to app with TRACECAT__DB_URI=postgresql+psycopg://app:${TRACECAT__DB_APP_PASSWORD}@postgres_db:5432/postgres, and keep the variables above set: the next start needs them for role setup.

Kubernetes

Run tracecat db roles upgrade yourself, from a machine or a one-off pod that can reach the database, with the admin’s credentials. Don’t store the admin’s credentials in the cluster. Then give the chart’s migrations Job the migrator login (migrations.migrator in the chart’s values, with a password Secret or aws_iam) and the services the app login (externalPostgres.auth.appSecret).

AWS Fargate

The Fargate stack runs Tracecat as one database user. Database roles aren’t available there yet.

Personal read-only access

Give people their own login and the tracecat_readonly role. As the admin: On RDS, with IAM login:
Elsewhere, with a password:
Then set her password in psql with \password alice, which hashes it on the client so the plaintext never reaches the server. tracecat_readonly can read every workspace’s data in public except credential columns such as tokens and password hashes. Row-level security still applies to it on workspace-scoped tables, so a session sees their rows only after it opts in: run SET app.rls_bypass = 'on'; first (or SET app.current_workspace_id = '<workspace-id>'; for one workspace). Tables without a policy, such as organization-wide ones like user, are readable without it. The role stays read-only either way.

Break-glass access

The admin login keeps full access: it is a member of tracecat_owner (or a superuser). Use it only for repairs, then rotate its password. Don’t hand it to a Tracecat service or the migrations.

Roll back

To return to a single database user, point every service and the migrations back at the admin login, then run this as the admin in the Tracecat database (replace <admin> with its name):
REASSIGN OWNED gives the tables back to the admin. Each DROP OWNED then only removes that role’s grants and default privileges; run them one role at a time, since one statement for several roles fails on their shared default privileges. DROP ROLE removes the roles, including people’s tracecat_readonly membership.