PostgreSQL
Tested with: PostgreSQL 17.11 and 18.6 on Databasezy (CloudNativePG 1.27) · zb CLI 0.1
PostgreSQL is the general-purpose relational engine on Databasezy: SQL with transactions, JSONB, rich indexing and a large extension and driver ecosystem. Pick it when you are not sure which engine you need.
Overview
Section titled “Overview”Each instance is a PostgreSQL cluster managed by CloudNativePG. It starts with a single primary; on paid plans you
can add a standby for high availability (--ha) and read replicas (--replicas). The newest supported major is the
default. Minor (patch) releases are not yet applied to running instances on a schedule.
| Status | Available |
|---|---|
| Category | Relational |
| Versions | 18, 17, 16, 15 (newest is the default) |
| Protocol and port | PostgreSQL wire protocol on 5432 |
| Runtime | Operator-backed (CloudNativePG) |
| Backups | barman-cloud base backups + WAL |
| Point-in-time recovery | Yes |
| Pause | Scale to zero |
| Free plan | Yes (size f0) |
| Features | Built-in transaction-mode pooler, Extensions, Read replicas, High availability (standby) |
| Licence | PostgreSQL |
Extensions
Section titled “Extensions”The image is CloudNativePG’s PostgreSQL image: the contrib modules plus pgvector, pgaudit and
pg_failover_slots. PostGIS and pg_cron are not included.
The generated user can create the extensions PostgreSQL marks as trusted, for example:
create extension if not exists pgcrypto;The trusted ones are pgcrypto, uuid-ossp, hstore, citext, pg_trgm, btree_gin, btree_gist, ltree,
unaccent, fuzzystrmatch, cube, intarray, isn, lo, seg, tablefunc, tcn, dict_int,
tsm_system_rows and tsm_system_time. vector (pgvector), pg_stat_statements and pgaudit are in the image
but only a superuser can create them, and you cannot request them for an instance yet. pg_available_extensions
lists everything in the image, including the extensions you cannot create.
Create an instance
Section titled “Create an instance”- Open the portal and choose New instance.
- Pick PostgreSQL and a version (18, 17, 16, 15).
- Choose a size and region. On the Free plan the size is f0. Secure placement is listed only after your organization has signed the BAA.
- Check the hourly price and monthly estimate, then confirm. The Connect tab fills in when the instance is ready.
zb instances create --engine postgres --engine-version 18 \ --size s1 --region us-east --name postgres-demo --wait# On the Free plan, use --size f0
# Reveal the credentials once and store them in your secret managerzb instances credentials reveal postgres-democurl -sS https://api.databasezy.com/v1/orgs/$ZB_ORG/projects/$ZB_PROJECT/instances \ -H "Authorization: Bearer $ZB_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "engine": "postgres", "engine_version": "18", "size": "s1", "region": "us-east", "name": "postgres-demo"}'
The response is 201 with the instance in status requested, or 202 when a
team approval policy applies. See the REST API reference.
Connect
Section titled “Connect”Every snippet uses sslmode=verify-full or the driver’s equivalent. libpq-based drivers do not read the operating
system’s trust store, so they get the CA bundle path explicitly.
PostgreSQL listens on port 5432 (PostgreSQL wire protocol). Versions: 18, 17, 16, 15. Replace the example host with the one on your instance's Connect tab; credentials are shown once at creation.
postgresql://app:<password>@pg-7f3k.us-east.databasezy.com:5432/app?sslmode=verify-full&sslrootcert=databasezy-ca.pemimport { readFileSync } from "node:fs";import { Pool } from "pg";
const host = "pg-7f3k.us-east.databasezy.com";export const pool = new Pool({ host, port: 5432, database: "app", user: "app", password: process.env.ZB_PASSWORD, ssl: { ca: readFileSync("databasezy-ca.pem", "utf8"), rejectUnauthorized: true, // verify chain servername: host, // verify hostname (verify-full) }, max: 10, // keep well under the size's max_connections idleTimeoutMillis: 30_000,});
const { rows } = await pool.query("select version()");console.log(rows[0]);Tested with: PostgreSQL 18 · node-postgres (pg) 8.13
import osfrom psycopg_pool import ConnectionPool
conninfo = ( "host=pg-7f3k.us-east.databasezy.com port=5432 dbname=app user=app " f"password={os.environ['ZB_PASSWORD']} " "sslmode=verify-full sslrootcert=databasezy-ca.pem")pool = ConnectionPool(conninfo, min_size=1, max_size=10)
with pool.connection() as conn: print(conn.execute("select version()").fetchone())Tested with: PostgreSQL 18 · psycopg 3.2 · psycopg_pool 3.2
package main
import ( "context" "fmt" "os"
"github.com/jackc/pgx/v5/pgxpool")
func main() { ctx := context.Background() dsn := fmt.Sprintf( "postgres://app:%s@pg-7f3k.us-east.databasezy.com:5432/app?sslmode=verify-full&sslrootcert=databasezy-ca.pem&pool_max_conns=10", os.Getenv("ZB_PASSWORD"), ) pool, err := pgxpool.New(ctx, dsn) if err != nil { panic(err) } defer pool.Close()
var v string if err := pool.QueryRow(ctx, "select version()").Scan(&v); err != nil { panic(err) } fmt.Println(v)}Tested with: PostgreSQL 18 · pgx v5.7
use sqlx::postgres::{PgConnectOptions, PgPoolOptions, PgSslMode};
#[tokio::main]async fn main() -> Result<(), sqlx::Error> { let opts = PgConnectOptions::new() .host("pg-7f3k.us-east.databasezy.com") .port(5432) .database("app") .username("app") .password(&std::env::var("ZB_PASSWORD").expect("ZB_PASSWORD")) .ssl_mode(PgSslMode::VerifyFull) .ssl_root_cert("databasezy-ca.pem");
let pool = PgPoolOptions::new().max_connections(10).connect_with(opts).await?; let (v,): (String,) = sqlx::query_as("select version()").fetch_one(&pool).await?; println!("{v}"); Ok(())}Tested with: PostgreSQL 18 · sqlx 0.8 (runtime-tokio, tls-rustls)
import com.zaxxer.hikari.HikariConfig;import com.zaxxer.hikari.HikariDataSource;
public final class Db { public static HikariDataSource open() { var cfg = new HikariConfig(); cfg.setJdbcUrl("jdbc:postgresql://pg-7f3k.us-east.databasezy.com:5432/app" + "?sslmode=verify-full&sslrootcert=databasezy-ca.pem"); cfg.setUsername("app"); cfg.setPassword(System.getenv("ZB_PASSWORD")); cfg.setMaximumPoolSize(10); return new HikariDataSource(cfg); }}Tested with: PostgreSQL 18 · PostgreSQL JDBC 42.7 · HikariCP 6.2
using Npgsql;
var cs = new NpgsqlConnectionStringBuilder{ Host = "pg-7f3k.us-east.databasezy.com", Port = 5432, Database = "app", Username = "app", Password = Environment.GetEnvironmentVariable("ZB_PASSWORD"), SslMode = SslMode.VerifyFull, RootCertificate = "databasezy-ca.pem", MaxPoolSize = 10,};await using var dataSource = NpgsqlDataSource.Create(cs);await using var cmd = dataSource.CreateCommand("select version()");Console.WriteLine(await cmd.ExecuteScalarAsync());Tested with: PostgreSQL 18 · Npgsql 9.0
<?php$dsn = "pgsql:host=pg-7f3k.us-east.databasezy.com;port=5432;dbname=app;" . "sslmode=verify-full;sslrootcert=databasezy-ca.pem";$pdo = new PDO($dsn, "app", getenv("ZB_PASSWORD"), [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_PERSISTENT => true, // reuse connections across requests]);echo $pdo->query("select version()")->fetchColumn();Tested with: PostgreSQL 18 · PHP 8.3 · PDO pgsql
require "pg"
conn = PG.connect( host: "pg-7f3k.us-east.databasezy.com", port: 5432, dbname: "app", user: "app", password: ENV.fetch("ZB_PASSWORD"), sslmode: "verify-full", sslrootcert: "databasezy-ca.pem")puts conn.exec("select version()").firstTested with: PostgreSQL 18 · pg 1.5 · Ruby 3.3
# Connection URI (copy from Instance → Connect in the portal)export DATABASE_URL='postgresql://app:<password>@pg-7f3k.us-east.databasezy.com:5432/app?sslmode=verify-full&sslrootcert=databasezy-ca.pem'
psql "$DATABASE_URL"# or with zb (spawns psql with TLS flags and a one-time credential)zb connect pg-7f3kTested with: PostgreSQL 18 · psql 17
The PostgreSQL connection guide goes deeper on TLS, pooling and the allow-list. For serverless and edge runtimes, turn on the transaction-mode pooler under Connect → Pooler and read connection pooling first. Framework guides: Prisma, Drizzle, Django, SQLAlchemy, Rails, Laravel, Spring, Phoenix, Next.js.
See Connecting to Databasezy for host names, the CA bundle and the allow-list, which work the same for every engine.
Migrate in
Section titled “Migrate in”You can bring an existing PostgreSQL database in from these sources. Each links to a step-by-step guide.
| Source | How | Continuous sync | Guide status |
|---|---|---|---|
| Neon | Connection string | Yes | Available |
| Supabase | Connection string | Yes | Available |
| Amazon RDS and Aurora | Connection string | Yes | Coming soon · phase 2 |
| Render | Connection string | Yes | Coming soon · phase 2 |
| Railway | Connection string | Yes | Coming soon · phase 2 |
| Heroku | Connection string, File upload | No | Coming soon · phase 2 |
| Fly.io | Connection string, Local tools (zb migrate --from local) | Yes | Coming soon · phase 2 |
| Aiven | Connection string | Yes | Coming soon · phase 2 |
| DigitalOcean Managed Databases | Connection string | Yes | Coming soon · phase 2 |
| Crunchy Bridge | Connection string | Yes | Coming soon · phase 2 |
| Vercel Postgres (legacy, now Neon) | Connection string | Yes | Coming soon · phase 2 |
| Prisma Postgres | Connection string | No | Coming soon · phase 2 |
| Xata | Connection string | No | Coming soon · phase 3 |
| Netlify DB | Connection string | Yes | Coming soon · phase 2 |
| Google Cloud SQL | Connection string | Yes | Coming soon · phase 3 |
| Azure Database | Connection string | Yes | Coming soon · phase 3 |
| Self-hosted server | Connection string, Local tools (zb migrate --from local) | Yes | Available |
| Local files and dumps | File upload, Local tools (zb migrate --from local) | No | Available |
| Docker container | Local tools (zb migrate --from local) | No | Available |
| Another Databasezy instance | Instance to instance | Yes | Coming soon · phase 2 |
PostgreSQL sources can use continuous sync: logical replication keeps the target current after the copy, so cutover takes seconds. Moving to a new major version is an instance-to-instance migration; see engine version upgrades.
How migrations work explains preflight, verification and cutover.
Backups and restore
Section titled “Backups and restore”CloudNativePG takes barman-cloud base backups and archives the write-ahead log continuously to object storage; point-in-time recovery replays that log up to the moment you choose.
PostgreSQL backups use barman-cloud base backups + WAL. They run inside the instance's namespace, stream straight to the cell's object storage and are checksummed on upload. How often they run and how long they are kept follows your plan's backup policy. A backup is always taken before a resize or a version upgrade.
Point-in-time recovery is supported: you can restore to any moment inside the PITR window of your plan or the PITR add-on. Restores create a new instance by default and leave the original untouched; an in-place restore asks you to type the instance name and takes a pre-change backup first.
zb backups create postgres-demo --label before-release # manual snapshotzb backups list postgres-demozb backups restore postgres-demo <backup-id> --name postgres-demo-restore
# Point in time (RFC 3339) inside the PITR windowzb backups pitr-window postgres-demozb backups restore postgres-demo <backup-id> --at "2026-09-27T08:15:00Z" --name postgres-demo-pitrPause and scale to zero
Section titled “Pause and scale to zero”PostgreSQL can scale to zero. A paused instance has no running pods and bills no compute; its storage and backups are kept and billed as usual. Connections are refused until you resume it. On the Free plan an instance pauses by itself after 15 minutes without connections and wakes on the next one; the gateway holds that connection for up to 30 seconds while it starts.
zb instances pause postgres-demozb instances resume postgres-demoSee Pause and resume for schedules, wake times and billing while paused.
Limits and sizes
Section titled “Limits and sizes”PostgreSQL runs on every size, including the Free plan's f0. The size sets the CPU, memory, storage ceiling and connection limit; the gateway refuses connections over the limit with a protocol error. Storage grows in steps up to the ceiling, and you can resize at any time.
| Size | vCPU | Memory | Max storage | Max connections | ≈ $ / month |
|---|---|---|---|---|---|
f0 | 0.063 | 512 MiB | 1 GB | 20 | Free |
s0 | 0.25 | 1 GiB | 20 GB | 60 | $10 |
s1 | 0.5 | 2 GiB | 50 GB | 100 | $15 |
s2 | 1 | 4 GiB | 200 GB | 200 | $60 |
m2 | 2 | 8 GiB | 500 GB | 400 | $110 |
m4 | 4 | 16 GiB | 1 TB | 800 | $210 |
l8 | 8 | 32 GiB | 4 TB | 1,500 | $410 |
l16 | 16 | 64 GiB | 8 TB | 3,000 | $960 |
xl32 | 32 | 128 GiB | 16 TB | 5,000 | $1,870 |
Full details, including hourly prices and burst CPU, are in the size catalogue; plan quotas are in limits and quotas.
Security
Section titled “Security”Every instance has one database user, the generated app. It owns the app database and its public schema:
it can create schemas, tables and the trusted extensions, and grant privileges on the objects it owns. It is not a
superuser and cannot create other roles or databases (CloudNativePG creates the database owner without
CREATEROLE or CREATEDB), so your application, migrations and reporting tools all connect as app. Separate
users per tool are not available yet; use schemas to keep their objects apart, and rotate the password if it
leaks.
- TLS on every connection. TLS 1.2 is the minimum and TLS 1.3 is preferred; plaintext is never
offered. Verify the server, not just the encryption: use
sslmode=verify-full. See TLS and the CA bundle. - IP allow-list. The gateway checks the client address before authentication, on every plan.
Manage it under Network → Allow-list in the portal or with
PUT /v1/orgs/{org}/instances/{id}/network. - Credentials. Generated inside the cell, shown once, never stored by the control plane. Rotate them with an overlap window so nothing breaks.
- Secure hosting. Secure placement (HIPAA-ready) is a per-instance option once your organization has signed the BAA: dedicated nodes, customer-managed keys and immutable backups. See Secure hosting and the BAA.
- Staff access. Databasezy staff cannot read your data without a grant you issue. See data confidentiality.
Which extensions can I install?
Section titled “Which extensions can I install?”The trusted ones listed under Extensions: run create extension <name> as the generated user. vector, pg_stat_statements and pgaudit are in the image but need a superuser, and PostGIS and pg_cron are not available. When you migrate in, preflight lists extensions on the source that Databasezy does not ship, before anything is copied.
How do I move to a newer major version?
Section titled “How do I move to a newer major version?”Create a new instance on the newer version and run zb migrate --from <old> --to <new> --mode copy_and_sync. Test against the new instance while sync runs, then cut over. A running instance never changes major version on its own.
Can I create more database users?
Section titled “Can I create more database users?”Not yet. The generated app user cannot create roles, so every client connects as app. See Security.
Should I use the pooler or my own pool?
Section titled “Should I use the pooler or my own pool?”Long-running services should keep their own pool and stay under the size’s connection limit. Serverless functions and edge runtimes should use the built-in transaction-mode pooler, keeping in mind that session state such as LISTEN and session-level prepared statements does not carry across transactions.
Can I run read replicas?
Section titled “Can I run read replicas?”Yes, on paid plans. Pass --replicas <n> (and --ha for a standby) when you create the instance.