Fix: PostgreSQL FATAL: role "username" does not exist
Part of: Database Errors
Quick Answer
How to fix PostgreSQL FATAL role does not exist error caused by missing database roles, peer authentication, wrong usernames, and platform-specific installation defaults.
The Auth Layer Rejected You Before You Started
Personally, I rate this error as one of the friendliest in the PostgreSQL ecosystem once you understand the rule it is enforcing. The most common cause is also the simplest: PostgreSQL defaulted to your OS username for the role, and there is no role with that name. I learned to type sudo -u postgres psql before anything else on a fresh box. You try to connect to PostgreSQL and get:
psql: error: connection to server on socket "/var/run/postgresql/.s.PGSQL.5432" failed:
FATAL: role "ubuntu" does not existOr variations:
FATAL: role "root" does not existFATAL: role "myuser" does not existpsql: FATAL: role "postgres" does not existdjango.db.utils.OperationalError: FATAL: role "app_user" does not existPostgreSQL refused the connection because the database role (user) you are trying to authenticate as does not exist in the PostgreSQL server.
Quick Reference Before You Dive In
If you arrived here from Google with a fresh FATAL, the five facts that resolve roughly 90 percent of cases:
sudo -u postgres psqlis the first move on a fresh install. This switches your OS user topostgresso peer authentication matches the only role that exists after install. The PostgreSQL roles documentation and the authentication methods reference are the canonical sources.psqldefaults to your OS username for the role. If your shell user isubuntuand PostgreSQL only has apostgresrole,psqllooks for roleubuntuand fails. Pass-U postgres(or any explicit role) to override.peerauthentication maps OS user to PostgreSQL role name. On most Linux distributions the defaultpg_hba.confusespeerfor local connections. Either create a matching PostgreSQL role or changepeertoscram-sha-256and reload.- In Docker, the default role is set by
POSTGRES_USERat container creation. Once the data volume exists, you cannot change the default role by editing the env var; you must re-init the volume or runCREATE ROLEmanually. - PostgreSQL folds unquoted identifiers to LOWERCASE.
CREATE ROLE MyUseractually createsmyuser. Connecting as"MyUser"(with quotes) then fails. Always use lowercase role names unless you specifically need the quoted form everywhere.
The rest of this article walks through each cause in detail, plus the failure modes most other guides skip.
How PostgreSQL Maps a Connection to a Role
PostgreSQL uses roles for authentication. Every connection must specify a role name. When you run psql without specifying a user, PostgreSQL defaults to your operating system username. If your OS username is ubuntu, root, or john, PostgreSQL looks for a role with that name; if it does not exist, you get this error.
The single source of confusion behind this error is that PostgreSQL historically had two concepts (USER and GROUP) that were unified into a single ROLE concept in version 8.1 (Nov 2005). Today, CREATE USER is just an alias for CREATE ROLE ... WITH LOGIN, and CREATE GROUP is an alias for CREATE ROLE without the LOGIN attribute. The error message uses the modern term “role” no matter how the role was originally created. So a tutorial that says “create a PostgreSQL user named myapp” is functionally identical to “create a role with LOGIN named myapp”; both produce the same FATAL: role does not exist error if you forgot to run the statement.
The other source of confusion is the default authentication method. On a fresh install, the local socket entry in pg_hba.conf is set to peer on most distributions, which means PostgreSQL takes the operating-system username from the socket and looks for a database role with the same name. If your OS user is alice but the only role is postgres, peer auth fails with the role-does-not-exist message even though the role you “wanted” to connect as never appeared anywhere in your command. That is why the conventional first step is sudo -u postgres psql: it switches your OS user so peer auth matches.
Common causes:
- Running psql as your OS user without a matching role. Your OS user is
ubuntubut PostgreSQL only has thepostgresrole. - Fresh installation. PostgreSQL creates only the
postgressuperuser role by default. No other roles exist until you create them. - Wrong username in connection string. Your application’s
DATABASE_URLspecifies a role that was never created. - Docker container setup. The container only has the default role unless you configure
POSTGRES_USER. - Peer authentication. PostgreSQL is configured to match the OS username to the database role name, and they don’t match.
- Case-folding. PostgreSQL lower-cases unquoted identifiers, so
CREATE ROLE MyUseractually createsmyuser. If you then connect as"MyUser"(with quotes), you get role-does-not-exist for the quoted, case-preserved name.
Version History That Changes the Failure Mode
Authentication defaults and role mechanics have shifted across PostgreSQL releases and across distributions packaging PostgreSQL. The same command produces different errors depending on which version you are talking to:
- PostgreSQL 8.1 (Nov 2005):unified role model. Before 8.1,
pg_userandpg_groupwere separate catalogs. After 8.1, both are views overpg_roles.CREATE USERandCREATE GROUPremain as legacy aliases. Any tutorial older than 2005 may give incorrect mental models. - PostgreSQL 8.4 (Jul 2009):
peerauthentication method. Before 8.4, the local socket usedident(over a special socket-level Unix mechanism). 8.4 introducedpeer, which became the default forlocalentries on most Linux distributions. This is the moment most “role does not exist on Ubuntu” tutorials really refer to. - PostgreSQL 10 (Oct 2017):
scram-sha-256authentication added. SCRAM is a stronger password hash than MD5. 10 added support, but defaults stayed onmd5until version 14. - PostgreSQL 14 (Sep 2021):
scram-sha-256is the default ininitdb. Runinitdbon 14+ and thepg_hba.confentries for password authentication usescram-sha-256. If you upgrade a 13 cluster’s data directory in place, the oldmd5entries remain. Connecting with a client that only knows MD5 (very old libpq) against a 14+ role created with SCRAM produces password-mismatch errors that sometimes get conflated with role-does-not-exist. Use--auth-host=scram-sha-256and--auth-local=scram-sha-256explicitly when re-initdb’ing to make the choice visible. - PostgreSQL 16 (Sep 2023):regex matching in
pg_hba.confandpg_ident.conf. Entries can now use/regex/to match role names. This lets you map a single regex of OS usernames onto multiple database roles, which simplifies multi-tenant setups but makes “why was I matched to this role?” harder to debug. UseSELECT * FROM pg_hba_file_rules;andpg_ident_file_mappings(also new in 16) to see the parsed view. - Distribution differences:Debian/Ubuntu vs RHEL/CentOS. Debian-family packages run
initdbwithlocal all postgres peerpluslocal all all peer, so peer is the default for all local users. RHEL-family packages historically usedident sameuser. Homebrew on macOS creates a role matching$(whoami)rather thanpostgres, which trips up scripts that hard-codesudo -u postgres psql. INHERITattribute evolution. Role membership was always inheritable by default (CREATE ROLE ... INHERIT). In 16,NOINHERITbecame a more useful pattern with the introduction of explicitSET ROLErequirements and theINHERIT FALSEmembership option. If you “added a role to a group” and the new role still cannot do what the group can, check whetherINHERITis set on the membership.
When to Use Which Fix
The next eight sections cover the fixes in detail. The table below maps your situation to the recommended fix.
| Your situation | Recommended fix | Why |
|---|---|---|
| Fresh PostgreSQL install, no role for you | Fix 1: sudo -u postgres psql, then CREATE ROLE | Bootstrap from the superuser |
Want psql to work without flags | Fix 2: create role matching whoami | Peer auth lines up |
| App connection string fails | Fix 3: verify role + password + db via \du and \l | Each piece must exist |
| Need to change auth method | Fix 4: edit pg_hba.conf, reload | peer vs scram-sha-256 |
| Docker container with wrong default role | Fix 5: re-init volume or CREATE ROLE inside | Env var is set-once |
Homebrew on macOS missing postgres role | Fix 6: createuser --superuser postgres | Homebrew uses whoami |
| Role exists but cannot see tables | Fix 7: grant on schema, default privileges | relation does not exist follows |
| Want to inspect / rename / drop roles | Fix 8: \du, ALTER ROLE, REASSIGN OWNED | Standard maintenance |
If multiple rows apply, pick the topmost one.
Fix 1: Connect as the postgres Superuser First
On most installations, the only role that exists initially is postgres. Connect as that user first:
sudo -u postgres psqlThis switches to the postgres OS user and runs psql, which connects as the postgres role via peer authentication.
Once connected, you can create the role you need:
CREATE ROLE myuser WITH LOGIN PASSWORD 'mypassword';Or with additional privileges:
CREATE ROLE myuser WITH LOGIN PASSWORD 'mypassword' CREATEDB;Then create a database for the user:
CREATE DATABASE mydb OWNER myuser;Exit with \q and connect as the new user:
psql -U myuser -d mydb -h localhostA flag I add reflexively when connecting on a new machine: -h localhost. This forces TCP / IP and password authentication, bypassing peer auth entirely. Without it, psql tries the Unix socket first and peer auth fires (which is where most “role does not exist” reports actually come from). One extra flag, hours saved.
Fix 2: Create a Role Matching Your OS Username
If you want psql to work without specifying -U, create a role that matches your OS username:
# Check your OS username
whoami
# Output: ubuntu
# Create a matching role
sudo -u postgres createuser --interactive ubuntuThe --interactive flag prompts you for options:
Shall the new role be a superuser? (y/n) n
Shall the new role be allowed to create databases? (y/n) y
Shall the new role be allowed to create more new roles? (y/n) nThen create a database with the same name:
sudo -u postgres createdb ubuntuNow psql works without any flags:
psqlPostgreSQL defaults to your OS username for both the role and the database.
Fix 3: Fix the Connection String
If the error comes from an application, check the database connection string:
postgresql://myuser:mypassword@localhost:5432/mydbVerify that:
- The role
myuserexists: run\duin psql to list all roles. - The password is correct: reset it with
ALTER ROLE myuser WITH PASSWORD 'newpassword';. - The database
mydbexists: run\lin psql to list all databases.
Django settings.py:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'mydb',
'USER': 'myuser', # This role must exist
'PASSWORD': 'mypassword',
'HOST': 'localhost',
'PORT': '5432',
}
}Rails database.yml:
default: &default
adapter: postgresql
username: myuser # This role must exist
password: mypassword
host: localhostIf the connection itself fails before authentication, see Fix: PostgreSQL connection refused.
Fix 4: Fix pg_hba.conf Authentication
PostgreSQL’s pg_hba.conf file controls who can connect and how they authenticate. Common authentication methods:
| Method | How it works |
|---|---|
peer | OS username must match database role name |
md5 | Password authentication (hashed) |
scram-sha-256 | Password authentication (more secure) |
trust | No authentication (dangerous) |
ident | Like peer, but over TCP |
Find your pg_hba.conf:
sudo -u postgres psql -c "SHOW hba_file;"Common location: /etc/postgresql/16/main/pg_hba.conf
The peer authentication issue:
If pg_hba.conf has:
local all all peerThen every local connection requires the OS username to match the PostgreSQL role name. Either create a matching role (Fix 2) or change peer to md5 or scram-sha-256:
local all all scram-sha-256Reload PostgreSQL after editing:
sudo systemctl reload postgresqlA specific gotcha I have walked into: editing pg_hba.conf and assuming changes are live. They are not. You must systemctl reload postgresql (or pg_ctl reload) before the new entries take effect. A restart disconnects existing clients; a reload does not. When troubleshooting auth, always reload between edits, otherwise you are debugging stale rules.
Fix 5: Fix Docker PostgreSQL Roles
In Docker, the default role is controlled by the POSTGRES_USER environment variable:
docker run -e POSTGRES_USER=myuser -e POSTGRES_PASSWORD=mypassword -e POSTGRES_DB=mydb -p 5432:5432 postgres:16If you omit POSTGRES_USER, the default role is postgres.
Docker Compose:
services:
db:
image: postgres:16
environment:
POSTGRES_USER: myuser
POSTGRES_PASSWORD: mypassword
POSTGRES_DB: mydb
ports:
- "5432:5432"Existing container with wrong role:
If the container was already created with a different user, the data volume retains the old configuration. Delete the volume and recreate:
docker compose down -v # -v removes volumes
docker compose up -dWarning: -v deletes all data in the PostgreSQL volume. Only do this for development databases.
If you need to add a role to an existing container:
docker exec -it my-postgres psql -U postgres -c "CREATE ROLE myuser WITH LOGIN PASSWORD 'mypassword';"
docker exec -it my-postgres psql -U postgres -c "CREATE DATABASE mydb OWNER myuser;"Fix 6: Fix macOS Homebrew PostgreSQL
Homebrew PostgreSQL on macOS creates a superuser role matching your macOS username automatically. But if you run psql as a different user or the role was deleted:
Check the default role:
whoami
# Output: john
psql -l # List databases — uses "john" as the roleIf the role was deleted or never created:
createuser --superuser $(whoami)
createdb $(whoami)If postgres role doesn’t exist (common with Homebrew):
createuser --superuser postgresHomebrew installs do not always create the postgres role, which trips up tutorials and tools that assume it exists.
Fix 7: Fix Role Permissions
After creating a role, you might need to grant it access to existing databases and schemas:
-- Connect as postgres superuser
sudo -u postgres psql
-- Grant connection to a database
GRANT CONNECT ON DATABASE mydb TO myuser;
-- Connect to the database
\c mydb
-- Grant usage on schema
GRANT USAGE ON SCHEMA public TO myuser;
-- Grant access to all existing tables
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO myuser;
-- Grant access to future tables
ALTER DEFAULT PRIVILEGES IN SCHEMA public
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO myuser;
-- Grant sequence usage (needed for auto-increment columns)
GRANT USAGE ON ALL SEQUENCES IN SCHEMA public TO myuser;
ALTER DEFAULT PRIVILEGES IN SCHEMA public
GRANT USAGE ON SEQUENCES TO myuser;If the role exists but can’t see tables, the issue is schema permissions. This manifests as relation does not exist errors.
Fix 8: List and Manage Existing Roles
List all roles:
\duOr:
SELECT rolname, rolsuper, rolcreatedb, rolcreaterole, rolcanlogin
FROM pg_roles
ORDER BY rolname;Rename a role:
ALTER ROLE old_name RENAME TO new_name;Drop a role:
-- First reassign owned objects
REASSIGN OWNED BY old_role TO postgres;
-- Then drop
DROP ROLE old_role;You cannot drop a role that owns objects. Reassign or drop the owned objects first.
Grant superuser:
ALTER ROLE myuser WITH SUPERUSER;Remove superuser (for security):
ALTER ROLE myuser WITH NOSUPERUSER;Stranger Causes I Have Tracked Down
If the role exists but you still get this error:
Check for case sensitivity. PostgreSQL folds unquoted identifiers to lowercase. If the role was created with double quotes (CREATE ROLE "MyUser"), you must always use quotes. Check with \du; the role name is shown exactly as stored.
Check the PostgreSQL port. If multiple PostgreSQL instances are running, you might be connecting to the wrong one:
psql -U myuser -h localhost -p 5432
psql -U myuser -h localhost -p 5433 # Another instanceCheck pg_ident.conf for ident mapping. If pg_hba.conf uses peer map=mymap, the pg_ident.conf file controls which OS users map to which database roles. An incorrect mapping causes this error.
Check for connection pooler interference. PgBouncer or Pgpool-II might authenticate against their own user list, not PostgreSQL’s. Check the pooler’s auth_file or userlist.txt.
Check if PostgreSQL is actually running:
systemctl status postgresqlIf the service is down, you get a connection error rather than a role error. But if a different database engine is running on the same port, you might get unexpected authentication errors.
Check pg_hba_file_rules and pg_ident_file_mappings (PostgreSQL 16+). Instead of grepping pg_hba.conf by hand, query the parsed view from inside psql:
SELECT line_number, type, database, user_name, address, auth_method, options, error
FROM pg_hba_file_rules
ORDER BY line_number;The error column flags any malformed entries that the running server is ignoring. A typo in pg_hba.conf that silently disables a rule is a common reason “the right entry is there” but auth still fails.
Check the cluster’s initdb defaults. Run pg_config --pkglibdir and pg_settings for password_encryption. A cluster initialized on PostgreSQL 14+ defaults to scram-sha-256. A role created on a 13 cluster, then pg_upgraded to 14, still has its old MD5 hash and may fail SCRAM challenges from newer libpq clients. Re-set the password with ALTER ROLE myuser PASSWORD 'pw'; after upgrading to regenerate the hash with the cluster default.
Check that the template1 database has not been dropped or renamed. New databases are cloned from template1. If template1 is missing or corrupted, role-related catalog views can return stale results, and role creation can fail silently in some replication setups. Verify with \l and compare against a fresh install.
Verify the role on the right cluster in a multi-instance setup. Running pg_lsclusters (Debian/Ubuntu) shows every PostgreSQL instance on the machine. It is common to create a role on cluster 15/main and connect to cluster 16/main. The role exists somewhere, just not where you connected.
What Other Tutorials Get Wrong About This Error
Most PostgreSQL tutorials list the same fixes but frame them in ways that produce subtle bugs.
They show psql without flags as if it should work. It does not, after a fresh install on Linux. The default role is postgres, not your OS user. Tutorials that say “just run psql” produce the exact error this article exists to fix.
They omit the peer-vs-password distinction. peer authentication maps OS user to role name; scram-sha-256 checks a password. Articles that show role-creation steps without explaining which auth method is configured leave readers confused when their password works in one context and fails in another.
They confuse USER and ROLE. Tutorials that write CREATE USER in one example and CREATE ROLE in another without mentioning they are aliases produce a mental model mismatch. CREATE USER is CREATE ROLE WITH LOGIN; same object, same error message.
They miss the case-folding rule. CREATE ROLE MyUser actually stores myuser. Connecting with -U "MyUser" (quoted) then fails. Articles that show camelCase or PascalCase role names without flagging the quoting requirement produce broken patterns.
They omit pg_hba_file_rules as the modern diagnostic. Grepping pg_hba.conf by hand is the old way. PostgreSQL 16+ exposes the parsed rules and any errors via pg_hba_file_rules. Tutorials that focus only on the file send readers debugging stale rules.
They miss the Docker volume persistence trap. Setting POSTGRES_USER in docker-compose.yml after the volume already exists does nothing; the env var only applies at first init. Articles that show env vars as “the fix” without warning about volume persistence send readers in circles.
Frequently Asked Questions
Why does psql look for my OS username as the role?
PostgreSQL clients default to the current OS username for the role when -U is not specified. This is intentional: it matches the peer authentication model where the operating system identity IS the database identity. On a fresh install where only the postgres role exists, you need to pass -U postgres or switch to the postgres OS user with sudo -u postgres psql.
What is the difference between peer and md5 / scram-sha-256 authentication?
peer validates by matching the OS username (via Unix socket) to the PostgreSQL role name; no password is checked. md5 and scram-sha-256 check a password the client supplies. peer is more convenient on a single-user dev machine but cannot work for remote connections; password methods work everywhere. scram-sha-256 is stronger than md5 and is the default in PostgreSQL 14+.
Why does my Docker container ignore POSTGRES_USER changes?
POSTGRES_USER is only honored when the data directory is initialized for the first time. Once the volume exists, the env var has no effect. To change the default role, either re-init the volume (docker compose down -v; destroys data) or run CREATE ROLE myuser ... inside the running container.
Can I just edit pg_hba.conf to use trust and fix this?
You can, but you should not. trust allows any connection from the matching host to authenticate as any role without a password. It is appropriate ONLY for completely isolated dev environments. Production setups always need scram-sha-256 (preferred) or md5 for password-based auth, plus TLS for remote connections.
Why does CREATE ROLE MyUser produce a lowercase role?
PostgreSQL folds unquoted identifiers to lowercase. To preserve case, double-quote: CREATE ROLE "MyUser". But then you must always quote it: psql -U "MyUser" -d mydb. This trips up tutorials copy-pasted between PostgreSQL and SQL Server / MySQL (which handle case differently). For sanity, stick to lowercase role names.
Does the connection string fail with the same error if the role exists but the password is wrong?
No, the message is different. A non-existent role gives FATAL: role "name" does not exist. A wrong password against an existing role gives FATAL: password authentication failed for user "name". The two are different problems with different fixes; do not conflate them.
If the connection itself fails rather than authentication, see Fix: PostgreSQL connection refused. If the role exists but queries fail because tables are missing, see Fix: relation does not exist. If you also see deadlocks during your application’s startup migrations, those interact with new-role grants; see Fix: PostgreSQL deadlock detected.
For similar authentication issues in MySQL, see Fix: MySQL access denied for user.
Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.
Was this article helpful?
Related Articles
Fix: PostgreSQL JSONB Query Not Working — Operator Errors, Wrong Results, or Slow Queries
How to fix PostgreSQL JSONB query issues — -> vs ->> operators, @> containment, GIN indexes, type casting, array queries, and indexing strategies for JSONB columns.
Fix: PostgreSQL Row Level Security Not Working — Policy Not Applied, All Rows Visible, or Permission Denied
How to fix PostgreSQL Row Level Security (RLS) issues — enabling RLS, policy expressions, BYPASSRLS role, SET ROLE, current_user vs session_user, and Supabase auth.uid() patterns.
Fix: PostgreSQL Index Not Being Used — Query Planner Ignores Index
How to fix PostgreSQL indexes not being used — EXPLAIN ANALYZE output, function on indexed column, type mismatches, statistics staleness, partial indexes, and query planner costs.
Fix: PostgreSQL Slow Query — Finding and Optimizing with EXPLAIN ANALYZE
How to diagnose and fix slow PostgreSQL queries — reading EXPLAIN ANALYZE output, adding the right indexes, fixing N+1 queries, optimizing joins, and using pg_stat_statements.