Database

Connect your application to the database

This guide shows how to connect an application to Zenifra PostgreSQL or MariaDB with the most common Python, Node.js, and Bun drivers. Every connection uses TLS and verifies the database certificate and hostname.

Before you start

  1. Open the database in the Console and copy the connection URI. The full password is shown only at creation or after a password rotation.
  2. In the application project, open Environment Variables and create DATABASE_URL with that URI.
  3. Always use the host shown in the Console, in the postgresql-<id>.managed.zenifra.com or mariadb-<id>.managed.zenifra.com format. The certificate is issued for that name, so connections through the IP address fail verification.

Do not turn off certificate verification to "make it work": do not use sslmode=require, sslmode=disable, rejectUnauthorized: false, or equivalent options. The adjustments below solve the known cases while keeping full verification.

Summary by driver

DatabaseLanguageDriverWhat to do
PostgreSQLTerminalpsqlUse the Console URI as is
PostgreSQLPythonpsycopg / psycopg2 (binary packages)Set SSL_CERT_FILE
PostgreSQLPythonasyncpgRemove sslrootcert from the URI and pass a TLS context
PostgreSQLNode.js and Bunpg (node-postgres)Remove &sslrootcert=system from the URI
PostgreSQLNode.js and Bunpostgres (postgres.js)Use the Console URI as is
PostgreSQLBunBun.sqlUse the Console URI as is
MariaDBNode.jsmariadbUse the Console URI as is
MariaDBNode.js and Bunmysql2Configure ssl as an object, with verifyIdentity
MariaDBPythonPyMySQLProvide the CA bundle and enable identity verification

PostgreSQL

The PostgreSQL URI in the Console ends with ?sslmode=verify-full&sslrootcert=system. These parameters require TLS, verify the certificate chain against the system trust store, and check the hostname. Some drivers do not understand sslrootcert=system and need the adjustments below.

psql

psql "$DATABASE_URL"

psql versions that do not support sslrootcert=system accept the path of the system CA bundle instead of system, for example sslrootcert=/etc/ssl/certs/ca-certificates.crt.

Python with psycopg or psycopg2

The psycopg[binary] and psycopg2-binary packages ship their own TLS library, which cannot find the system certificates. Without an adjustment, the connection fails with SSL error: certificate verify failed.

Set the environment variable below in the project. It points the driver to the system CA bundle, and the URI stays the same:

SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt
import os
import psycopg

with psycopg.connect(os.environ["DATABASE_URL"]) as conn:
    conn.execute("SELECT 1")

Python applications published on Zenifra without their own Dockerfile already get this variable. If the application was published before 2026-10-21, run a new deploy. With your own Dockerfile, add ENV SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt and make sure the image installs the ca-certificates package. Django and SQLAlchemy use psycopg or psycopg2 under the hood and follow the same rule.

Python with asyncpg

asyncpg treats sslrootcert=system as a file name and fails with FileNotFoundError. Remove sslrootcert from the URI and pass a TLS context that verifies the certificate and hostname:

import os
import ssl
import asyncpg

dsn = os.environ["DATABASE_URL"].split("?")[0]
conn = await asyncpg.connect(dsn, ssl=ssl.create_default_context())

Node.js and Bun with pg (node-postgres)

pg treats sslrootcert=system as a file name and fails with ENOENT: no such file or directory, open 'system'. Remove &sslrootcert=system and keep sslmode=verify-full. The driver then verifies the certificate and hostname with the CAs bundled with Node.js:

DATABASE_URL=postgresql://app:<password>@<host>:<port>/app?sslmode=verify-full
import pg from 'pg'

const client = new pg.Client({ connectionString: process.env.DATABASE_URL })
await client.connect()

Node.js and Bun with postgres.js

import postgres from 'postgres'

const sql = postgres(process.env.DATABASE_URL)
await sql`SELECT 1`

Bun with Bun.sql

const sql = new Bun.SQL(process.env.DATABASE_URL)
await sql`SELECT 1`

MariaDB

The MariaDB URI in the Console uses the mariadb://app:<password>@<host>:<port>/app?ssl=true format.

Node.js with mariadb

import mariadb from 'mariadb'

const conn = await mariadb.createConnection(process.env.DATABASE_URL)
await conn.query('SELECT 1')

On Bun, use mysql2 instead of the mariadb connector.

Node.js and Bun with mysql2

mysql2 does not accept ssl=true in the URI and fails with SSL profile must be an object. Pass the connection details as an object. verifyIdentity: true is required for the driver to check the certificate hostname:

import mysql from 'mysql2/promise'

const url = new URL(process.env.DATABASE_URL)
const conn = await mysql.createConnection({
  host: url.hostname,
  port: Number(url.port),
  user: decodeURIComponent(url.username),
  password: decodeURIComponent(url.password),
  database: url.pathname.slice(1),
  ssl: { rejectUnauthorized: true, verifyIdentity: true },
})

Python with PyMySQL

import os
from urllib.parse import unquote, urlsplit

import pymysql

url = urlsplit(os.environ["DATABASE_URL"])
conn = pymysql.connect(
    host=url.hostname,
    port=url.port,
    user=unquote(url.username),
    password=unquote(url.password),
    database=url.path.lstrip("/"),
    ssl_ca="/etc/ssl/certs/ca-certificates.crt",
    ssl_verify_cert=True,
    ssl_verify_identity=True,
)

The /etc/ssl/certs/ca-certificates.crt path exists in Debian, Ubuntu, and Alpine images with the ca-certificates package installed. On other systems, use the matching CA bundle.

Common errors

MessageCauseHow to fix
SSL error: certificate verify failedA Python driver with its own TLS library cannot find the system CAsSet SSL_CERT_FILE as in psycopg or psycopg2
ENOENT: no such file or directory, open 'system'pg treats sslrootcert=system as a fileRemove &sslrootcert=system from the URI
FileNotFoundError in asyncpgasyncpg treats sslrootcert=system as a fileRemove sslrootcert and pass ssl=ssl.create_default_context()
SSL profile must be an objectmysql2 does not accept ssl=true in the URIPass ssl as an object
Hostname/IP does not match certificate's altnames or IP address mismatchThe connection uses an IP or another name instead of the Console hostUse the exact host shown in the Console
password authentication failed or Access deniedThe password in DATABASE_URL is not the current oneCheck the password or generate a new one in the database settings and update the application variable

Next steps

Last updated on

On this page