Hi all,
I’m deploying a Node.js app on DigitalOcean App Platform that connects to a DigitalOcean-managed PostgreSQL database. I’m using drizzle-orm for database access.
I’m consistently getting this error during login/auth operations:
Error: self-signed certificate in certificate chain at /workspace/node_modules/pg-pool/index.js:45:11 ... code: 'SELF_SIGNED_CERT_IN_CHAIN'
#What I’ve already tried:
digitalocean-ca.crt) from the database “Connection” page../certs/digitalocean-ca.crt in my repo.db.ts, I’m reading and injecting it like this:import { drizzle } from 'drizzle-orm/node-postgres';
import { Pool } from 'pg';
import fs from 'fs';
import path from 'path';
const certPath = path.resolve(__dirname, '../../certs/digitalocean-ca.crt');
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
ssl: {
rejectUnauthorized: true,
ca: fs.readFileSync(certPath).toString(),
},
});
export const db = drizzle(pool);
fs.existsSync()).sslmode=disable or ?ssl=false I am using sslmode=enable).rejectUnauthorized: false, everything works — but that’s insecure and not an option for production.So my question is: Firstly, why is there not an option to simply disable the SSL requirement? I don’t want the communication between the database server and my app to be secured. I’ve already set trusted apps, so it doesn’t need an SSL layer as well, in my opinion.
exact error
[AUTH] Login error after 80 ms: Error: self-signed certificate in certificate chain
[layerr-app-prod] [2025-06-26 05:39:58] at /workspace/node_modules/pg-pool/index.js:45:11
[layerr-app-prod] [2025-06-26 05:39:58] at process.processTicksAndRejections (node:internal/process/task_queues:105:5)
[layerr-app-prod] [2025-06-26 05:39:58] at async file:///workspace/node_modules/drizzle-orm/node-postgres/session.js:83:22
[layerr-app-prod] [2025-06-26 05:39:58] at async DatabaseStorage.getUserByEmail (file:///workspace/dist/index.js:3306:22)
[layerr-app-prod] [2025-06-26 05:39:58] at async file:///workspace/dist/index.js:4786:20 {
[layerr-app-prod] [2025-06-26 05:39:58] code: 'SELF_SIGNED_CERT_IN_CHAIN'
Thanks in advance.
Aasim
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!
Accepted Answer
I’m posting this here to save someone else hours of their life.
For whatever reason, do not attempt to connect to a DigitalOcean managed Postgres database using a single connection string. Doing so breaks SSL validation and will cause you to repeatedly receive:
SELF_SIGNED_CERT_IN_CHAIN
Instead, you must decompose the connection string into individual connection parameters. When done this way, SSL validation works correctly.
pg Connection (Node.js)This configuration works properly when using pg in Node.js:
import { Pool } from "pg";
export const pool = new Pool({
host: process.env.DB_HOST,
port: parseInt(process.env.DB_PORT || "25060"),
database: process.env.DB_NAME,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
ssl: ca ? { rejectUnauthorized: true, ca } : undefined,
max: 10, // Maximum connections in pool
idleTimeoutMillis: 30000, // Close idle connections after 30s
connectionTimeoutMillis: 5000 // Timeout for new connections
});
In a DigitalOcean Web App, pass all values via environment variables, including the CA certificate.
⚠️ Important: Use the database name of the managed Postgres instance, not the name of a database you created inside that instance.
DB_CA_CERT=${your_db_name.CA_CERT}
DB_SSL=true
DB_HOST=${your_db_name.HOSTNAME}
DB_PORT=${your_db_name.PORT}
DB_NAME=${your_db_name.DATABASE}
DB_USER=${your_db_name.USERNAME}
DB_PASSWORD=${your_db_name.PASSWORD}
❌ Do not use a Postgres connection string with DigitalOcean managed databases
✅ Break the connection into individual parameters
✅ Pass the CA cert explicitly
✅ Use the managed database name, not a child database name
🚫 Avoid SELF_SIGNED_CERT_IN_CHAIN errors entirely
If this saves even one person a night of debugging, it did its job.
Hey Aasim,
When you attach a Managed PostgreSQL database to your App Platform service, DigitalOcean automatically injects the required environment variables, including DATABASE_URL and DATABASE_CA_CERT.
You can check out the answers from this thread a couple of years ago as well:
So instead of bundling the .crt file in your repo and reading it from disk, just reference the injected DATABASE_CA_CERT directly in your code:
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
ssl: {
rejectUnauthorized: true,
ca: process.env.DATABASE_CA_CERT,
},
});
This avoids filesystem issues and is the recommended approach for App Platform deployments.
As for disabling SSL: that’s not possible with DigitalOcean Managed Databases, SSL is always enforced, even for trusted apps.
- Bobby
Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.
Full documentation for every DigitalOcean product.
The Wave has everything you need to know about building a business, from raising funding to marketing your product.
Stay up to date by signing up for DigitalOcean’s Infrastructure as a Newsletter.
New accounts only. By submitting your email you agree to our Privacy Policy
Scale up as you grow — whether you're running one virtual machine or ten thousand.
Sign up and get $200 in credit for your first 60 days with DigitalOcean.*
*This promotional offer applies to new accounts only.