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 Error
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.
Why This Happens
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 — and if it doesn’t 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” — and 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.
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 localhostPro Tip: Using
-h localhostforces TCP/IP connection with password authentication, bypassing peer authentication. Without it, psql uses the Unix socket and peer auth, which requires the OS username to match the role name.
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 postgresqlCommon Mistake: Editing
pg_hba.confbut forgetting to reload PostgreSQL. The changes only take effect after a reload (systemctl reload) or restart (systemctl restart). A restart disconnects existing clients; a reload does not.
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;Still Not Working?
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.
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.