Overview
pgEdge is an active-active distributed PostgreSQL cluster using Spock multi-master replication. Every node accepts both reads and writes simultaneously, and data written to any node replicates to all others automatically. The cluster spans multiple geographic locations with configurable replicas per location, providing a globally distributed, fault-tolerant database with no single point of failure. Database credentials are not template values. pgEdge reads its username, password and database name from a dictionary secret you create before installing, so no password passes through Helm or lands in the release.Architecture
- pgEdge — Stateful workload running PostgreSQL 17 with the Spock extension. All nodes are active writers connected in a full-mesh replication ring. Each replica gets its own persistent volume.
- pgcat — Connection pooler providing a single virtual endpoint for applications. Sends writes to one designated node and spreads reads across the others.
- Spock — Multi-master logical replication extension included in the pgEdge image. Handles cross-node replication with last-update-wins conflict resolution.
What Gets Created
- Stateful pgEdge Workload — PostgreSQL 17 with the Spock extension. One set of replicas per configured location, each replica individually addressable through replica-direct DNS.
- Standard pgcat Workload — Connection pooler that routes application traffic to pgEdge nodes. Runs
pgcat.minReplicastopgcat.maxReplicasreplicas in each configured location and autoscales on RPS. - Cron Backup Workload (optional) — Runs
pg_dumpon a schedule and uploads the result to AWS S3 or GCS. Suspended in every location except the first one you configure. - Volume Set — ext4 general-purpose SSD volumes with 7-day snapshots and a final snapshot on deletion. One volume per replica.
- Identity & Two Policies — An identity bound to the workloads, with
revealon this release’s secrets and on your credentials secret, plusviewon the one GVC you install into so each node can confirm at boot that the GVC really has every location you listed. When backup is enabled, the identity also carries the cloud account and IAM policy. - Secrets — An opaque secret holding the pgEdge node startup script, an opaque secret holding the pgcat startup script, and, only when backup is enabled, a dictionary secret holding the backup destination. Your credentials secret is created by you and is never part of the release.
This template does not create a GVC. Every resource lands in the GVC you install into, so
cpln workload exec, cpln logs and uninstalling all work against that GVC, and uninstalling can never delete it. Nothing runs in a GVC location you did not list in locations.Prerequisites
A GVC must already exist, and it must contain every location you list inlocations. The requirement is one-directional — the GVC may have more locations than you list, and nothing pgEdge-related runs in those. Check what a GVC has before installing:
spec.staticPlacement.locationLinks. To add a missing one:
dictionary secret must exist before you install. These are the credentials you type into every connection string, so they are not values — a value would leave them in the Helm release.
postgres.credentialsSecretName to the name you used. Secret names are org-wide, so give each release its own. pgcat uses the same password for its admin console, replacing the fixed pgcat_admin password earlier versions shipped.
For backups you also need an AWS or GCP cloud account and a storage bucket. See Backup.
Installation
To install, follow the instructions for your preferred method:UI
Browse, install, and manage templates visually
CLI
Manage templates from your terminal
Terraform
Declare templates in your Terraform configurations
Pulumi
Declare templates in your Pulumi programs
Migrating from 1.x
Template versions through 1.1.1 created their own GVC, so that GVC is part of the 1.x release’s manifest. Version 2.0.0 does not declare it — and Helm deletes what a chart stops declaring.1
Back up the old cluster
Use If replication on the old cluster is dead — which it is on every 1.x install that never applied the fix below — nodes may hold different data. Compare row counts per node before deciding which one to dump, or reconcile them first with the replication fix.
backup.enabled on the 1.x release, or take a manual dump against a node directly:2
Choose the GVC for the new release
Create or pick a GVC and make sure it has exactly the locations you intend to list in
locations. See Prerequisites.3
Create the credentials secret and install 2.0.0 as a NEW release
Use a different release name: secret names are org-wide, so a same-named release collides with the 1.x one even in another GVC.Coming from 1.0.2 or earlier, the credentials were plain values then (
postgres.username, postgres.password, postgres.database), and the template shipped a working default password that was published in the public template repository. Choose a new password now and put it in the dictionary secret — a 2.0.0 install creates its roles from that secret on a fresh data directory, so this is the moment to change it, and you are pointing applications at a new endpoint at cutover anyway.Do not carry the published 1.0.x default forward. Restoring the dump does not depend on the old cluster’s password.4
Restore and cut over
Restore the dump into the new cluster (see Restoring a Backup), then point your applications at the new pooled endpoint,
NEW_RELEASE-pgcat.NEW_GVC.cpln.local:5432.5
Uninstall the old release against the GVC you installed it into
Not the GVC it created — the GVC you passed at install time is where Helm tracks the release, and uninstalling from there takes the created GVC with it.
Switching Replication On for an Existing 1.x Cluster
PostgreSQL 17.11 shipsoutput_plugin_libraries = 'pgoutput, test_decoding', and Spock’s output plugin is not on that list. On every template version through 1.1.1 this means no replication slot could ever be created: each subscription sits at down, each node accepts writes that never leave it, and every status surface still reports healthy. The reason appears in the node’s own PostgreSQL server log, not in cpln logs:
postgresql.conf during initdb, so a fresh 2.0.0 install needs nothing. An existing data directory keeps the old setting, so if you are staying on 1.x while planning the migration, apply it by hand on every node, connecting to each node directly rather than through pgcat:
replicating within seconds of the reload. Check on every node:
Configuration
The defaultvalues.yaml for this template:
Locations and Replicas
Each entry pairs a location with a replica count. Every location listed must exist in the GVC you install into; extra GVC locations are ignored. Listing the same location twice is rejected at render — duplicates would produce duplicate Spock node names.
The first entry is special: its
replica-0 is the node pgcat sends writes to, and it is the only location the backup cron runs in.
Set multiZone: true to spread replicas across availability zones within each location. Verify your selected locations support multiple availability zones before enabling.
GVC locations you did not list show as red in the console, with
This workload location is deactivated because maxScale is set to 0. That is the mechanism that keeps a shared GVC safe — it is what a healthy install looks like, not a fault.Volume Set
Set the initial storage capacity (minimum 10 GiB). Optionally enable autoscaling to expand as data grows:Internal Access
Control which workloads can connect to pgEdge and pgcat. Neither workload is exposed publicly, and the template has no public access option.same-gvc— Allow access from all workloads in the same GVC.same-org— Allow access from all workloads in the org.workload-list— Allow access only from the workloads you list, plus this release’s own workloads, which the template adds for you.
pgcat Pool Modes
pgcat multiplexes application connections into a smaller pool of real database connections, reducing overhead and protecting Postgres from connection exhaustion.defaultPoolSize sets how many real Postgres connections pgcat maintains per pool.
pgcat Read and Write Routing
pgcat parses each query, sends writes toreplica-0 of your first configured location, and sends reads to the other nodes. A single-node cluster has no other node, so that one node serves reads as well — otherwise every SELECT would fail with AllServersDown. This switches on node count alone and needs no configuration.
pgcat builds its configuration at container start. Its config is a TOML file, and a
cpln:// secret reference inside a file is never resolved — the platform only resolves those for environment variables. So rather than rendering the password into the file, the template mounts a startup script that assembles the config from the secret at boot. Nothing changes in how you configure pgcat.Connecting
Connect through pgcat for all application traffic. Use the fully-qualified.GVC_NAME.cpln.local form — the bare workload name does not resolve reliably from every workload type.
Connect to a node directly when you need to control which node executes a statement — schema changes, per-node verification and reconciliation all require a direct connection.
Schema Changes (DDL)
Spock replicates row-level changes (INSERT, UPDATE, DELETE) automatically. DDL does not replicate. A plain CREATE TABLE or ALTER TABLE applies only to the node you ran it on; the other nodes never learn about it, and rows written into that table on one node cannot be applied on a node where it does not exist.
Creating a Table
The simplest correct procedure is to run the sameCREATE TABLE on every node. The auto-add trigger fires locally on each one, so the table ends up in the default replication set everywhere and DML replicates in all directions:
replicate_ddl. Running step 2 on that node fails with duplicate key value violates unique constraint "replication_set_table_pkey", and skipping it strands writes made on the other nodes — outbound filtering happens where the write lands. Confirm on every node:
Other DDL
ALTER TABLE and DROP TABLE follow the same rule — apply on every node, or broadcast once:
Primary Keys
Useuuid primary keys instead of serial/bigserial. Each node maintains its own sequence, so auto-increment integers will collide when the same ID is generated on multiple nodes simultaneously:
Restarts and Replication Health
An upgrade, or anything else that restarts the pgEdge tier, restarts every replica in every location at once — nothing serializes a rolling restart on a stateful workload. Treat it as a planned write interruption.- Writes are unavailable for about 60 seconds. Measured across a three-location simultaneous restart, with one write per second flowing through pgcat.
- pgcat bans a backend for a further 60 seconds after a failed health check, so queries can keep failing with
AllServersDownfor up to a minute after the nodes themselves are back. - Readiness is not a serving signal here. The workload can report ready before writes actually succeed again.
replicating. On a healthy cluster of N nodes, each node has N−1 subscriptions and N−1 replication slots.
Backup
Whenbackup.enabled is true, a cron workload runs pg_dump on the configured schedule. Because every pgEdge node holds a full copy of the data, the backup job connects to replica-0 of the first configured location — and runs in that location only, however many locations the GVC has.
Keep backup.schedule quoted in your values file. Any valid cron expression works, including one beginning with * such as "*/30 * * * *".
The backup job connects over the internal network like any other client, so it is subject to Internal Access. Version 2.0.1 adds the backup workload to a
workload-list automatically when backup.enabled is true; on 2.0.0 it was omitted, and every run uploaded an empty file while reporting success.AWS S3 Prerequisites
- Create your S3 bucket and note its name and region. Set
backup.aws.bucketandbackup.aws.region. - If you do not have a Cloud Account set up, refer to the docs to Create a Cloud Account. Note the cloud account name.
- Create a new AWS IAM policy with the following JSON (replace
YOUR_BUCKET_NAME):
- Set
backup.aws.cloudAccountNameto your cloud account name. - Set
backup.aws.policyNameto the policy created in step 3.
GCS Prerequisites
- Create your GCS bucket and note its name. Set
backup.gcp.bucket. - If you do not have a Cloud Account set up, refer to the docs to Create a Cloud Account. Note the cloud account name.
- Add the
Storage Object Adminrole (roles/storage.objectAdmin) to the GCP service account created by the cloud account — that is what the chart binds. - Set
backup.gcp.cloudAccountNameto your cloud account name.
Restoring a Backup
AWS S3:Important Notes
- Never upgrade a 1.x release onto 2.0.0 in place — it deletes the GVC the 1.x chart created and everything in it. Migrate to a new release: Migrating from 1.x.
- The GVC must contain every location you list, and may contain more. A missing location is not caught at install: the pgEdge container exits with
FATAL: locations declared in values are not in GVC .... An already-initialized node logs aWARNINGinstead and keeps serving. - Shrinking the GVC’s location list under a running cluster leaves every node logging that warning on each restart — shrink
locationsin your values at the same time. - With
internal_access.type: workload-list, list only your clients. The list also governs replication between the nodes and the backup job’s connection, so a clients-only list silently breaks both. Version 2.0.1 adds the release’s own workloads for you — see Internal Access. - Release names must be unique per org — secrets are org-wide, so two releases with the same name collide even in different GVCs.
- Minimum replicas — Use at least 3 replicas per location for production to survive a node loss within a location.
- Conflict resolution — Concurrent writes to the same row from different nodes are resolved by last-update-wins based on commit timestamp. For workloads requiring stronger consistency, route writes for a given entity to a single node using application-level logic.
- DDL does not replicate — create every table on every node, and give every table a
PRIMARY KEYorCREATE TABLEfails outright. - multiZone — Verify your selected locations support multiple availability zones before enabling.
External References
pgEdge Documentation
Official pgEdge documentation
Spock Documentation
Spock multi-master replication extension
pgcat Documentation
pgcat connection pooler
pgEdge Template
View the source files, default values, and chart definition