Skip to content

Fix: PostgreSQL FATAL: role "username" does not exist

FixDevs · (Updated: )

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 exist

Or variations:

FATAL: role "root" does not exist
FATAL: role "myuser" does not exist
psql: FATAL: role "postgres" does not exist
django.db.utils.OperationalError: FATAL: role "app_user" does not exist

PostgreSQL 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 ubuntu but PostgreSQL only has the postgres role.
  • Fresh installation. PostgreSQL creates only the postgres superuser role by default. No other roles exist until you create them.
  • Wrong username in connection string. Your application’s DATABASE_URL specifies 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 MyUser actually creates myuser. 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_user and pg_group were separate catalogs. After 8.1, both are views over pg_roles. CREATE USER and CREATE GROUP remain as legacy aliases. Any tutorial older than 2005 may give incorrect mental models.
  • PostgreSQL 8.4 (Jul 2009) — peer authentication method. Before 8.4, the local socket used ident (over a special socket-level Unix mechanism). 8.4 introduced peer, which became the default for local entries 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-256 authentication added. SCRAM is a stronger password hash than MD5. 10 added support, but defaults stayed on md5 until version 14.
  • PostgreSQL 14 (Sep 2021) — scram-sha-256 is the default in initdb. Run initdb on 14+ and the pg_hba.conf entries for password authentication use scram-sha-256. If you upgrade a 13 cluster’s data directory in place, the old md5 entries 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-256 and --auth-local=scram-sha-256 explicitly when re-initdb’ing to make the choice visible.
  • PostgreSQL 16 (Sep 2023) — regex matching in pg_hba.conf and pg_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. Use SELECT * FROM pg_hba_file_rules; and pg_ident_file_mappings (also new in 16) to see the parsed view.
  • Distribution differences — Debian/Ubuntu vs RHEL/CentOS. Debian-family packages run initdb with local all postgres peer plus local all all peer, so peer is the default for all local users. RHEL-family packages historically used ident sameuser. Homebrew on macOS creates a role matching $(whoami) rather than postgres, which trips up scripts that hard-code sudo -u postgres psql.
  • INHERIT attribute evolution. Role membership was always inheritable by default (CREATE ROLE ... INHERIT). In 16, NOINHERIT became a more useful pattern with the introduction of explicit SET ROLE requirements and the INHERIT FALSE membership option. If you “added a role to a group” and the new role still cannot do what the group can, check whether INHERIT is 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 psql

This 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 localhost

Pro Tip: Using -h localhost forces 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 ubuntu

The --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) n

Then create a database with the same name:

sudo -u postgres createdb ubuntu

Now psql works without any flags:

psql

PostgreSQL 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/mydb

Verify that:

  1. The role myuser exists — run \du in psql to list all roles.
  2. The password is correct — reset it with ALTER ROLE myuser WITH PASSWORD 'newpassword';.
  3. The database mydb exists — run \l in 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: localhost

If 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:

MethodHow it works
peerOS username must match database role name
md5Password authentication (hashed)
scram-sha-256Password authentication (more secure)
trustNo authentication (dangerous)
identLike 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   peer

Then 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-256

Reload PostgreSQL after editing:

sudo systemctl reload postgresql

Common Mistake: Editing pg_hba.conf but 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:16

If 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 -d

Warning: -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 role

If the role was deleted or never created:

createuser --superuser $(whoami)
createdb $(whoami)

If postgres role doesn’t exist (common with Homebrew):

createuser --superuser postgres

Homebrew 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:

\du

Or:

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 instance

Check 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 postgresql

If 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.

F

FixDevs

Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.

Was this article helpful?

Related Articles