diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,9 @@
+# v1.0.15.0
+
+## Non-breaking
+
+- Added a `connectdb` spec covering a conninfo that gives an unresolvable `host` together with a `hostaddr` that is reachable (a loopback proxy in front of the container). `libpq` connects to the `hostaddr` and never resolves the `host`, and still reports the name from `host`. Both the `keyword=value` form and the URI form (`?hostaddr=`) are covered. Found `pqi-native` never reads `hostaddr`: it resolves `host` through DNS regardless, so the connection fails, and the key is passed on in the startup packet as if it were a server setting. Found via `pqi-native` issue #11.
+
 # v1.0.14.0
 
 ## Non-breaking
diff --git a/pqi-conformance.cabal b/pqi-conformance.cabal
--- a/pqi-conformance.cabal
+++ b/pqi-conformance.cabal
@@ -1,6 +1,6 @@
 cabal-version: 3.0
 name: pqi-conformance
-version: 1.0.14.0
+version: 1.0.15.0
 category: Database, PostgreSQL, Testing
 synopsis: Differential conformance tests for pqi adapters
 description:
@@ -96,6 +96,7 @@
     Pqi.Conformance.Operation.CmdTuples
     Pqi.Conformance.Operation.Connectdb
     Pqi.Conformance.Operation.Connectdb.HandshakeReset
+    Pqi.Conformance.Operation.Connectdb.Hostaddr
     Pqi.Conformance.Operation.Connectdb.MissingUnixSocketDirectory
     Pqi.Conformance.Operation.Connectdb.Rejection
     Pqi.Conformance.Operation.Connectdb.UnixSocketUri
diff --git a/src/library/Pqi/Conformance/Operation/Connectdb.hs b/src/library/Pqi/Conformance/Operation/Connectdb.hs
--- a/src/library/Pqi/Conformance/Operation/Connectdb.hs
+++ b/src/library/Pqi/Conformance/Operation/Connectdb.hs
@@ -15,6 +15,7 @@
 import Pqi.Conformance.Harness
 import Pqi.Conformance.Observation
 import qualified Pqi.Conformance.Operation.Connectdb.HandshakeReset as HandshakeReset
+import qualified Pqi.Conformance.Operation.Connectdb.Hostaddr as Hostaddr
 import qualified Pqi.Conformance.Operation.Connectdb.MissingUnixSocketDirectory as MissingUnixSocketDirectory
 import qualified Pqi.Conformance.Operation.Connectdb.Rejection as Rejection
 import qualified Pqi.Conformance.Operation.Connectdb.UnixSocketUri as UnixSocketUri
@@ -31,6 +32,7 @@
   MissingUnixSocketDirectory.spec adapter
   UnresolvableHost.spec adapter
   HandshakeReset.spec adapter
+  Hostaddr.spec adapter
 
   describe "connectdb" do
     it "opens a usable connection" \conninfo ->
diff --git a/src/library/Pqi/Conformance/Operation/Connectdb/Hostaddr.hs b/src/library/Pqi/Conformance/Operation/Connectdb/Hostaddr.hs
new file mode 100644
--- /dev/null
+++ b/src/library/Pqi/Conformance/Operation/Connectdb/Hostaddr.hs
@@ -0,0 +1,76 @@
+-- | Coverage for a conninfo that gives both @host@ and @hostaddr@, in the
+-- @keyword=value@ form and in the URI form (@?hostaddr=…@).
+--
+-- Per libpq's documentation
+-- (<https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNECT-HOSTADDR>),
+-- when @hostaddr@ is present the client connects to that numeric address and
+-- must not resolve @host@: the name is kept only for authentication, SSL
+-- certificate verification and the like. A name that no resolver can
+-- translate is therefore harmless as long as @hostaddr@ is usable.
+--
+-- Found in @pqi-native@ (<https://github.com/nikita-volkov/pqi-native/issues/11>):
+-- @hostaddr@ is not one of the keys 'Pqi.Native.Connection.parseConnInfo'
+-- recognises, so it was never consulted when dialling. The candidate resolved
+-- @host@ through DNS regardless, and so reported a connection failure for a
+-- conninfo that @libpq@ connects with. The key was also left in the
+-- parameters forwarded to the server in the startup packet, where @libpq@
+-- never sends it.
+--
+-- The scenario points @hostaddr@ at a loopback proxy in front of the shared
+-- container ('withProxy'), which makes the numeric address valid wherever the
+-- container's Docker daemon runs, and gives @host@ a name reserved by
+-- RFC 2606 to never resolve. The observation is the connection status
+-- together with the parsed host. The status carries the divergence. The host
+-- pins that the name is still what 'Pqi.host' reports, as @libpq@ does, so a
+-- fix that simply replaced @host@ with @hostaddr@ would be caught.
+--
+-- The URI form is its own example because @pqi-native@ parses a URI's query
+-- through a separate code path, where a fix to the @keyword=value@ one does
+-- not reach.
+module Pqi.Conformance.Operation.Connectdb.Hostaddr
+  ( spec,
+  )
+where
+
+import qualified Data.ByteString.Char8 as ByteString.Char8
+import qualified Pqi
+import Pqi.Conformance.Fixture.ConnectionLoss (withProxy)
+import Pqi.Conformance.Harness
+import Pqi.Conformance.Prelude
+import Test.Hspec
+
+spec :: Pqi.Adapter -> SpecWith ByteString
+spec adapter =
+  describe "connectdb" do
+    describe "a conninfo with an unresolvable host and a hostaddr" do
+      it "connects to the hostaddr without resolving the host, like the reference" \conninfo ->
+        differentialConnect adapter conninfo \adapter' conninfo' ->
+          withProxy conninfo' \proxiedConninfo _ ->
+            -- Later keywords win, so these override the proxy's @host@.
+            observe adapter' (proxiedConninfo <> " host=nonexistent.invalid.host hostaddr=127.0.0.1")
+
+      it "does the same when given as a URI query parameter" \conninfo ->
+        differentialConnect adapter conninfo \adapter' conninfo' ->
+          withProxy conninfo' \proxiedConninfo _ ->
+            observe
+              adapter'
+              ( "postgresql://postgres@nonexistent.invalid.host:"
+                  <> proxyPort proxiedConninfo
+                  <> "/postgres?hostaddr=127.0.0.1"
+              )
+
+observe :: Pqi.Adapter -> ByteString -> IO (Pqi.ConnStatus, Maybe ByteString)
+observe adapter conninfo = do
+  connection <- Pqi.connectdb adapter conninfo
+  observedStatus <- Pqi.status connection
+  observedHost <- Pqi.host connection
+  Pqi.finish connection
+  pure (observedStatus, observedHost)
+
+-- | The port the proxy listens on: 'withProxy' appends it as the last
+-- keyword of the conninfo it hands out.
+proxyPort :: ByteString -> ByteString
+proxyPort conninfo =
+  case [value | token <- reverse (ByteString.Char8.words conninfo), Just value <- [ByteString.Char8.stripPrefix "port=" token]] of
+    value : _ -> value
+    [] -> error "proxyPort: no port in the proxied conninfo"
