Every message Queen stores travels over the connection between the broker and PostgreSQL. That connection is plaintext by default. Two variables control it, and the difference between them is the difference between encryption and authenticated encryption.
| Variable | Default | Effect |
|---|---|---|
PG_USE_SSL |
false |
Wrap the connection in TLS at all |
PG_SSL_REJECT_UNAUTHORIZED |
true |
Validate the server’s certificate chain |
With PG_USE_SSL=false the broker connects with no TLS layer whatsoever: the
connector is built only when the flag is on. There is no “prefer” or opportunistic
mode: it is off, or it is on with one of the two verification behaviours below.
Verified TLS: the default when TLS is on
PG_USE_SSL=trueThe broker builds a rustls client on the ring provider and trusts the Mozilla root
set bundled into the binary at compile time (webpki-roots). The server’s chain must
validate against those roots and its name must match.
This is the mode to want. It is also the mode that fails against most managed PostgreSQL offerings, because they present a chain rooted in their own private CA rather than a public one. The broker has no variable for adding a custom root certificate: the trust store is the compiled-in Mozilla set and nothing else. If your provider’s chain is private, your options are the system trust of a sidecar or tunnel in front of PostgreSQL, or the encrypt-only mode below.
Encrypt-only: what managed PostgreSQL usually needs
PG_USE_SSL=truePG_SSL_REJECT_UNAUTHORIZED=falseThis installs a certificate verifier that accepts any certificate the server
presents. Signature verification for the TLS handshake itself still runs (the
handshake is cryptographically sound against the key in whatever certificate
arrives), but nothing checks whose key it is. It is the equivalent of libpq’s
sslmode=require.
What you get:
- The bytes on the wire are encrypted. A passive tap on the network path sees ciphertext, not payloads, and not the credentials in the startup message.
What you do not get:
- No proof of identity. Any endpoint that completes a TLS handshake is accepted as your database.
- No protection against an active man in the middle. An attacker who can redirect the broker’s TCP connection (a poisoned DNS answer, a hijacked service record, an ARP-level redirect, a compromised sidecar) terminates TLS with its own self-signed certificate, and the broker connects happily. It then holds the database password from the startup message and every message the broker writes.
- No detection of a certificate swap. There is nothing to compare against, so a substituted certificate produces no error and no log line.
In other words, encrypt-only defends against someone reading the wire and not against someone becoming the other end of it.
Choosing
-
Same host or private socket, nothing else on the segment. Leave
PG_USE_SSL=false. TLS to localhost buys ciphertext on a loopback nobody else can read, at the cost of a handshake per pooled connection. -
Managed PostgreSQL with a public CA chain.
PG_USE_SSL=trueand leavePG_SSL_REJECT_UNAUTHORIZEDat its default. Verify by breaking it on purpose once: point the host at something with a wrong name and confirm the broker fails to connect. -
Managed PostgreSQL with a private CA chain.
PG_USE_SSL=trueplusPG_SSL_REJECT_UNAUTHORIZED=false, and treat the network path as part of your trust boundary. Document that it is, because the certificate no longer proves anything about it. -
Across an untrusted network. Do not rely on either mode. Terminate the trust decision somewhere that can validate a private root: a TLS-terminating proxy next to the broker, or a VPN.
How it applies
The setting affects the broker’s whole PostgreSQL surface, not just the request path: the deadpool connection pool, the boot-time connection that applies the embedded schema, and the maintenance connections all go through the same connector. There is no separate knob for the migration connection.
The flags are read once at boot, like every other broker variable. A failure to establish TLS surfaces as a connection error at boot or as pool checkout failures at runtime. The broker does not silently fall back to plaintext.
Related
- Trust boundaries: where this boundary sits among the others, including the mesh port and the plaintext HTTP listener.
- Payload encryption at rest: the only mechanism that protects payloads once they are inside the database.
- The full variable list, with defaults, is in the reference.