Skip to content

Matches page titles and page text. Forty pages, indexed at build time.

Certificates and verify-full

What certificate your database endpoint presents, what a client has to do to verify it properly, what rotation asks of you, and what a compromise would reach.

Your database endpoint presents a publicly trusted certificate covering that endpoint's hostname. Your client verifies it against the trust store it already has. There is no bundle to install and no path to configure.

Verify it properly

bash
psql "postgresql://[email protected]:25438/appdb?sslmode=verify-full&sslrootcert=system"

verify-full is the only acceptable mode. require encrypts the connection and accepts a certificate from anyone, which protects against passive interception and not against an active one. verify-ca checks the chain but not the hostname.

sslrootcert=system is libpq's name for the operating system's trust store and needs libpq 16 or newer; on an older libpq give it the path to that store instead (/etc/ssl/certs/ca-certificates.crt on Debian and Ubuntu).

Two drivers do not read sslrootcert and need a parameter of their own: pgjdbc needs sslfactory=org.postgresql.ssl.DefaultJavaSSLFactory before it will use the JVM truststore, and node-postgres needs servername before rejectUnauthorized amounts to verify-full. See client examples for the exact configuration per language.

Connect to the hostname, not to an IP address. verify-full matches the hostname against the certificate and an IP address will not match.

The private key

The private key is generated on the host that runs your service, inside that service's own directory, owned by the service user at mode 0600. PostgreSQL refuses to start with a key any wider than that.

The key never leaves the host. The host builds a certificate signing request; the control plane completes the authority's challenge and returns a signed certificate. Nothing carries the key to another host, to the control plane or into a secrets store.

What replaced the earlier design

Until 22 September 2026 this page described issuance from a certificate authority of ours, with a bundle customers would deploy alongside their application. That design is withdrawn. It was never built, no such authority ran, and the bundle it depended on was never downloadable, so verify-full against a Balta service was not something a customer could execute.

Public issuance is the design now, for one reason that outweighs the rest: it verifies against a store the customer's platform already maintains, so the step that was never finished is a step that no longer exists. The internal private authority stays where it was, for mutual TLS between our own components on our own network. It is not what your connection is presented.

Rotation

Certificates are short-lived, and a short lifetime is the revocation control that actually works here, because PostgreSQL clients rarely check revocation lists.

Replacement changes nothing on your side. You are verifying against roots your operating system, your JVM or your Node build maintains, and those roots are not ours to rotate.

What a compromise would reach

Stated plainly, because a vague claim here is worth nothing:

CompromisedReaches
One database runtimeThe private key for that endpoint. Nothing that impersonates another customer
One host, at rootThe private keys of the endpoints on that host
The control planeThe ability to order new certificates for hostnames we control

No key on a database host can be presented for a hostname it does not cover, because each key is generated for one endpoint and the certificate over it names that endpoint.