High availability for PostgreSQL keeps client connections pointed at a single stable endpoint while the cluster moves the database service between nodes during maintenance or failure.

In a Pacemaker + Corosync cluster, the pcs CLI defines resources that the cluster starts, stops, and monitors. A floating IP resource provides the client-facing endpoint, and a systemd resource starts the PostgreSQL service on the node selected to run it.

Failover restarts interrupt active sessions and require consistent database storage between nodes. Use shared storage with one active instance, or use a replication-aware approach instead of a plain systemd resource when each node has its own data directory. Services controlled by the cluster should not be enabled to start automatically at boot outside cluster control.

Steps to set up PostgreSQL high availability with PCS:

  1. Confirm the cluster is online and has quorum.
    $ sudo pcs status
    Cluster name: clustername
    Cluster Summary:
      * Stack: corosync (Pacemaker is running)
      * Current DC: node-01 (version 2.1.6-6fdc9deea29) - partition with quorum
      * Last updated: Thu Jan  1 05:40:04 2026 on node-01
      * Last change:  Thu Jan  1 05:40:02 2026 by root via cibadmin on node-01
      * 3 nodes configured
      * 0 resource instances configured
    
    Node List:
      * Online: [ node-01 node-02 node-03 ]
    
    Full List of Resources:
      * No resources
    
    Daemon Status:
      corosync: active/enabled
      pacemaker: active/enabled
      pcsd: active/enabled
  2. Identify the PostgreSQL service unit name.
    $ systemctl list-unit-files --type=service | grep -E '^postgresql.*\.service'
    postgresql.service                           disabled        enabled
    postgresql@.service                          indirect        enabled

    On installations with multiple instances, pick the specific unit that should move with the cluster.

  3. Disable the PostgreSQL service unit on each node to prevent automatic startup outside cluster control.
    $ sudo systemctl disable --now postgresql.service
    Synchronizing state of postgresql.service with SysV service script with /usr/lib/systemd/systemd-sysv-install.
    Executing: /usr/lib/systemd/systemd-sysv-install disable postgresql
    Synchronizing state of postgresql.service with SysV service script with /usr/lib/systemd/systemd-sysv-install.
    Executing: /usr/lib/systemd/systemd-sysv-install disable postgresql
    Synchronizing state of postgresql.service with SysV service script with /usr/lib/systemd/systemd-sysv-install.
    Executing: /usr/lib/systemd/systemd-sysv-install disable postgresql

    Leaving the service enabled can cause start/stop races at boot and risks corruption when shared storage or single-writer assumptions apply.

  4. Create a floating IP resource for the database endpoint.
    $ sudo pcs resource create db_ip ocf:heartbeat:IPaddr2 ip=192.0.2.30 cidr_netmask=24 op monitor interval=30s

    On multi-homed hosts, add nic=<interface> so the IP binds to the intended network.

  5. Create the PostgreSQL service resource.
    $ sudo pcs resource create db_service systemd:postgresql@16-main op monitor interval=30s

    On versioned installations, use the unit reported by systemctl list-unit-files, such as postgresql@16-main.service or postgresql-16.service.

  6. Group the IP and database resources.
    $ sudo pcs resource group add db-stack db_ip db_service

    A group implies colocation and ordering, so the IP starts before PostgreSQL and both move together.

  7. Verify the resource group placement.
    $ sudo pcs status resources
      * Resource Group: db-stack:
        * db_ip	(ocf:heartbeat:IPaddr2):	 Started node-01
        * db_service	(systemd:postgresql@16-main):	 Started node-01
  8. Confirm the floating IP address is present on the node running the group.
    $ ip addr show | grep -F "192.0.2.30"
        inet 192.0.2.30/24 brd 192.0.2.255 scope global secondary eth0
  9. Confirm PostgreSQL accepts connections via the floating IP address.
    $ pg_isready -h 192.0.2.30 -p 5432
    192.0.2.30:5432 - accepting connections

    If readiness fails, confirm PostgreSQL is listening on the floating IP and client access rules allow it.

  10. Run a failover test after the group is running.