Fix PostgreSQL "Peer authentication failed" for Django
Fix Django FATAL Peer authentication failed for user when connecting to local PostgreSQL: understand peer versus password auth in pg_hba.conf, connect over TCP with HOST=localhost, or map the Linux user correctly.
Problem statement
On a server with PostgreSQL installed locally, Django fails to connect:
django.db.utils.OperationalError: connection to server on socket
"/var/run/postgresql/.s.PGSQL.5432" failed: FATAL: Peer authentication failed for user "myapp"
The password is correct and the role exists. The problem is the authentication method. Debian and Ubuntu configure PostgreSQL to use peer authentication for local Unix-socket connections: the server asks the kernel which Linux user owns the connecting process and only allows it if that name matches the database role. Gunicorn running as www-data or deploy cannot log in as role myapp this way, no matter what password you send.
- Easiest fix: set
HOSTtolocalhost(or127.0.0.1) in Django settings so it connects over TCP, which usesscram-sha-256password auth. - Alternatively, change the
localline for your database inpg_hba.conftoscram-sha-256and reload PostgreSQL. - Or run Django as a Linux user with the same name as the role (or add an ident map).
- Reload, do not restart:
sudo systemctl reload postgresql.
Step-by-step solution
1. Confirm the connection method
An empty HOST in Django means "connect via the default Unix socket". Check what is configured:
python manage.py shell -c "from django.db import connection; print(repr(connection.settings_dict['HOST']))"
'' or a path like /var/run/postgresql means a socket connection, governed by local lines in pg_hba.conf. localhost or an IP means TCP, governed by host lines.
2. Option A: connect over TCP
DATABASES = {
"default": {
"ENGINE": "django.db.backends.postgresql",
"NAME": "myapp",
"USER": "myapp",
"PASSWORD": env("DB_PASSWORD"),
"HOST": "127.0.0.1",
"PORT": "5432",
}
}
With a DATABASE_URL, use postgres://myapp:secret@127.0.0.1:5432/myapp. Default Debian and Ubuntu pg_hba.conf already has:
host all all 127.0.0.1/32 scram-sha-256
host all all ::1/128 scram-sha-256
so password authentication works immediately. The TCP overhead on localhost is negligible for a Django app.
3. Option B: password auth on the socket
If you prefer the socket (for example, to keep PostgreSQL off the network entirely), add a line above the generic local all all peer line:
# /etc/postgresql/16/main/pg_hba.conf
# TYPE DATABASE USER METHOD
local myapp myapp scram-sha-256
local all all peer
sudo systemctl reload postgresql
pg_hba.conf is read top to bottom and the first matching line wins, so order matters.
4. Option C: keep peer, match the user
Peer authentication is secure and needs no password. To use it, run Gunicorn as a Linux user named like the role (User=myapp in the systemd unit, with USER set and PASSWORD empty in Django), or map users explicitly:
# pg_ident.conf
# MAPNAME SYSTEM-USERNAME PG-USERNAME
djangomap www-data myapp
# pg_hba.conf
local myapp myapp peer map=djangomap
5. Check the logs if it still fails
sudo tail -n 20 /var/log/postgresql/postgresql-16-main.log
The server log names the pg_hba.conf line that matched (Connection matched file "/etc/postgresql/16/main/pg_hba.conf" line 118), which shows whether your new line is being used or an earlier one wins.
Verification check:
sudo -u www-data psql "host=127.0.0.1 dbname=myapp user=myapp" -c "SELECT current_user;"
python manage.py migrate --check
Run the check as the same Linux user Gunicorn uses, because peer results depend on who runs the command.
Explanation
pg_hba.conf maps each connection type, database, user, and address to an authentication method. Debian's packaging chooses peer for local sockets because it lets the postgres OS user administer the server without a password, and keeps local access tied to Unix accounts. Django deployments usually run under a service account whose name does not match the database role, so the default fails. Moving to TCP on loopback swaps in password authentication with no change to PostgreSQL itself.
Edge cases / notes
- "Ident authentication failed" is the same idea for TCP connections on some RHEL-family defaults; change the
hostline toscram-sha-256. sudo -u postgres psqlworks because thepostgresOS user matches thepostgresrole under peer auth. That does not mean your app's role can connect the same way.- Docker images of PostgreSQL use
trustor password auth by default; peer errors there usually mean you connected to a host installation instead. - Password hashing. If you switch an old
md5setup toscram-sha-256, reset the role's password so it is stored as SCRAM.
Next steps
- If you now see a password error instead, follow Fix Postgres Password Authentication Failed.
- Review the full connection setup in Connect Django to PostgreSQL in Production.
FAQ
Why does psql work but Django fails?
You probably run psql as a user whose name matches the role, or connect with -h localhost. Django connects via the socket as a different OS user.
Is peer authentication insecure?
No. It is very secure for local access. It just requires the OS user to match the role.
Do I need to restart PostgreSQL after editing pg_hba.conf?
No. A reload is enough: sudo systemctl reload postgresql.
Is connecting over TCP to localhost slower?
Marginally, and not measurably for a typical Django app.