Now-Next

SaaS architecture

role "…" does not exist in PostgreSQL, measured

PostgreSQL's role does not exist, measured on 17: psql as root in Docker, POSTGRES_USER on a reused volume, quoted names, GRANT, SET ROLE and pg_restore.

Chris van Eijk, Now-Next · · 7 min read

FATAL: role "root" does not exist (SQLSTATE 28000, invalid_authorization_specification) means the server has no role with exactly the name the client logged in with. The name need not be one you typed: psql takes your operating-system user name when you give no -U, and inside a Docker container that is root. The same text as an ERROR (42704) comes from a statement such as GRANT or ALTER … OWNER TO that names a role that is not there.

We measured on the official Docker images postgres:17 (PostgreSQL 17.11) and postgres:12 (12.22), with a psql 16.15 client on the host. The connection error’s SQLSTATE does not appear in psql’s output, so we read it from the server log with %e in log_line_prefix.

Why does psql say role “root” does not exist?

Because it connects as the user you are. In a container, docker exec runs as root:

-- docker exec -it c psql
-- psql: error: connection to server on socket "/var/run/postgresql/.s.PGSQL.5432" failed:
--   FATAL:  role "root" does not exist
-- server log: sqlstate=28000

Adding -d postgres did not help; the role is the problem, not the database. On PostgreSQL 12 the message was identical. docker exec -u postgres c psql connected as postgres to database postgres. The psql documentation states the rule: “The default database user name is your operating-system user name. Once the database user name is determined, it is used as the default database name.” PGUSER overrides it.

That second sentence is the next error. After we created a role chris (our host user name) and connected without naming a database, the role was fine and the database was not:

-- psql -h 127.0.0.1 -p 55501
-- FATAL:  database "chris" does not exist      (sqlstate 3D000)
-- psql -h 127.0.0.1 -p 55501 postgres  ->  current_user = chris, current_database = postgres

Why does it say password authentication failed instead?

From the host, through the published port, a missing role did not produce role does not exist. The official image’s pg_hba.conf ends in host all all all scram-sha-256, and for that line the client only got FATAL: password authentication failed for user "chris" (SQLSTATE 28P01). The real reason was only in the server log:

-- FATAL:  password authentication failed for user "chris"
-- DETAIL:  Role "chris" does not exist.
--   Connection matched file "/var/lib/postgresql/data/pg_hba.conf" line 128: "host all all all scram-sha-256"

Inside the container, over the Unix socket or 127.0.0.1 (both trust in that file), the same missing role gave role "…" does not exist. So whether you see this error or a password error depends on the authentication method, not on the role: docker logs tells you which it is. With an existing role and a wrong password, the client message was identical; only the missing Role "…" does not exist. DETAIL line in docker logs told the two apart. The other causes behind that message are in password authentication failed for user.

Why does role “postgres” not exist in my Docker container?

Because POSTGRES_USER replaces it. With POSTGRES_USER=app and no POSTGRES_DB, the only non-system role was app (a superuser), and the databases were postgres, app, template1 and template0. psql -U postgres then failed with FATAL: role "postgres" does not exist inside the container, and with password authentication failed for user "postgres" from the host.

The Docker Compose variant: the variables only act on an empty data directory. We recreated the container on the same named volume with POSTGRES_USER=newuser, POSTGRES_PASSWORD=pw2 and POSTGRES_DB=newdb. The log said PostgreSQL Database directory appears to contain a database; Skipping initialization, and:

-- docker exec c psql -U newuser
-- FATAL:  role "newuser" does not exist
-- roles: app only; databases: postgres, app, template1, template0 (no newdb)
-- psql -U app with the OLD password: works; with the new one: password authentication failed

Changing the variables in docker-compose.yml changed nothing in the database. Either create the role yourself (create role newuser login password '…') or, if the data is disposable, start on a new, empty volume: on a fresh volume with POSTGRES_USER=newuser and POSTGRES_DB=newdb, both were created. Pass -d newdb as well: without it psql looked for a database newuser and failed with database "newuser" does not exist.

Why does a role with capitals not exist?

SQL folds unquoted names to lower case; the -U option does not. We created "App" (quoted) and Mixed (unquoted, stored as mixed):

What you ranResult
psql -U Appconnected as App
psql -U appFATAL: role "app" does not exist
psql -U MixedFATAL: role "Mixed" does not exist
psql -U mixedconnected as mixed
psql -U '"App"'FATAL: role ""App"" does not exist
grant … to appERROR: 42704: role "app" does not exist
grant … to "App"GRANT

A name in a connection string is taken literally; a name in SQL needs double quotes if it has capitals. The same folding causes relation does not exist for tables.

Which statements give role does not exist as an ERROR?

All of these named a role nosuch that did not exist. PostgreSQL 12.22 gave the same codes.

StatementSQLSTATE
grant select on t to nosuch42704
alter table t owner to nosuch42704
alter role nosuch rename to x, drop role nosuch42704
select 'nosuch'::regrole, has_table_privilege('nosuch', …)42704
set role nosuch, set session authorization nosuch22023 (invalid_parameter_value)
drop role if exists nosuchNOTICE … skipping, no error

The SET ROLE row matters if you catch errors by code: the text is the same, the SQLSTATE is not. The other codes are listed in our measured SQLSTATE overview.

Why does pg_restore say role does not exist?

A dump records owners and grants by role name, and roles are not part of pg_dump. We dumped a table owned by tenant_owner with a GRANT SELECT … TO reporting and restored it into a server that had only app:

-- pg_restore -d dst1 src.dump
-- pg_restore: error: could not execute query: ERROR:  role "tenant_owner" does not exist
-- Command was: ALTER TABLE public.invoices OWNER TO tenant_owner;
-- pg_restore: error: could not execute query: ERROR:  role "reporting" does not exist
-- Command was: GRANT SELECT ON TABLE public.invoices TO reporting;
-- pg_restore: warning: errors ignored on restore: 2      (exit code 1)

The table and its row were restored anyway, owned by app; only the ownership and the grant were lost. With --no-owner --no-privileges the restore was silent and exited 0; with only --no-owner the GRANT error remained. With -1 (single transaction) the first error rolled everything back: no table at all. If the roles should exist on the target, pg_dumpall --roles-only on the source wrote the CREATE ROLE statements, including the NOLOGIN role reporting. Restoring a single tenant has its own catches, which we covered in pg_dump of one tenant.

How do you see which roles exist?

\du in psql, or a query on pg_roles. In the container started with POSTGRES_USER=app:

\du
--  Role name |                         Attributes
--  app       | Superuser, Create role, Create DB, Replication, Bypass RLS

select rolname, rolcanlogin from pg_roles where rolname !~ '^pg_';
--  app | t

\du hides the 15 predefined pg_* roles that PostgreSQL 17.11 had; the query above filters them out too. Check rolcanlogin: a role that exists but cannot log in gave a different error, FATAL: role "nologin_r" is not permitted to log in. Once the role exists, the next error can be about its rights — see permission denied for schema public.

What we did not measure

Windows installers and their default postgres user, Homebrew or distribution packages, managed databases, pg_ident.conf user-name maps, and LDAP or peer authentication. We did not measure psql clients other than 16.15, nor --role on pg_restore.

Sources: PostgreSQL 17 documentation on psql (default user and database name), read on 22 October 2026. All measurements are our own, on PostgreSQL 17.11 and 12.22 in the official Docker images, on 22 October 2026.

Let's talk

What are we building?

One conversation is enough to know whether we fit. Tell us what you have in mind — we will tell you how fast it can happen, and whether we are the right people for it.

Start the conversation