The PostgreSQL error "no pg_hba.conf entry for host" has a reassuring side: the network is fine. The client reached the server, the server read the request, went through its host-based authentication rules and found none that matched. Nothing is broken. There is simply no rule for this exact combination of address, user, database and connection type. The fix is to find which of the four doesn't match, and the error message already tells you most of it.
On current PostgreSQL versions the message looks like this:
FATAL: no pg_hba.conf entry for host "10.20.0.15", user "app", database "appdb", no encryption
The PostgreSQL documentation on authentication problems describes it as what you are most likely to get when you do reach the server but it does not want to talk to you. Compare that with "Connection refused" or a timeout. Those mean you never got that far: the server isn't listening on that address, or a firewall is in the way.
Read the message field by field
The text is built in auth.c, and every part of it is useful:
- host: the client IP address as the server sees it. This is the most common surprise. Behind NAT, a load balancer, a connection pooler or a container network it is often not the address you expect.
- user: the database role the client asked for, not the operating system user.
- database: the requested database. If the client didn't specify one, libpq defaults it to the user name. That is how people end up with a request for database "app" when they meant "appdb".
- encryption state:
no encryption,SSL encryptionorGSS encryption. PostgreSQL 13 and older wroteSSL off/SSL onhere instead.
For a physical replication connection the wording changes to "no pg_hba.conf entry for replication connection from host ..." and there is no database field. That is a hint by itself: replication needs its own rule, as we will see below.
Also check the server log, not only the client. The docs point out that the log often has more detail than the client gets. If you use host names in pg_hba.conf, the log will also say whether the client IP resolved to a name and whether the forward lookup matched.
How PostgreSQL chooses a rule
The rules in the pg_hba.conf documentation are short. Records are checked top to bottom, and the first record whose connection type, client address, database and user all match is used. There is no fall-through: if that record's authentication fails, later records are not tried. If nothing matches, access is denied with the error above.
So "no entry" means none of the lines matched on all four fields at once. The usual reasons, roughly in the order we meet them:
- The address is not what you think. The rule allows
10.20.0.0/24, but the application now runs in a different subnet after a migration. Or it connects through PgBouncer or a proxy, and the server sees the proxy's address. Trust the IP in the error message, not the network diagram. - IPv4 versus IPv6. The docs are explicit: an entry written in IPv4 format only matches IPv4 connections, and an IPv6 entry only matches IPv6. If
localhostresolves to::1, a127.0.0.1/32rule does nothing for that client. - Wrong connection type.
locallines only cover Unix-domain sockets,hostcovers TCP/IP with or without encryption,hostsslonly TCP/IP with SSL, andhostnosslonly TCP/IP without it. A rule set made entirely ofhostssllines will reject a client connecting without TLS, and the message will end with "no encryption". Ifsslis not enabled on the server, the docs sayhostsslrecords are ignored apart from a warning in the log. - Replication is not "all". The keyword
allin the database column does not cover physical replication. You need a line withreplicationin the database column. The docs add one more wrinkle:replicationmatches physical replication connections, not logical ones. Logical replication connections specify a database and are matched like normal connections. - Group membership. A plain role name matches only that role. To match members of a group role, prefix it with
+. Superusers are not treated as members of a role just because they are superusers. - The file you edited is not the file in use. The path is set by
hba_file. On some packaged installations it lives outside the data directory. Check withSHOW hba_file;before you edit anything. - You edited it but never reloaded. The file is read at startup and on SIGHUP. Saving it changes nothing until you reload. Windows is the exception: there, changes apply to new connections right away.
See the rules the way the server does
Start with the server log line, then compare it with the parsed rules. The pg_hba_file_rules view shows one row per non-comment line, in the order they are evaluated:
SELECT rule_number, file_name, line_number, type,
database, user_name, address, netmask, auth_method, error
FROM pg_hba_file_rules
ORDER BY rule_number NULLS LAST, line_number;
The rule_number and file_name columns exist from PostgreSQL 16, which also added include directives to pg_hba.conf. On 15 and older, drop those two columns from the query. By default only superusers can read the view.
One detail makes the view very handy, and it is also easy to misread: it reports the current contents of the file, not what the server loaded last. That is perfect for checking a change before reloading. A row with a non-null error means the line can't be parsed:
SELECT file_name, line_number, error
FROM pg_hba_file_rules
WHERE error IS NOT NULL;
Then go through the list from the top with the four values from the error message, and stop at the first line that matches all of them. If you get to the end, that is your answer. If an earlier line matches and uses reject, the client gets a different message, "pg_hba.conf rejects connection for host ...", which tells you an explicit rule blocked it.
Fix it with a narrow rule
Add the most specific line that covers the real client, and place it above any broader rule that could match first:
# TYPE DATABASE USER ADDRESS METHOD
# application servers, TLS required
hostssl appdb app 10.20.0.0/24 scram-sha-256
# replica streaming (physical replication)
hostssl replication replicator 10.20.1.12/32 scram-sha-256
Then reload. You don't need to restart. The rules are checked when a connection is opened, so existing sessions keep running:
SELECT pg_reload_conf();
-- then confirm the file parsed cleanly
SELECT count(*) FROM pg_hba_file_rules WHERE error IS NOT NULL;
pg_ctl reload or kill -HUP on the postmaster do the same. If the file has a syntax error, the reload doesn't lock everyone out. The code in hba.c parses the new file into a temporary list and keeps the old rules if anything fails. The log says "... pg_hba.conf was not reloaded". That's safe, but it also means your fix silently didn't take effect, so check the log or the view after each reload.
host all all 0.0.0.0/0 trust. The docs are blunt about what trust means: anyone who can connect can log in as any user, superusers included, with no password. Use a precise address range and scram-sha-256, even as a temporary fix. Our PostgreSQL security hardening checklist covers how to keep this file strict afterwards.One more check before you leave: remote clients can only connect if listen_addresses includes an interface they can reach. The connection settings docs give the default as localhost, and changing it needs a restart, not a reload. If the server isn't listening there, the client gets "Connection refused" instead of this error.
When the error changes to "password authentication failed"
Often the next attempt after a fix fails with FATAL: password authentication failed for user "app". That's progress: a rule matched, and now the method on that rule is failing. The client only sees this generic text on purpose. The real reason is in the server log, which also names the pg_hba.conf line that matched (PostgreSQL 16 and newer add the file name). crypt.c and the SCRAM code log details such as:
Role "app" does not exist.The role name is wrong. The client still gets "password authentication failed", so nobody learns from the error which roles exist.User "app" has no password assigned.The docs confirm that password authentication always fails for a role with no password set.User "app" has an expired password.Checkrolvaliduntil.User "app" does not have a valid SCRAM secret.The rule saysscram-sha-256, but the password is stored as an MD5 hash.
The last one comes up a lot after hardening. According to the password authentication docs, a SCRAM-stored password works with scram-sha-256, md5 (PostgreSQL silently switches to SCRAM) and password. An MD5-stored password works only with md5 and password. So if you change a line from md5 to scram-sha-256 before re-setting old passwords, those users get locked out. The documented order is: confirm every client library supports SCRAM, set password_encryption = 'scram-sha-256' (already the default on current versions), have users set new passwords, and only then change the method in pg_hba.conf.
-- which hash type does the role have? (superuser only)
SELECT rolname, left(rolpassword, 14) AS hash_prefix, rolvaliduntil
FROM pg_authid
WHERE rolname = 'app';
-- re-set the password so it is stored as SCRAM
SET password_encryption = 'scram-sha-256';
\password app
A hash that starts with SCRAM-SHA-256$ is SCRAM, and one that starts with md5 is the old format.
Two quirks that cause confusing errors
The same error twice. libpq's default sslmode is prefer. The libpq docs describe it as "first try an SSL connection; if that fails, try a non-SSL connection". So a client may show two FATAL lines, one ending "SSL encryption" and one ending "no encryption". That means neither attempt matched a rule. For production clients, set sslmode=require or stricter so you always know which kind of connection you are debugging.
Peer authentication failures. A local line with peer compares the operating system user name with the requested role. Running psql -U postgres as root over the socket matches that line and fails with "Peer authentication failed for user "postgres"". This is a different error from "no entry", and the fix is to run it as the right OS user (or use a user name map), not to rewrite the rule.
Make the next one quicker
- Keep the file under version control and deploy it with your configuration management, with a comment on each line saying who it is for.
- Check
pg_hba_file_rulesfor errors in CI or before every reload. It catches typos before they reach the server. - Alert on bursts of authentication failures. The logging docs note that failed authentication is always logged, whatever
log_connectionsis set to, so the data is already in your logs. - Plan pg_hba.conf changes into network moves. New subnets, a new pooler or a failover to another region almost always need new lines. Add them before the move, not during the incident.
Key takeaways
- "no pg_hba.conf entry for host" means the client reached PostgreSQL but no rule matched its address, user, database and connection type.
- Rules are matched top to bottom, the first match wins, and there is no fall-through.
- Trust the IP in the message. Proxies, NAT, IPv6 and containers change it.
alldoes not cover physical replication, andhostssldoes not match non-TLS clients.- Validate with
pg_hba_file_rules, reload withpg_reload_conf(), and check the log for "was not reloaded". - Never fix it with
trust. When it turns into "password authentication failed", read the server log for the real reason.