I’ve been paged for this error more times than I want to admit, and every time it is a different root cause wearing the same trench coat.
The message looks like one problem:
FATAL: no pg_hba.conf entry for host "10.12.4.37", user "app_user", database "appdb", no encryption
or, on some PostgreSQL versions:
FATAL: no pg_hba.conf entry for host "10.12.4.37", user "app_user", database "appdb", SSL off
But the next useful check might be a missing rule, the wrong file, IPv4 versus IPv6, container networking, SSL policy, or rule ordering. Most bad advice fixes exactly one of those and leaves you wondering why the “solution” did nothing.
Here is the flow I use when I want the outage to end.
TL;DR: the fast path
When you see no pg_hba.conf entry for host, do this first:
- Read the exact
host,user,database, and encryption/SSL text from the error. - Check the PostgreSQL server log. Trust the address PostgreSQL saw, not the address you expected.
- Find the
pg_hba.conffile PostgreSQL is actually using:
SHOW hba_file;
- Read
pg_hba.conffrom top to bottom. First match wins. - Check whether the logged address is IPv4 or IPv6.
- If Docker or containers are involved, verify the real bridge/subnet.
- Check whether the rule uses
host,hostssl, orhostnossl. - Validate the file with
pg_hba_file_rules. - Reload. Do not restart for an HBA-only change.
The log line you want looks like this:
2026-07-10 03:14:22.901 UTC [18473] app_user@appdb FATAL: no pg_hba.conf entry for host "10.12.4.37", user "app_user", database "appdb", no encryption
That host, that user, that database, and that encryption state are your whole search query.
Everything else is detail work.
First, decode the error message itself
A typical client-side failure looks like this:
psql: error: connection to server at "db01" (10.12.4.20), port 5432 failed:
FATAL: no pg_hba.conf entry for host "10.12.4.37", user "app_user", database "appdb", no encryption
Do not read this as “Postgres is broken.” Read it as a filled-out form.
| Error field | Meaning | HBA column involved |
|---|---|---|
host "10.12.4.37" |
Client address PostgreSQL received | address |
user "app_user" |
Requested database role | user |
database "appdb" |
Requested database name | database |
no encryption, SSL off, SSL on
|
Whether the connection used SSL/GSS encryption |
type: host, hostssl, hostnossl, etc. |
| No auth method shown | No matching row was found |
method only matters after a row matches |
A pg_hba.conf record has this shape:
# TYPE DATABASE USER ADDRESS METHOD
host appdb app_user 10.12.4.37/32 scram-sha-256
Every relevant field in the error has to match a row in pg_hba.conf.
If the address does not cover 10.12.4.37, the row does not match.
If the database column does not include appdb, the row does not match.
If the user column does not include app_user, the row does not match.
If the row is hostssl but the client connected without SSL, the row does not match.
If no row matches, PostgreSQL emits the generic FATAL. It deliberately does not tell the client which rule almost matched or why. That is a security decision, not a missing convenience feature.
Find the file PostgreSQL is actually using
Before editing anything, confirm the active HBA path:
SHOW hba_file;
Example:
hba_file
-------------------------------------
/etc/postgresql/16/main/pg_hba.conf
This matters. On Debian/Ubuntu packages, Docker images, source installs, and managed-ish environments, the file you think is active may not be the file PostgreSQL actually loaded.
If you have no working database session, check from the server using your service layout, for example:
sudo -u postgres psql -c 'SHOW hba_file;'
or inspect the PostgreSQL configuration with the same data directory the service uses.
Cause 1: There is no matching rule
This is the boring case, and boring is fine at 3am.
Suppose the current file has:
# TYPE DATABASE USER ADDRESS METHOD
host appdb app_user 10.12.4.21/32 scram-sha-256
host appdb app_user 10.12.4.22/32 scram-sha-256
The log says:
FATAL: no pg_hba.conf entry for host "10.12.4.37", user "app_user", database "appdb", no encryption
PostgreSQL saw 10.12.4.37. Your HBA file allows .21 and .22. There is no mystery.
Add the missing row:
# TYPE DATABASE USER ADDRESS METHOD
host appdb app_user 10.12.4.37/32 scram-sha-256
Then reload:
SELECT pg_reload_conf();
If you cannot reload from SQL, use the OS-level equivalent:
pg_ctl reload -D /var/lib/postgresql/16/main
or:
sudo systemctl reload postgresql
For pg_hba.conf, reload is the move. A full restart is usually self-inflicted pain.
Cause 2: The rule exists, but it is below a rule that wins first
pg_hba.conf is evaluated top to bottom.
Not best match.
Not most specific match.
First match.
This is one of the easiest mistakes to miss:
# TYPE DATABASE USER ADDRESS METHOD
host all all 10.0.0.0/8 reject
host appdb app_user 10.12.4.37/32 scram-sha-256
That second row is never reached. The broad reject catches the connection first.
The fix is not to keep editing the specific rule. Move it above the broad rule, or narrow the broad rule:
# TYPE DATABASE USER ADDRESS METHOD
host appdb app_user 10.12.4.37/32 scram-sha-256
host all all 10.0.0.0/8 reject
When debugging, do not just search for the rule you wanted. Scan upward from it and ask: “Would anything before this match first?”
Possible cause 3: IPv4 versus IPv6
This is worth checking, especially with localhost or dual-stack names.
You write:
host all all 0.0.0.0/0 scram-sha-256
and assume you allowed every host.
You did not. You allowed every IPv4 host.
IPv6 is separate:
host all all ::/0 scram-sha-256
The loopback addresses are separate too:
host all all 127.0.0.1/32 scram-sha-256
host all all ::1/128 scram-sha-256
If your app connects to localhost, resolvers may try ::1 before 127.0.0.1. A rule for 127.0.0.1/32 will not match ::1.
Check the FATAL line. If the host contains colons, it is IPv6:
FATAL: no pg_hba.conf entry for host "::1", user "app_user", database "appdb", no encryption
No amount of reloading will make an IPv4 CIDR match an IPv6 address.
Possible cause 4: Docker or container networking changed the source IP
If your app runs in a container and connects to PostgreSQL on the host, the source address may not be 127.0.0.1.
Depending on the setup, it may be something from a Docker bridge network, such as:
FATAL: no pg_hba.conf entry for host "172.17.0.4", user "app_user", database "appdb", no encryption
That means your HBA rule for local connections will not help:
host all all 127.0.0.1/32 scram-sha-256
PostgreSQL did not see 127.0.0.1. It saw 172.17.0.4.
Check the actual Docker subnet on the database host:
docker network inspect bridge | grep Subnet
Example:
"Subnet": "172.17.0.0/16"
Then add an appropriate rule:
host appdb app_user 172.17.0.0/16 scram-sha-256
Be careful with platform assumptions. Docker Desktop on macOS and Windows uses a different networking layer than Docker Engine on Linux. A Compose setup that works on a laptop can fail in Linux CI or production because the source IP is different.
The PostgreSQL log wins. Always.
Possible cause 5: SSL/TLS policy does not match the client
The HBA TYPE column matters.
These are not equivalent:
host appdb app_user 10.12.4.0/24 scram-sha-256
hostssl appdb app_user 10.12.4.0/24 scram-sha-256
hostnossl appdb app_user 10.12.4.0/24 scram-sha-256
A hostssl row only matches encrypted SSL connections.
A hostnossl row only matches non-SSL connections.
A plain host row can match either.
So if your file contains only this:
hostssl appdb app_user 10.12.4.0/24 scram-sha-256
and the client connects with SSL disabled, PostgreSQL may report:
FATAL: no pg_hba.conf entry for host "10.12.4.37", user "app_user", database "appdb", no encryption
The IP, user, and database look right. The encryption state is wrong.
Fix the client:
sslmode=require
or change the HBA policy if non-SSL is intentionally allowed:
host appdb app_user 10.12.4.0/24 scram-sha-256
or explicitly allow both with separate rows:
hostssl appdb app_user 10.12.4.0/24 scram-sha-256
hostnossl appdb app_user 10.12.4.0/24 scram-sha-256
If you use client certificates, also check certificate identity rules:
hostssl appdb app_user 10.12.4.0/24 scram-sha-256 clientcert=verify-full
With clientcert=verify-full, the client certificate must validate, and the certificate identity must match the database user unless you are using a configured user map. If that fails, you may see a certificate/authentication failure rather than a plain “no HBA entry,” but it belongs in the same SSL branch of the investigation.
Important non-cause: password authentication failed
Do not confuse this error:
FATAL: no pg_hba.conf entry for host "10.12.4.37", user "app_user", database "appdb", no encryption
with this one:
FATAL: password authentication failed for user "app_user"
They mean opposite things.
no pg_hba.conf entry means no HBA row matched.
password authentication failed means a row did match, PostgreSQL ran the authentication method, and the credentials failed.
For example, this row may match perfectly:
host appdb app_user 10.12.4.37/32 scram-sha-256
If the password is wrong, you do not have an HBA problem anymore. You have a password, role, secret, or client-driver problem.
Also check the authentication method itself:
host all all 10.0.0.0/8 md5
md5 still exists, but scram-sha-256 is the modern choice for new rules
Top comments (0)