DEV Community

Philip McClarence
Philip McClarence

Posted on

FATAL: no pg_hba.conf entry for host: a diagnostic flow for a symptom with sever

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
Enter fullscreen mode Exit fullscreen mode

or, on some PostgreSQL versions:

FATAL:  no pg_hba.conf entry for host "10.12.4.37", user "app_user", database "appdb", SSL off
Enter fullscreen mode Exit fullscreen mode

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:

  1. Read the exact host, user, database, and encryption/SSL text from the error.
  2. Check the PostgreSQL server log. Trust the address PostgreSQL saw, not the address you expected.
  3. Find the pg_hba.conf file PostgreSQL is actually using:
   SHOW hba_file;
Enter fullscreen mode Exit fullscreen mode
  1. Read pg_hba.conf from top to bottom. First match wins.
  2. Check whether the logged address is IPv4 or IPv6.
  3. If Docker or containers are involved, verify the real bridge/subnet.
  4. Check whether the rule uses host, hostssl, or hostnossl.
  5. Validate the file with pg_hba_file_rules.
  6. 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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

Example:

              hba_file
-------------------------------------
 /etc/postgresql/16/main/pg_hba.conf
Enter fullscreen mode Exit fullscreen mode

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;'
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

The log says:

FATAL:  no pg_hba.conf entry for host "10.12.4.37", user "app_user", database "appdb", no encryption
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Then reload:

SELECT pg_reload_conf();
Enter fullscreen mode Exit fullscreen mode

If you cannot reload from SQL, use the OS-level equivalent:

pg_ctl reload -D /var/lib/postgresql/16/main
Enter fullscreen mode Exit fullscreen mode

or:

sudo systemctl reload postgresql
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

and assume you allowed every host.

You did not. You allowed every IPv4 host.

IPv6 is separate:

host    all    all    ::/0         scram-sha-256
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

That means your HBA rule for local connections will not help:

host    all    all    127.0.0.1/32    scram-sha-256
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Example:

"Subnet": "172.17.0.0/16"
Enter fullscreen mode Exit fullscreen mode

Then add an appropriate rule:

host    appdb    app_user    172.17.0.0/16    scram-sha-256
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

The IP, user, and database look right. The encryption state is wrong.

Fix the client:

sslmode=require
Enter fullscreen mode Exit fullscreen mode

or change the HBA policy if non-SSL is intentionally allowed:

host       appdb    app_user    10.12.4.0/24    scram-sha-256
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

with this one:

FATAL:  password authentication failed for user "app_user"
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

md5 still exists, but scram-sha-256 is the modern choice for new rules

Top comments (0)