How to fix Certificate verify failed in PostgreSQL
PostgreSQL "certificate verify failed" is an SSL/TLS error that occurs when the client cannot verify the server's certificate. It usually means the root CA certificate is missing, outdated, or misconfigured.
Quick fix
Fixes Certificate verify failed
wget https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem -O rds-ca-global.pem- Download the correct root certificate from your provider
- Place the certificate where the client expects it
- Configure your client with sslmode and an absolute sslrootcert path
- Test the connection
wget https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem -O rds-ca-global.pemCertificate verify failed
What this error means
The "certificate verify failed" error occurs during the SSL/TLS handshake when a PostgreSQL client attempts to establish a secure connection to the server. When sslmode is set to "verify-ca" or "verify-full", the client (libpq, JDBC, psycopg, etc.) verifies that the server's certificate was signed by a trusted Certificate Authority (CA). If the CA certificate file is missing, the certificate has expired, or the certificate was issued by an authority the client does not trust, the connection fails with this error. This is a security mechanism designed to prevent man-in-the-middle attacks. The client refuses to connect to a server whose identity cannot be verified using a trusted root certificate, so the fix is to supply the correct trusted root CA rather than to weaken verification.
Typical symptoms
- PostgreSQL client (psql, JDBC, libpq, psycopg) fails to connect with a "certificate verify failed" error
- SSL/TLS connection errors appear in application logs or client output
- Connection succeeds with sslmode=require (no CA check) but fails with sslmode=verify-ca or sslmode=verify-full
- Error appears after a managed database provider rotates its root certificate
- Docker containers with PostgreSQL clients fail after certificate rotation or when ca-certificates is missing
Common causes
- The root certificate file (root.crt) is missing or not found in the expected location (~/.postgresql/root.crt on Linux/macOS, %APPDATA%\postgresql\root.crt on Windows)
- The provided sslrootcert path is wrong or unreadable — including using a literal '~' in a connection string, which libpq does not expand to your home directory
- The root certificate file is outdated and no longer matches the server's certificate chain (common when managed providers rotate CAs, e.g. AWS RDS rds-ca-2019 being superseded)
- The server's certificate was issued by a CA whose root certificate has not been downloaded and trusted yet
- In Docker or minimal base images, the ca-certificates package is not installed, so system trust roots are unavailable
- The root certificate file exists but has permissions that prevent the client process from reading it
How to fix "Certificate verify failed"
Obtain the root CA certificate from your database provider's official source. Always use the provider's documented download method rather than ad-hoc URLs.
For AWS RDS / Aurora PostgreSQL (use the current global bundle; rds-ca-2019 is expired/superseded):
wget https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem -O rds-ca-global.pemFor Azure Database for PostgreSQL (current servers chain to the DigiCert Global Root G2):
wget https://cacerts.digicert.com/DigiCertGlobalRootG2.crt.pem -O azure-root.pemFor Google Cloud SQL, the per-instance server CA is not available from a fixed wget URL. Download it via the gcloud CLI (or the Cloud Console > your instance > Connections > Security):
gcloud sql instances describe INSTANCE_NAME \
--format="value(serverCaCert.cert)" > cloudsql-server-ca.pemVerify the file is a valid certificate before using it:
openssl x509 -in root.crt -noout -text
openssl x509 -in root.crt -noout -datesOn Linux and macOS, libpq looks for ~/.postgresql/root.crt by default:
mkdir -p ~/.postgresql
chmod 700 ~/.postgresql
cp root.crt ~/.postgresql/root.crt
chmod 600 ~/.postgresql/root.crtOn Windows, the default location is:
%APPDATA%\postgresql\root.crtwhich typically resolves to C:\Users\<YourUsername>\AppData\Roaming\postgresql\root.crt. Create the postgresql directory if it does not exist.
If you place the file at the default path, libpq will use it automatically for verify-ca/verify-full without an explicit sslrootcert.
Use verify-full and point sslrootcert at the certificate. Important: libpq does NOT expand ~ in the sslrootcert connection parameter, so always use an absolute path (or the literal default-location keyword system on newer clients). Using sslrootcert=~/.postgresql/root.crt will silently fail to find the file.
For psql / libpq (use an absolute path):
psql "postgresql://user@host:5432/database?sslmode=verify-full&sslrootcert=/home/youruser/.postgresql/root.crt"If the cert is at the default ~/.postgresql/root.crt location, you can omit sslrootcert entirely:
psql "postgresql://user@host:5432/database?sslmode=verify-full"For JDBC (Java) — use an absolute, forward-slash path:
String url = "jdbc:postgresql://host:5432/database"
+ "?sslmode=verify-full"
+ "&sslrootcert=/home/youruser/.postgresql/root.crt";
// On Windows: sslrootcert=C:/Users/Username/AppData/Roaming/postgresql/root.crtFor psycopg / psycopg2 (Python) — expand the path yourself):
import os, psycopg2
conn = psycopg2.connect(
host="host",
dbname="database",
user="user",
sslmode="verify-full",
sslrootcert=os.path.expanduser("~/.postgresql/root.crt"),
)Avoid putting the password in the URL where possible; prefer a ~/.pgpass file, the PGPASSWORD environment variable, or your driver's credential mechanism.
Verify the connection now works with full verification (note the absolute path):
psql "postgresql://user@host:5432/database?sslmode=verify-full&sslrootcert=/home/youruser/.postgresql/root.crt"If successful, you should reach the PostgreSQL prompt. You can confirm the session is encrypted with:
SELECT ssl, version, cipher FROM pg_stat_ssl WHERE pid = pg_backend_pid();If the error persists, continue with the steps below.
Minimal base images often lack system trust roots and your provider CA. Install ca-certificates and copy the root certificate into the image:
FROM node:latest
RUN apt-get update \
&& apt-get install -y --no-install-recommends ca-certificates \
&& rm -rf /var/lib/apt/lists/*
# Ship the provider root CA with the image
COPY root.crt /etc/ssl/certs/postgres-ca.crt
# libpq reads these env vars; use an absolute path here too
ENV PGSSLROOTCERT=/etc/ssl/certs/postgres-ca.crt
ENV PGSSLMODE=verify-fullFor security, mount or bake in the specific provider CA rather than relying on connection-string overrides at runtime.
If issues persist, inspect the certificate and the chain the server actually presents:
# Check the root cert's validity dates
openssl x509 -in root.crt -noout -dates
# Inspect the chain the server sends (PostgreSQL uses STARTTLS-style negotiation,
# so use -starttls postgres)
openssl s_client -starttls postgres -connect host:5432 -showcertsIf the certificate has expired or was superseded by a provider rotation, download the current root certificate (Step 1) and replace your root.crt. If the server's chain is signed by a different CA than your file, you have the wrong root certificate.
Disabling verification removes protection against man-in-the-middle attacks, so treat this as a temporary diagnostic step only — never as a fix for production traffic.
The secure resolution is always to install the correct root CA (Steps 1-3). If you must confirm that a verification mismatch (not a network/auth problem) is the cause, you can briefly test with encryption but no CA check:
# sslmode=require encrypts but does NOT verify the server identity — diagnostic only
psql "postgresql://user@host:5432/database?sslmode=require"If this connects, the problem is specifically the trusted root certificate — go back to Step 1 and supply the correct CA, then switch back to sslmode=verify-full. Do not leave clients on require/disable in production.
Advanced notes
SSL modes explained: PostgreSQL supports "disable" (no encryption), "allow" (encryption optional), "prefer" (encryption preferred, falls back to unencrypted), "require" (encryption mandatory but no certificate verification), "verify-ca" (verifies the certificate chain), and "verify-full" (verifies the chain AND that the hostname matches the certificate's CN/SAN). For production, use "verify-full"; "require" still leaves you exposed to active man-in-the-middle attacks because it does not check the server's identity.
Path handling: libpq does not expand "~" in the sslrootcert parameter or in PGSSLROOTCERT, so always use an absolute path. Newer libpq clients (PostgreSQL 16+) also accept sslrootcert=system to use the operating system's default trust store, which is convenient when your server's CA is already a public root.
Certificate rotation: Managed providers periodically rotate root certificates. AWS RDS publishes a combined global-bundle.pem and has retired older bundles such as rds-ca-2019; Azure and Google Cloud SQL similarly update their roots. Subscribe to your provider's maintenance notices and refresh root.crt ahead of forced rotations.
Multiple root certificates: If you connect to servers chained to different CAs, concatenate the root certificates into one PEM file and point sslrootcert at it:
cat root1.crt root2.crt > combined_root.crtFrequently asked questions
- What does "Certificate verify failed" mean?
- The "certificate verify failed" error occurs during the SSL/TLS handshake when a PostgreSQL client attempts to establish a secure connection to the server. ) verifies that the server's certificate was signed by a trusted Certificate Authority (CA). If the CA certificate file is missing, the certificate has expired, or the certificate was issued by an authority the client does not trust, the connection fails with this error.
- How do I fix "Certificate verify failed"?
- PostgreSQL "certificate verify failed" is an SSL/TLS error that occurs when the client cannot verify the server's certificate. It usually means the root CA certificate is missing, outdated, or misconfigured.
- What causes "Certificate verify failed"?
- The root certificate file (root.crt) is missing or not found in the expected location (~/.postgresql/root.crt on Linux/macOS, %APPDATA%\postgresql\root.crt on Windows) The provided sslrootcert path is wrong or unreadable — including using a literal '~' in a connection string, which libpq does not expand to your home directory The root certificate file is outdated and no longer matches the server's certificate chain (common when managed providers rotate CAs, e.g. AWS RDS rds-ca-2019 being superseded)
- Is the "Certificate verify failed" error serious?
- It is a medium-severity error — more of a blocker than a risk. The steps above resolve it.
Official resources & further reading
- 📚PostgreSQL Official Documentation
- 📖PostgreSQL Error Codes(Official Docs)
- 📖PostgreSQL SSL/TLS Support(Official Docs)