Managed PostgreSQL

Highly-available Postgres, without the babysitting

A database cluster is one or more member VMs, each running its own PostgreSQL with a full local copy of the data. Patroni handles automatic leader election and failover, and a managed load balancer gives you one stable :5432 endpoint that always points at the current primary — no client reconfiguration when a member dies.

create a cluster, then connect
# In the dashboard: Console -> Clusters -> New cluster
#   Mode:    Database
#   Engine:  PostgreSQL
#   Version: 16
#   Nodes:   3        (1 = single, non-HA · 3+ = HA · 2 is not allowed)
#   Plan:    BXS.s1   (each member runs this plan)

# Then connect to the single stable endpoint — writes always
# reach whichever member is the current primary (the superuser
# is 'postgres'; its password is revealed on the cluster page):
psql "postgres://postgres:<password>@203.0.113.10:5432/appdb?sslmode=require"

Quickstart

Create the cluster in the dashboard (Console → Clusters → New cluster), pick Database mode and PostgreSQL, choose your node count, and connect to the single endpoint. The endpoint, superuser and database tools all live on the cluster page.

from create to connected
# In the dashboard: Console -> Clusters -> New cluster
#   Mode:    Database
#   Engine:  PostgreSQL
#   Version: 16
#   Nodes:   3        (1 = single, non-HA · 3+ = HA · 2 is not allowed)
#   Plan:    BXS.s1   (each member runs this plan)

# Then connect to the single stable endpoint — writes always
# reach whichever member is the current primary (the superuser
# is 'postgres'; its password is revealed on the cluster page):
psql "postgres://postgres:<password>@203.0.113.10:5432/appdb?sslmode=require"

When to use it

High availability is worth paying for when a database outage would hurt. When it would not, a single managed-DB instance is simpler and cheaper — reach for the cluster when you need failover.

Use a cluster (3+ nodes) when downtime hurts

Production databases that must survive a member VM crashing, a Postgres process dying, or a bad minor upgrade. If a primary going down would page you, you want automatic failover and a stable endpoint that follows it.

A single managed-DB instance is often enough

For dev, staging, side projects and low-stakes workloads, the built-in databases on a single instance are simpler and cheaper. There is no automatic failover, but you also pay for one VM instead of three plus a load balancer.

Start single, grow into HA

A one-node cluster is a normal managed Postgres with the same endpoint, superuser and database tooling — just no replica to fail over to. Scale it to three members later when the workload earns HA.

Create a cluster

Three choices decide the shape of the cluster: the engine, the version, and how many members. The one rule to remember is the node count.

1

Pick the mode and engine

Create a new cluster and choose Database mode, then PostgreSQL as the engine. Clusters run PostgreSQL 16 today. This is a database cluster, not a container cluster — Suble runs and supervises Postgres for you.

mode -> engine -> version
Mode:    Database
Engine:  PostgreSQL
Version: 16

2

Choose the node count

One node is a single, non-HA database. Three or more nodes give you high availability: one primary and N-1 replicas. Two nodes is deliberately not allowed — a two-member group has no safe quorum to elect a leader from.

the 1-or-3+ rule
Nodes: 3     # 1 = single (no failover)
             # 3 = HA (1 primary + 2 replicas)
             # 2 = not allowed (no safe quorum)

3

Size the members and create

Every member runs the same plan and gets its own full copy of the data — there is no shared or network storage. Suble provisions the members on a private network, a load balancer for the endpoint, wires up replication, and hands you a connection string.

provision the cluster
Plan: BXS.s1     # each member runs this plan
# -> members + load balancer + private network

Why not two nodes? A cluster elects its leader by majority. With two members, a single failure leaves one vote — never a majority — so the group cannot safely agree on a new primary. That is why Suble allows 1 (single, no failover) or 3 or more (real HA), but never 2.

Connect

You connect to the load balancer's public IP on :5432 with sslmode=require. The LB is a TCP passthrough, so TLS runs end-to-end between your client and Postgres. The members themselves are private-only — you never address them directly.

psql / DATABASE_URL
# Same endpoint whether you have 1 member or 5 — it always
# lands on the current primary. TLS is required end-to-end.
# Admin as the 'postgres' superuser (password on the cluster page):
psql "postgres://postgres:<password>@203.0.113.10:5432/appdb?sslmode=require"

# For your app, connect as a least-privilege user you created,
# not the superuser (libpq / most drivers):
DATABASE_URL="postgres://appuser:<password>@203.0.113.10:5432/appdb?sslmode=require"

Reveal the superuser

A superuser password is generated for you and revealed on demand — owner-only. Use it for administration and to bootstrap your application's own users.

Databases & users

Create application databases and per-database users from the dashboard — or with SQL over the same connection. Give each app a least-privilege user, not the superuser.

bootstrap a database + app user
-- After revealing the superuser (owner-only) you can create
-- app databases and least-privilege users from the dashboard,
-- or with plain SQL over the same connection:

CREATE DATABASE appdb;
CREATE USER appuser WITH PASSWORD 'a-strong-secret';
GRANT ALL PRIVILEGES ON DATABASE appdb TO appuser;

How failover works

Every member runs Postgres plus Patroni, and each keeps a full local copy of the data kept in sync by streaming replication. Patroni elects one member as the primary; the others are replicas. The load balancer only ever routes writes to whoever is currently the leader.

Member VMs, shared-nothing

Each member is a real instance running its own Postgres with a FULL local copy of the data. Nothing is shared over the network — no shared volume, no SAN. Members replicate to each other with Postgres streaming replication.

Patroni for failover

Patroni supervises Postgres on every member and runs the leader election (using its built-in raft — no external etcd to operate). If the primary VM or process dies, Patroni promotes the healthiest replica in seconds.

One stable endpoint

A managed load balancer exposes a single :5432 endpoint. HAProxy runs an active health check against each member's Patroni /primary REST endpoint — only the current leader passes — so every write is routed to the primary, and the endpoint auto-follows a failover.

Private members, TLS to the edge

The member VMs are private-only; you reach the database through the load balancer's public IP. The LB is a TCP passthrough, so TLS is end-to-end from your client to Postgres — connect with sslmode=require.

clients → LB :5432 → the current primary
Your app
   │  postgres://postgres:***@<lb-ip>:5432/appdb?sslmode=require
   ▼
┌──────────── Managed Load Balancer · TCP :5432 ────────────┐
│  HAProxy — active check on each member's Patroni /primary  │
│  only the current leader answers 200 -> writes go there    │
└──────┬───────────────────┬────────────────────┬───────────┘
       │ private network    │                    │
       ▼                    ▼                    ▼
  member-1 (PRIMARY)    member-2 (replica)   member-3 (replica)
  Postgres + Patroni    Postgres + Patroni   Postgres + Patroni
  full local copy       full local copy      full local copy
       └──────── streaming replication ──────────►

The routing trick is a single health check: HAProxy polls each member's Patroni /primary REST endpoint, and only the current leader answers 200. So the :5432 endpoint always resolves to the primary, and it follows a failover automatically.

what happens when the primary dies
# Before: member-1 is primary, the LB routes :5432 -> member-1
# member-1 crashes ↓

# 1. Patroni notices the leader is gone and elects a new one
# 2. member-2 is promoted to PRIMARY (seconds)
# 3. member-2 now answers 200 on /primary; member-1 does not
# 4. HAProxy's health check moves :5432 traffic to member-2

# Your client: one dropped connection, then reconnect to the
# SAME endpoint — no host change, no config edit. New writes
# land on member-2. The dashboard + Activity tab record the
# role change and a notification fires.

What your client sees: one dropped connection, then a normal reconnect to the same endpoint— no host change and no config edit. Use a driver that reconnects (most pools do) and a failover is a brief blip, not an outage. The role change lands in the Activity tab and fires a notification, and the dashboard shows each member's role and replication lag.

Configuration

Beyond the node count and plan, you can tune the postgresql.parameters that actually change behavior — connection limits, memory, WAL sizing, timeouts — from the cluster's Settings tab or the CLI.

An allow-list of real GUCs

max_connections, shared_buffers, work_mem, WAL sizing, statement and idle-in-transaction timeouts, and more — the parameters that actually change behavior. Managed settings (replication, TLS, listen addresses, pg_hba) aren't on the list, so a cluster can't be misconfigured into an unsafe state.

Reload live, or a leader-last restart

Most keys (like work_mem) apply the moment you save — Patroni pushes them to every member via patronictl edit-config, no restart. A few (like max_connections, shared_buffers) genuinely need Postgres to restart to take effect — those roll one member at a time, replicas first and the primary last, so writes are never interrupted.

Sticks through scale, resize and self-heal

Your settings are stored on the cluster, not just the running members — so a replica added by scaling up, a member replaced during a resize, or a self-healed replacement all come up already carrying your configuration.

tune it from the dashboard or the CLI
# Cluster page -> Settings tab -> Configuration
#   Max connections:  200     (default: 100 — needs a restart)
#   Work mem:         8MB     (default: 4MB — applies live)

# Or from the CLI:
suble cluster config get my-postgres
suble cluster config set my-postgres work_mem=8MB
suble cluster config set my-postgres max_connections=200
#   -> restart-class change: replicas restart first, the
#      primary last -- no write outage, a brief failover

# Reset a key to its default by setting it to an empty value:
suble cluster config set my-postgres work_mem=

Backups & point-in-time recovery

Replication keeps you online; backups let you go back in time. Suble runs pgBackRest against your own S3 bucket — scheduled full backups plus continuous WAL archiving — so you can restore the cluster to any second in its retention window. HA and backups are different jobs: a replica faithfully copies a dropped table, a backup lets you recover from before the drop.

Your own S3 bucket

Point backups at any S3-compatible bucket — AWS, Cloudflare R2, Backblaze B2 or similar. Give Suble an endpoint, bucket and access key (region defaults to auto, path to /); the secret key is write-once, stored encrypted and never shown again. Suble verifies the credentials live before saving — a real write-and-delete test, also on a Test connection button — so a bad key surfaces now, not at the first backup. The data lives in your account, not ours.

Scheduled full + continuous WAL

Choose a daily or weekly full backup and set how many to retain. Between full backups, Postgres write-ahead log (WAL) segments are archived continuously — that is what makes restoring to an arbitrary timestamp possible, not just to the last full backup. You can also trigger an on-demand backup any time.

Restore into a new cluster

A restore never overwrites the running cluster. Pick either a point in time (replay WAL to an exact second) or a specific backup, give it a name, and Suble provisions a fresh cluster restored to that point — so you can inspect it, pull out the rows you lost, or cut over on your terms.

enable backups against your S3 bucket
# Console -> Clusters -> your DB cluster -> Backups tab
#   S3 endpoint:  https://s3.eu-central-1.amazonaws.com
#   Region:       eu-central-1
#   Bucket:       my-suble-backups
#   Path:         /prod-db
#   Access key:   AKIA...
#   Secret key:   ****   (write-once — stored encrypted, never shown again)
#   Schedule:     Daily full backup + continuous WAL archiving
#   Retention:    7 full backups

# Suble initializes the pgBackRest stanza, takes the first full
# backup, and streams WAL to your bucket from then on.
restore to a timestamp -> a new cluster
# Backups tab -> Restore. Choose ONE of:
#   Point in time:    2026-07-18 09:30:00   (replay WAL to that second)
#   A specific backup: 20260718-013455F     (restore exactly that one)
#   New cluster:      prod-db-restored

# Suble provisions a brand-new cluster restored to that point and
# brings it up with automatic failover. Your live cluster is never
# touched — restore is always non-destructive. (Ask for a time before
# your earliest backup and Suble tells you the earliest you can reach.)

Restore is always non-destructive: it creates a new cluster and leaves the original running. Deleting a backup, by contrast, is permanent: Suble runs pgBackRest expire to remove that backup set — and the WAL it alone was pinning — from your S3 bucket, then drops its record, so your storage bill matches what you keep. It is a type-to-confirm action. (Only a failed backup that never uploaded is a record-only delete.)

What it protects against

Replication keeps your database online through failures — but it is not a backup. Understand what each one covers.

High availability

If the primary goes down, automatic failover promotes a healthy replica in seconds, keeping your database online. Your data is continuously replicated to every node. The same replicated design gives you zero-downtime patchingand minor upgrades (members are updated one at a time, the leader last), and automatic member self-healing — a failed member is rebuilt and re-synced into the cluster on its own. We're expanding this across multiple datacenter nodes and regions.

Backups cover the other half

HA and backups solve different problems. Replication protects against a node dying right now; it does nothing about someone dropping a table an hour ago — every replica faithfully replicates the drop. That is what managed backups and point-in-time recovery (pgBackRest, to your own S3 bucket) are for — see Backups & point-in-time recovery above.

Pricing

There is no special "managed Postgres" SKU. A database cluster costs the sum of its parts — the same per-resource meters as everything else on Suble, all metered hourly and capped monthly.

Member VMs

Each member is billed exactly like a standalone instance — its plan's hourly rate, capped at the monthly price. Three members cost three instances; scale down and the bill follows by the hour.

The load balancer

The managed LB that exposes the endpoint is billed at its size's rate. Reuse an existing load balancer and it is not billed twice.

The private network

The cluster's private network is metered hourly. Reuse an existing network and there is nothing extra to pay. Deleting the cluster releases everything it owns.

The dashboard shows the live monthly estimate as you size the cluster, and the cluster page breaks the running cost down by members, load balancer and network.

Reference

The choices you make when creating a Postgres cluster, and what you manage afterward from the cluster page. For the shared infrastructure model — members, load balancer, private network — see the Clusters overview.

Create options

Options

moderequiredDatabaseChoose Database (not Container) so the cluster runs a managed database engine instead of your containers.
enginerequiredPostgreSQLThe database engine. This page covers PostgreSQL.
versionrequired16The PostgreSQL major version. Clusters run PostgreSQL 16 today; more versions are on the way.
nodesrequired1 or >= 3Member count. 1 = single, non-HA. 3+ = HA (one primary + replicas). 2 is not allowed — no safe quorum for leader election.
planrequiredstringMember VM plan code (e.g. BXS.s1). Every member runs this plan, and each holds a full local copy of the data.
networkstringPrivate network the members + load balancer share. Auto-created with the cluster, or point it at an existing one.

Manage

On the cluster page

Connection stringreadThe single load-balanced host:5432 to connect to, shown on the cluster page. Use sslmode=require. It always points at the current primary.
Superuser passwordrevealA generated superuser password, revealed on demand and owner-only. Use it for administrative tasks and to bootstrap app users.
Databasescreate / dropCreate and manage application databases from the dashboard, or with SQL over the connection.
Users & rolescreate / manageCreate per-database app users with least privilege, and manage their passwords — separate from the superuser.
Members & rolesreadEach member's current role (PRIMARY / replica), its replication lag, and health — so you can see who leads and how far behind the replicas are.
MetricsreadLive per-member CPU, memory, disk and network, plus Postgres stats — active vs max connections and per-database size — on the cluster's Metrics tab.
Updatesrun / scheduleRoll OS package and PostgreSQL minor-version updates across the cluster with no write outage — members are updated one at a time and the leader is handed off last. Run on demand or in a weekly maintenance window.
ConfigurationtuneTune postgresql.parameters that matter — max_connections, shared_buffers, work_mem, WAL sizing, statement timeouts, and more. Reload-class keys apply live; restart-class keys roll one member at a time, primary last.
Backups & PITRconfigure / restorePoint backups at your own S3 bucket (live-verified on save): scheduled full backups plus continuous WAL archiving. Back up on demand, and restore either to any timestamp or to a specific backup, into a brand-new cluster (non-destructive).
ActivityreadFailovers and role changes are recorded in the cluster's Activity tab and also fire a notification.