This is the multi-page printable view of this section. .
Tutorials
- 1: Troubleshooting
- 2: Manual PITR Drill
- 3: Enabling HugePage for PostgreSQL
- 4: Clone and Side-Restore a PostgreSQL Instance
- 5: Accidental Deletion
- 6: HA Drill: Handling 2-of-3 Node Failure
- 7: Bind a L2 VIP to PostgreSQL Primary with VIP-Manager
- 8: Deploy HA Citus Cluster
This section provides step-by-step tutorials for common PostgreSQL tasks and scenarios.
- Citus Cluster: Deploy and manage Citus distributed clusters
- Disaster Drill: Emergency recovery when 2 of 3 nodes fail
- PG VIP: Configure L2 VIP for PostgreSQL clusters
1 - Troubleshooting
This document lists possible PostgreSQL and Pigsty failures. It gives standard procedures to locate, resolve, and analyze them.
Disk Space Exhausted
Disk space exhaustion is the most common type of failure.
Symptoms
If the database disk is full, PostgreSQL cannot operate normally. Logs repeatedly report “no space left on device”, and new writes fail. PostgreSQL may also report a PANIC and shut down.
The Pigsty NodeFsSpaceFull alert triggers when filesystem free space is less than 10%. To locate the problem, open the NODE Instance dashboard and inspect the FS metrics panel.
Diagnosis
You can also log in to the database node and run df -h.
Identify the full partition.
Then check these directories and their sizes to identify which files consume the space:
- Data directory (
/pg/data/base): Stores data files for tables and indexes; pay attention to heavy writes and temporary files - WAL directory (e.g.,
pg/data/pg_wal): Stores PG WAL; WAL accumulation/replication slot retention is a common cause of disk exhaustion. - Database log directory (e.g.,
pg/log): If PG logs are not rotated in time and large amounts of errors are written, they may also consume significant space. - Local backup directory (e.g.,
data/backups): When using pgBackRest or similar tools to save backups locally, this may also fill up the disk.
If the issue occurs on the Pigsty admin node or monitoring node, also consider:
- Monitoring data: VictoriaMetrics time-series metrics and VictoriaLogs log storage both consume disk space; check retention policies.
- Object storage data: Pigsty’s integrated Silo object storage may be used for PG backup storage.
After you identify the largest directory, use du -sh <directory> to locate large files or subdirectories.
Resolution
Disk exhaustion requires immediate action to free space and keep the database operating.
If the data disk and system disk are the same, a full disk may prevent shell commands from running.
In this case, delete the /pg/dummy placeholder file to free emergency space for shell commands.
If accumulated pg_wal caused a database crash, free space and restart the database service.
Then check data integrity carefully.
Transaction ID Wraparound
PostgreSQL reuses 32-bit transaction IDs (XIDs) in a cycle. Exhaustion causes a transaction ID wraparound failure (XID Wraparound).
Symptoms
The first typical sign is age saturation entering the warning zone in PGSQL Persist - Age Usage.
Database logs begin to show messages such as WARNING: database "postgres" must be vacuumed within xxxxxxxx transactions.
If the condition gets worse, PostgreSQL enters protection mode. With about 1 million transaction IDs remaining, the database becomes read-only. At the limit of about 2.1 billion (2^31), it rejects new transactions and shuts down to prevent data corruption.
Diagnosis
PostgreSQL and Pigsty enable automatic garbage collection (AutoVacuum) by default. Thus, this failure usually indicates another underlying problem. Common causes include very long transactions (SAGE), incorrect Autovacuum settings, blocked replication slots, and insufficient resources. Storage engine or extension bugs and bad disk blocks can also cause it.
First, identify the database with the highest age. Then inspect table ages in the Pigsty PGCAT Database - Tables panel. Also inspect database error logs for evidence of the cause.
Resolution
- Immediately freeze old transactions: If the database is not yet in read-only protection mode, immediately run a manual VACUUM FREEZE. For faster results, first freeze the oldest tables individually. Connect as a superuser and run
VACUUM FREEZE table_name;on tables identified with the largestrelfrozenxid. Prioritize tables with the highest XID age. This can quickly reclaim transaction ID space. - Single-user mode rescue: If the database rejects writes or has shut down for protection, start it in single-user mode. Run
VACUUM FREEZE database_name;to freeze and clean the entire database. Then restart in multi-user mode. This can remove wraparound protection and permit writes again. Operate carefully in single-user mode. Make sure that enough transaction IDs remain to complete the freeze. - Standby node takeover: If problems such as hardware failure prevent vacuum, consider promoting a read-only standby to primary. This can provide a cleaner environment for the freeze. For example, if bad blocks prevent vacuum on the primary, manually fail over to the standby and run emergency vacuum freeze. Verify that the new primary has frozen old transactions before you switch the load back.
Connection Exhaustion
max_connections limits PostgreSQL connections.
When clients exceed this limit, PostgreSQL rejects new connections.
Applications cannot connect and report FATAL: remaining connection slots are reserved for non-replication superuser connections or too many clients already.
Regular connection slots are exhausted.
Only slots reserved for superusers or replication remain.
Diagnosis
Connection exhaustion usually results from many concurrent client requests. Inspect active sessions in PGCAT Instance, PGCAT Database, or PGCAT Locks. Identify the query types that occupy the connections. Look for many “Idle in Transaction” connections, long transactions, and slow queries.
Resolution
Kill queries: If connection exhaustion blocks application operations, usually use pg_terminate_backend(pid) for immediate relief.
If connections use a pool, adjust its size settings and reload it to reduce database connections.
You can also increase max_connections.
Restart the database to apply the change.
etcd Quota Exhausted
An exhausted etcd quota will cause the PG high availability control plane to fail and prevent configuration changes.
Diagnosis
Pigsty uses etcd as the distributed configuration store (DCS) for high availability. etcd has a storage quota of about 2GB by default. At the limit, etcd rejects writes and reports “etcdserver: mvcc: database space exceeded”. Patroni then cannot write heartbeats or configuration changes to etcd, so cluster management fails.
Resolution
Pigsty v2.0.0 through v2.5.1 are affected by default. Pigsty v2.6.0 added automatic compaction for deployed etcd. If etcd stores only PostgreSQL high-availability leases, the problem no longer occurs in regular use.
Defective Storage Engine
TimescaleDB’s experimental Hypercore storage engine has confirmed defects. Reported cases include VACUUM failing to reclaim space and causing XID wraparound. If you use Hypercore, promptly migrate to native PostgreSQL tables or the default TimescaleDB engine.
Detailed introduction: PG New Storage Engine Failure Case (Chinese)
2 - Manual PITR Drill
This tutorial performs a PostgreSQL PITR drill in Pigsty v4.5.0’s four-node sandbox.
It runs pgsql-pitr.yml in the sequence down → pitr → up.
Separate validation steps precede data overwrite, timeline promotion, and HA reconstruction.
For one current node, use pig pitr.
For direct pgBackRest control, see the low-level pg-pitr utility.
Recovery stops Patroni/PostgreSQL and overwrites the target PGDATA with pgbackrest --force restore.
The up stage deletes the target cluster’s etcd prefix and rebuilds Patroni state.
The playbook prints a plan but has no interactive confirmation.
Before production use, the operator must state and confirm the exact cluster and recovery point.
Verify a recent usable backup that has been independently tested.
Run --check with exactly the same -l, variables, and tags, and schedule a maintenance window.
This tutorial does not authorize these commands in a production environment.
Prepare an Isolated Sandbox
Use Vagrant or another disposable four-node lab and select the ha/full template, which includes a Silo backup repository:
ha/full defines a single-node pg-meta, a three-node pg-test, and a Silo/pgBackRest repository.
This tutorial uses the exact target pg-meta.
Before you copy that selector to another environment, resolve its inventory.
Initial deployment and backup both change sandbox state. Production environments require their own deployment and backup approval process.
Establish Recovery Evidence
Start with read-only topology, backup-chain, and WAL-range checks:
info must show at least one usable backup with status: ok.
Archived WAL must cover the intended target.
check validates the current stanza and archive path.
It does not replace a real restore drill or validation of an independent copy.
In the sandbox, run Pigsty’s heartbeat helper to create an easy-to-verify time series:
Record, then stop the workload:
- the timezone-qualified timestamp you intend to recover to;
- heartbeat, LSN, and transaction boundaries around that point;
- current primary, timeline, and backup label;
- target cluster
pg-metaand target node.
Inspecting real application tables requires separate authorization. This tutorial uses only sandbox heartbeat data.
Declare the Recovery Task
Declare the target under pg-meta.vars in the sandbox inventory:
clusteris the source backup stanza and defaults to the targetpg_cluster.action: pausepauses PostgreSQL at the target for a human validation gate.archive: truepreserves archive settings.backup: trueis not a safe-backup substitute: it deletes an existing<pg_data>-backupbefore moving current PGDATA, so this drill keeps itfalse.
You can also pass the object temporarily with -e.
Preflight and all three stages must use exactly the same valid JSON to prevent variable differences.
Full Preflight
Before any stop or write action, check the complete workflow against the same target:
Confirm that Ansible resolves exactly pg-meta, then review the output for:
- source stanza, recovery type, time, timeline, and action;
- destination
pg_data, port, and repository; - tablespace/link mappings;
archiveandbackupbehavior.
--check validates inventory, variables, and task selection.
It cannot prove that a pgBackRest backup is restorable.
If the target, backup, or variables change, repeat preflight.
Stage One: Stop
Only after the operator reconfirms exact target pg-meta, recovery point, and maintenance window, run:
down attempts to pause Patroni automatic failover and stops Patroni on every target member.
If PostgreSQL remains running, it uses immediate shutdown.
Then verify every target node independently of the playbook result:
The expected results are inactive and “server is not running.”
If any member remains active, stop and diagnose the problem.
Do not start the restore.
Stage Two: Restore and Validate
Recheck pg_pitr and the target nodes before running the destructive stage:
This stage:
- renders
/pg/conf/pitr.confand/pg/bin/pg-restore; - optionally moves old PGDATA according to
backup; - creates the destination and runs pgBackRest restore with
--forceanddelta=y; - starts PostgreSQL directly and waits for a consistent recovery state in the log;
- prints a
pg_controldatasummary.
Control data proves only that the directory contains readable control state.
It does not verify the time, XID, or application boundary.
With action: pause, confirm that WAL replay reached and paused near the target:
Then inspect only the smallest authorized data scope. In the sandbox, check heartbeat rows. If the target is wrong:
- keep every Patroni member stopped;
- stop the manually started PostgreSQL;
- adjust the target and rerun the complete
--check; - rerun the
pitrstage.
Do not run up or allow replicas from the old timeline to rejoin.
Promote and Stage Three: Rebuild HA
Promote only after the operator accepts the recovered result and the creation of a new timeline:
The expected result is f.
Promotion changes state, and you cannot undo it without loss.
With every Patroni member still stopped and exact target still pg-meta, run:
For the primary, up deletes the /pg/pg-meta/ prefix from etcd.
The effective prefix also depends on pg_namespace and Citus settings.
It stops the manually started PostgreSQL and starts Patroni on the primary.
It then starts replicas one at a time and resumes HA.
The etcd deletion task tolerates errors.
Thus, a successful playbook result does not prove that it removed stale DCS state correctly.
Post-Recovery Acceptance
Verify each layer. Service startup alone does not complete recovery:
Also confirm:
- exactly the intended member is primary and replicas stream from the new timeline;
- HAProxy/VIP/DNS and application traffic point only to accepted instances;
- data and event boundaries around the recovery target are correct;
archive_mode,archive_command, and new-WAL archiving work;- monitoring, alerts, and the backup repository contain no stale cluster state.
After the new timeline is stable, create and verify a new full backup under the applicable approval process:
If recovery used archive: false, it wrote archive-mode=off.
Validate recovery and confirm a maintenance window before you reset that override and perform a controlled restart.
The default archive: true does not require this step.
Multi-Node and Cross-Cluster Recovery
- Old-timeline replicas must not rejoin without validation.
upstarts replicas one at a time and waits for clone/recovery; monitor them to completion. - In cross-stanza recovery,
pg_pitr.clusteris the source while-lis the destination being overwritten. Record and state both separately. - Cross-cluster recovery should normally use
archive: falseso a test destination cannot write WAL into the source stanza. Enable its own archiving only after acceptance and post-clone stanza cleanup. link_map,data,port, and a temporaryrepochange the actual storage and data targets; include all of them in both--checkand human review.
Related
3 - Enabling HugePage for PostgreSQL
Use
node_hugepage_countandnode_hugepage_ratioor/pg/bin/pg-tune-hugepage
To enable HugePages, consider node_hugepage_count and node_hugepage_ratio.
Apply these settings with ./node.yml -t node_tune.
HugePages have advantages and disadvantages for databases. Exclusive memory management prevents reallocation and reduces database OOM risk. However, HugePages can reduce performance in some conditions.
Before PostgreSQL starts, allocate enough huge pages.
The pg-tune-hugepage script can reclaim the unused portion.
This script is available only for PostgreSQL 15+.
If PostgreSQL is already running, use this method to enable huge pages. It requires PG15+:
4 - Clone and Side-Restore a PostgreSQL Instance
Pigsty v4.5.0 provides two local shell utilities:
pg-forkcopies a PostgreSQL data directory and gives the copy a separate port.pg-pitrinvokes pgBackRest to restore a stopped data directory to a selected target.
Use these utilities for sandbox drills, side-channel investigation, and temporary testing.
They do not orchestrate complete Patroni-cluster recovery.
Prefer pig pitr for a managed instance and staged pgsql-pitr.yml for a multi-node cluster.
pg-fork recursively removes an existing destination directory.
pg-pitr overwrites the destination with backup data.
Both can run without a prompt in a non-interactive environment.
Before a real run, verify absolute source and destination paths, ports, tablespaces, and exact cluster/instance identity.
Also verify an independent recent backup that has been tested.
A new CoW clone is not an independent backup.
pg-fork
pg-fork copies a PostgreSQL data directory on the current node.
Run it as the database OS user, normally postgres, or at least as a member of the postgres group:
Parameters
| Parameter | Meaning | Default |
|---|---|---|
<FORK_ID> | One digit from 1 to 9, used to derive the default directory and port | Required |
-d, --data <path> | Source data directory | $PG_DATA or /pg/data |
-D, --dst <path> | Destination data directory | /pg/data<FORK_ID> |
-p, --port <port> | Source instance port | $PG_PORT or 5432 |
-P, --dst-port <port> | Destination instance port | <FORK_ID>5432 |
-s, --skip | Skip the online-backup API and force cold-copy mode | No |
-y, --yes | Skip interactive confirmation | No |
The script rejects identical normalized source and destination paths. It cannot determine whether a custom destination contains other important data. If the destination directory exists, the script removes it recursively before the copy.
Hot Backup and Cold Copy
By default, the script connects to the source port and uses one psql session to run:
CHECKPOINT;pg_backup_start();rm -rf <destination>followed bycp -a --reflink=auto;pg_backup_stop(wait_for_archive => false).
If the source is unreachable on the selected port, the script automatically falls back to a cold copy.
It does not abort.
-s also forces a cold copy.
A cold copy is safe only after you independently confirm that the source instance has fully stopped.
A postmaster.pid warning alone does not prove the process state.
On the same filesystem, the script recognizes XFS with reflink enabled, Btrfs, Bcachefs, and OCFS2 as fast CoW modes.
Other filesystems or cross-filesystem destinations still use cp --reflink=auto, but may require a full copy.
The help text describes ZFS support more broadly than the detector.
The v4.5.0 implementation does not classify ZFS as a confirmed fast-CoW mode.
Clone Configuration
After a successful copy, pg-fork:
- removes
postmaster.pid,postmaster.opts, andstandby.signalfrom the destination; - clears physical replication-slot files in the destination;
- writes a separate
port,archive_mode=off, and a locallog_directoryto the destination’spostgresql.auto.conf; - removes
primary_conninfo,primary_slot_name, and oldrecovery_target*overrides.
The script does not check destination port availability or adjust memory settings. Before you start the copy, inspect at least these settings:
cp -a preserves symlinks under pg_tblspc.
pg-fork does not copy or remap tablespaces outside PGDATA.
Starting such a clone can access or modify the source tablespaces.
If external tablespaces exist, copy and remap each one independently.
Otherwise, do not use this script to create a writable clone.
Interaction Boundary
The script asks Proceed with fork? [y/N] only if standard input is a terminal and you did not use -y.
Pipes, CI, cron, and other non-interactive calls do not get this prompt.
Automation must enforce a strict absolute-path allowlist and check whether the destination exists before it calls the script.
Do not add -y by default only for convenience.
pg-pitr
pg-pitr is a low-level pgBackRest restore wrapper.
It does not pause or start Patroni, stop or start PostgreSQL, clear DCS state, or rebuild replicas.
Recovery Targets
Understand and select a recovery target before execution. Without arguments, the command only shows help:
| Parameter | pgBackRest semantics |
|---|---|
-d, --default | Set no stop target and replay to the available end of WAL |
-i, --immediate | Stop when the selected backup becomes consistent |
-t, --time <timestamp> | Recover to a timestamp |
-n, --name <restore-point> | Recover to a named restore point |
-l, --lsn <lsn> | Recover to an LSN |
-x, --xid <xid> | Recover to a transaction ID |
-S/--set, with compatibility alias -b/--backup, selects which backup set recovery starts from.
It does not select a stop target.
For example, -S 20251225-120000F -d still replays to the end of WAL.
Use -S ... -i to stop when that backup first becomes consistent.
For time, name, lsn, xid, and immediate, pgBackRest pauses at the target by default.
-P/--promote changes this action to automatic promotion.
Use -X/--exclusive only with a precise target such as time, lsn, or xid.
Other Options
| Parameter | Meaning |
|---|---|
-D, --data <path> | Absolute destination data directory; default /pg/data |
-s, --stanza <name> | pgBackRest stanza; defaults to the first non-global stanza in the config |
-T, --timeline <value> | latest, current, or a positive numeric timeline |
-P, --promote | Automatically promote recovery methods that have a stop target |
-v, --verbose | Enable pgBackRest info-level console logging |
-c, --check, --dry-run | Print the command without executing it |
-y, --yes | Skip the five-second countdown |
-- <args> | Pass additional arguments directly to pgBackRest |
-c checks the generated command.
It does not prove that the backup/WAL is usable or that PostgreSQL and Patroni have stopped.
The wrapper does not filter conflicting native arguments.
If you pass repository, tablespace, or link-mapping options, carefully review the final command.
Safe Execution Sequence
This example shows the low-level procedure for one isolated destination. For production cluster recovery, use the complete runbook:
Real execution rejects root and aborts if postmaster.pid exists in the destination.
Before removing even a stale PID file, the operator must confirm that PostgreSQL has stopped.
There is no y/N question.
An interactive terminal gets only an interruptible five-second countdown.
A non-interactive call skips the countdown and starts the restore immediately.
After restore, the operator starts and validates the instance:
Promote only after you verify the recovery target, authorized application data, timeline, and archive settings.
Promotion creates a new timeline and is not a reversible inspection operation.
pg-pitr does not disable archiving itself.
Before you follow its generic final “enable archive_mode” hint, inspect the effective setting.
Correct only an override introduced by this recovery.
Additional Side-Restore Risks
For a custom destination such as /pg/data1, pgBackRest can restore postgresql.auto.conf and overwrite the separate port set by pg-fork.
Before startup, recheck port, archive_mode, sockets, logging, and memory settings.
If the backup contains external tablespaces or links, a side restore can use the original paths.
For isolation, pass reviewed pgBackRest --tablespace-map, --link-map, or related arguments after --.
Inspect the generated command.
Otherwise, do not start the restored copy on the production host.
Recommended Clone-Validation Flow
- Verify the source instance, destination absolute path, destination port, tablespaces, and independent backup.
- Run
pg-fork <id>in an interactive terminal and confirm that the plan shows hot backup rather than an unintended cold-copy fallback. - Before starting the clone, run
pg-pitr -D <clone> ... -cand inspect the recovery command. - Execute restore only after explicit destination confirmation, then recheck the clone’s port and every external path.
- Start the clone on the isolated port and verify recovery state and only the application data you are authorized to inspect.
- Promote only if the clone is intentionally becoming a new primary; otherwise stop it and clean up only the exact, verified path.
Side validation reduces direct writes to the current PGDATA. It still uses the same backup repository, consumes host resources, and may access external tablespaces. The sandbox therefore still has risks.
Related
5 - Accidental Deletion
Accidental Data Deletion
For a small accidental DELETE, consider pg_surgery or pg_dirtyread for selective recovery in place.
If the deleted data has already been reclaimed by VACUUM, then use the general accidental deletion recovery process.
Accidental Object Deletion
For accidental DROP/DELETE operations, usually select a recovery method in this order:
- Check whether the application or another data system can supply the data. If so, recover it from that system.
- Check whether a delayed replica exists. If so, advance it to the time before deletion and query the data for recovery.
- If deletion is confirmed, check the backups and their recovery window. If the window covers the deletion time, start PITR.
- Select where to replay: an in-place cluster PITR rollback, a new server, or a replica. Then perform recovery.
Accidental Cluster Deletion
Pigsty management commands can delete an entire cluster, for example pgsql-rm.yml or bin/pgsql-rm.
Unless pg_rm_backup is false, these commands also delete the cluster backups.
Warning: In this situation, you cannot recover the data. Review the target and the deletion consequences carefully before you proceed.
For production, we recommend that you set this parameter to false globally in the inventory.
This preserves backups when you remove clusters.
6 - HA Drill: Handling 2-of-3 Node Failure
In a typical 3-node HA deployment, simultaneous failure of two nodes removes the majority. The system usually cannot complete automatic failover and needs manual intervention.
First, assess the two failed servers. If they can restart quickly, prioritize their recovery. Otherwise, use the Emergency Recovery Procedure.
The Emergency Recovery Procedure assumes that the administration node has failed and only one regular database node survives. In this case, the fastest recovery procedure is:
- Adjust HAProxy configuration to direct traffic to the primary.
- Stop Patroni and manually promote the PostgreSQL replica to primary.
Adjust HAProxy Configuration
If connections bypass HAProxy, skip this step. If connections use HAProxy, change its configuration to direct read/write traffic to the primary manually.
- Edit
/etc/haproxy/conf.d/<pg_cluster>-primary.cfg, where<pg_cluster>is your PostgreSQL cluster name, e.g.,pg-meta. - Comment out the health check configuration options to stop health checks.
- Comment out the other two failed machines in the server list, keeping only the current primary server.
After you change the configuration, wait until primary promotion before you run systemctl reload haproxy.
The new configuration disables HAProxy primary health checks, which use Patroni by default.
HAProxy then directs write traffic to the current primary.
Manually Promote Replica
Log in to the target server and switch to the dbsu user.
Run CHECKPOINT to flush data to disk.
Stop Patroni, restart PostgreSQL, then promote the replica:
If you changed the HAProxy configuration, now run systemctl reload haproxy.
This applies the configuration and directs traffic to the new primary.
Avoid Split Brain
After emergency recovery, the next priority is to Avoid Split Brain. Prevent the other two servers from returning online and becoming competing primaries. Such a split brain can cause data inconsistency.
Simple approaches:
- Power off/disconnect network the other two servers to ensure they don’t come online uncontrollably.
- Adjust the database connection string used by applications to point directly to the surviving server’s primary.
Then decide the next steps based on the specific situation:
- A: The two servers have temporary failures (e.g., network/power outage) and can be repaired in place to continue service.
- B: The two failed servers have permanent failures (e.g., hardware damage) and will be removed and decommissioned.
Recovery After Temporary Failure
If the other two servers have temporary failures and can return to service, use these repair and rebuild steps:
- Handle one failed server at a time, prioritize the admin node / INFRA node.
- Start the failed server and stop Patroni after startup.
When ETCD regains quorum, it resumes operation. Start Patroni on the surviving server, which is the current primary. Patroni takes control of the existing PostgreSQL instance and regains cluster leadership. Then enter maintenance mode:
On each of the other two instances, use the postgres account to run touch /pg/data/standby.signal.
The marker identifies the instance as a replica.
Then start Patroni:
After confirming Patroni cluster identity/roles are correct, exit maintenance mode:
Recovery After Permanent Failure
After permanent failure, first recover ~/pigsty on the administration node.
The essential files are pigsty.yml and files/pki/ca/ca.key.
If you cannot retrieve these two files or their backups, deploy a new Pigsty installation. Migrate the existing cluster through Backup Cluster.
Back up the
pigstydirectory regularly, for example with Git version control.
Configuration Repair
You can use the surviving node as the new administration node.
Copy ~/pigsty to it, then adjust the configuration.
For example, replace the original administration node 10.10.10.10 with the surviving node 10.10.10.12:
ETCD Repair
Then execute the following command to reset ETCD to a single-node cluster:
Follow the instructions in ETCD Reload Configuration to adjust ETCD Endpoint references.
INFRA Repair
If the surviving node does not have the INFRA module, configure and install it on that node. Run this command:
Repair monitoring on the current node:
PGSQL Repair
After you repair each module, use the standard expansion procedure to add nodes and restore cluster high availability.
7 - Bind a L2 VIP to PostgreSQL Primary with VIP-Manager
If all cluster nodes share one L2 network, you can optionally define an L2 VIP for the PostgreSQL cluster.
The VIP uses Master-Backup mode. It always points to the node that runs the cluster primary instance.
VIP-Manager manages the VIP. It reads the Leader Key that Patroni writes to DCS (etcd) to determine whether the local node is primary.
Enable VIP
To enable VIP for a cluster, set pg_vip_enabled to true at cluster level.
You can also enable it globally.
pg_vip_address must be an available, valid IP address with a subnet in the current L2 network.
pg_vip_interface defaults to auto.
With this setting, Pigsty detects each instance interface from its inventory IPv4 address.
If automatic detection is unsuitable for a non-standard or policy-routing environment, specify a valid interface name for each instance. For example:
To refresh the VIP configuration and restart the VIP-Manager, use the following command:
8 - Deploy HA Citus Cluster
Citus is a PostgreSQL extension that turns PostgreSQL into a distributed database. It scales horizontally across nodes to process large data volumes and query workloads.
Patroni v3.0+ provides native high-availability support for Citus. This simplifies Citus cluster setup. Pigsty also provides native support.
Note: Citus 13.x supports PostgreSQL 18, 17, 16, 15, and 14. Pigsty extension repo provides Citus ARM64 packages.
Citus Cluster
Pigsty natively supports Citus.
The current complete configuration template is conf/ha/citus.yml.
The following four-node example shows the key parameters.
It has a two-node coordinator cluster, pg-citus0, and two single-node Worker clusters, pg-citus1 and pg-citus2.
It is not an exact excerpt from the current complete template.
Citus has additional configuration requirements beyond those for a standard PostgreSQL cluster. First, download, install, load, and enable the Citus extension. Use these four parameters:
repo_packages: Must include thecitusextension, or you need to use a PostgreSQL offline package that includes Citus.pg_extensions: Must include thecitusextension, i.e., you must install thecitusextension on each node.pg_libs: Must explicitly includecitusin the first position; current Patroni templates use this parameter directly forshared_preload_libraries.pg_databases: Define a primary database that must have thecitusextension installed.
Then configure the Citus cluster:
pg_mode: Must be set tocitusto tell Patroni to use Citus mode.pg_primary_db: Must specify the name of the primary database withcitusextension, namedcitushere.pg_shard: Must specify a unified name as the cluster name prefix for all horizontal shard PG clusters,pg-citushere.pg_group: Set an integer shard number, starting from zero.0identifies the coordinator cluster. Other values identify Worker clusters.pg_cluster: Must be unique among physical PostgreSQL clusters. The usual name combinespg_shardwith a sequence number. The current role does not require the name to equal the concatenation ofpg_shardandpg_group.pg_dbsu_password: Must be set to a non-empty plaintext password, otherwise Citus will not work properly.pg_parameters: Recommended to setcitus.node_conninfoto enforce SSL access and require node-to-node client certificate verification.
After configuration, you can deploy the Citus cluster using pgsql.yml just like a regular PostgreSQL cluster.
Manage Citus Cluster
After defining the Citus cluster, deploy it using the pgsql.yml playbook:
On any member, use the DBSU (postgres) account to show Citus cluster status with patronictl, whose alias is pg:
Manage each horizontal shard as an independent PGSQL cluster with pg (patronictl).
For Citus, specify the shard number with --group:
The Citus system table pg_dist_node records cluster node information.
Patroni maintains this table automatically.
You can also view user authentication information (superuser access only):
You can then connect with a regular application user, such as dbuser_citus with DDL privileges:
Using Citus Cluster
We strongly recommend the Citus official documentation for its architecture and core concepts.
Understand the characteristics and uses of the five Citus table types:
- Distributed Table
- Reference Table
- Local Table
- Local Management Table
- Schema Table
On the coordinator, create distributed tables and reference tables. You can query them from any data node. From Citus 11.2, any Citus database node can act as coordinator.
Use pgbench to create tables.
Distribute the main table, pgbench_accounts, across nodes.
Use the other small tables as reference tables:
Run read/write tests:
Production Deployment
For production, you usually need physical streaming-replication replicas for the Coordinator and each Worker cluster.
The current conf/ha/citus.yml defines one pg-meta instance and 12 Citus instances across 13 hosts.
The Citus instances form six two-node physical clusters with pg_group values 0-5.
The following 10-node example shows a separate production topology.
It is not the current template.
We will cover a series of advanced Citus topics in subsequent tutorials:
- Read/write separation
- Failure handling
- Consistent backup and recovery
- Advanced monitoring and diagnostics
- Connection pooling