| name | pacemaker |
| description | Manages Pacemaker HA clusters with pcs and crmsh for resource provisioning, constraints, STONITH fencing, quorum configuration, and cluster lifecycle operations on two-node and multi-node setups. |
| license | MIT |
| compatibility | opencode |
| metadata | {"version":"1.0.0","domain":"linux","role":"implementation","scope":"infrastructure","output-format":"code","content-types":["code","guidance","config","do-dont"],"triggers":"pacemaker, pcs command, crmsh, cluster resource, STONITH fencing, CIB configuration, quorum management, promotable clone","archetypes":["tactical","diagnostic"],"anti_triggers":["brainstorming","vague ideation"],"response_profile":{"verbosity":"low","directive_strength":"high","abstraction_level":"operational"},"related-skills":"corosync, linux-services, storage-architecture","maturity":"stable","completeness":95,"exampleCount":4} |
Pacemaker HA Cluster Management
Senior Linux infrastructure engineer managing Pacemaker 3.x HA clusters with PCS and crmsh for resource provisioning, constraints, STONITH fencing, quorum configuration, and cluster lifecycle operations.
TL;DR Checklist
When to Use
Use this skill when:
- Building HA clusters — You're provisioning a two-node or multi-node Pacemaker cluster with Corosync and need end-to-end setup guidance
- Managing cluster resources — You need to create, migrate, stop, start, or troubleshoot OCF resource agents (VirtualIP, Apache, database, etc.)
- Configuring fencing — You must set up STONITH/fence devices (IPMI, iLO, DRAC, WTI) for a production cluster
- Fixing split-brain risk — A two-node cluster needs qdevice or corosync
two_node quorum configuration
- Troubleshooting clusters — Resources won't start, nodes show uncertain state, or CIB conflicts need resolution
- Implementing master/slave patterns — You're deploying promotable clones for databases (PostgreSQL, MySQL), DRBD, or other active/passive workloads
- Running cluster diagnostics — You need to generate reports with
crm_report or analyze CIB state
When NOT to Use
Avoid this skill for:
- Kubernetes clustering — Use Kubernetes native HA patterns instead; Pacemaker is not a container orchestrator
- Load balancing — Use HAProxy, Nginx, or cloud load balancers; Pacemaker manages node failover, not traffic distribution
- Single-node high availability — If only one server exists, you need backups/replication, not a cluster
- Application-level redundancy — Design your application to be stateless and multi-instance; Pacemaker is infrastructure-level failover
Use linux-services for systemd unit configuration that complements OCF resource agents. Use corosync for network-layer membership and token configuration.
Core Workflow
1. Cluster Initialization and Node Setup
Create the cluster from scratch with Corosync as the messaging layer.
sudo dnf install -y pcs pacemaker corosync
sudo systemctl enable --now pcsd
echo 'changeme' | sudo passwd hacluster --stdin
sudo pcs cluster auth node1 node2 -u hacluster -p 'changeme' --force
sudo pcs cluster setup --name mycluster node1 node2 --start --enable
Checkpoint: Run pcs status — all nodes should show as "Online" and the cluster state should be "Active." If any node is "Offline," check corosync logs: journalctl -u corosync --since "5 minutes ago".
2. Configure STONITH (Fencing) Devices
Every production cluster MUST have fencing configured before adding workloads.
sudo pcs stonith create fence-node1-ipmi \
stonith:fence_ipmilan \
ipaddr="192.168.1.100" \
login="admin" \
passwd="secret" \
pcmk_host_list="node1" \
power_wait="5" \
op monitor interval="60s"
sudo pcs stonith create fence-node2-ipmi \
stonith:fence_ipmilan \
ipaddr="192.168.1.101" \
login="admin" \
passwd="secret" \
pcmk_host_list="node2" \
power_wait="5" \
op monitor interval="60s"
sudo pcs property set stonith-enabled=true
sudo pcs property set pcmk_delay_base=30
sudo pcs property set pcmk_delay_max=60
sudo pcs stonith fence node1 --force
sudo pcs stonith fence node2 --force
Checkpoint: Both fence devices show as "Started" in pcs status resources. Each test fence action successfully powers cycles the target node (or reports success from the management interface).
3. Configure Quorum Strategy
Two-node clusters without qdevice are susceptible to split-brain. Choose a strategy:
sudo pcs qdevice setup alibi --init --nodes node1,node2
sudo pcs property set no-quorum-policy=ignore
Checkpoint: pcs status corosync shows correct membership count. pcs status quorum confirms expected votes match and the node count provides adequate quorum.
4. Create Resources with Constraints
Define virtual IP, application, and storage resources with proper ordering.
sudo pcs resource create VirtualIP ocf:heartbeat:IPaddr2 \
ip="192.168.1.200" \
cidr_netmask="24" \
op monitor interval="30s"
sudo pcs resource create Apache ocf:heartbeat:apache \
configfile="/etc/httpd/conf/httpd.conf" \
statusurl="http://localhost/server-status" \
op monitor interval="60s" \
op start timeout="60s" \
op stop timeout="60s"
sudo pcs resource create WebSite ocf:heartbeat:apache \
configfile="/etc/httpd/conf.d/webapp.conf" \
op monitor interval="30s"
sudo pcs constraint colocation add apache-with-vip VirtualIP Apache INFINITY
sudo pcs constraint order VirtualIP then Apache kind=Mandatory
Checkpoint: pcs status resources shows all three resources running on the same node. Verify with crm_simulate -s that the constraint graph is consistent.
5. Implement Promotable Clones (Master/Slave)
For databases and shared-storage workloads requiring active/passive failover.
sudo pcs resource create db-storage ocf:linbit:drbd \
drbd_resource=db0 \
op monitor interval="30s" role=Slave \
op promote interval="0s" role=Master
sudo pcs resource master db-master db-storage \
master-max=1 \
master-node-max=1 \
clone-max=2 \
clone-node-max=1 \
notify=true
sudo pcs constraint colocation add db-app-with-db-master WebSite with db-master INFINITY
sudo pcs constraint order db-master then WebSite kind=Mandatory
Checkpoint: pcs status resources shows one node as Master and the other as Slave for db-master. Failover test: pcs cluster standby node1 — verify that the database promotes on node2 within the expected window.
Implementation Patterns
Pattern 1: Complete Cluster Build Script (BAD vs. GOOD)
BAD — Manual step-by-step without error handling, skipping fencing, no idempotency
sudo systemctl enable pcsd
sudo systemctl start pcsd
echo "changeme" | passwd hacluster --stdin
pcs cluster setup mycluster node1 node2
pcs property set stonith-enabled=false
pcs resource create VIP ocf:heartbeat:IPaddr2 ip=10.0.0.50 op monitor interval=30s
Problems:
- No
set -euo pipefail — failures are silently ignored
- Disables STONITH — Red Hat does NOT support unfenced clusters; split-brain data corruption risk
- Hardcoded passwords in plain text without secure handling
- No authentication between nodes (pcs cluster auth missing)
- No quorum configuration for the cluster type
- No start/stop timeout definitions on resources
- Not idempotent — re-running will create duplicate resources or fail
GOOD — Idempotent, secure, production-ready with fencing and quorum
#!/usr/bin/env bash
set -euo pipefail
readonly CLUSTER_NAME="${1:?Usage: $0 <cluster_name> node1 node2 ...}"
readonly NODES=("${@:2}")
readonly QDEVICE_NODE="${4:-}"
if [[ ${#NODES[@]} -lt 2 ]]; then
echo "ERROR: At least 2 nodes required" >&2
exit 1
fi
echo "=== Pacemaker Cluster Setup: ${CLUSTER_NAME} ==="
echo "Nodes: ${NODES[*]}"
echo "[1/6] Installing packages..."
for node in "${NODES[@]}"; do
echo " → Ensuring packages on ${node}..."
ssh -o StrictHostKeyChecking=no root@"${node}" \
dnf install -y --setopt=install_weak_deps=false \
pcs pacemaker corosync || {
echo "ERROR: Failed to install packages on ${node}" >&2
exit 1
}
ssh root@"${node}" systemctl enable --now pcsd
done
echo "[2/6] Configuring node authentication..."
for node in "${NODES[@]}"; do
if ! ssh root@"${node}" pcs cluster auth >/dev/null 2>&1; then
echo " → Authenticating with ${node}..."
ssh root@"${node}" passwd hacluster --stdin <<< "${HA_CLUSTER_PASSWORD:-changeme}"
fi
done
echo "[3/6] Creating cluster: ${CLUSTER_NAME}..."
if ! pcs cluster auth --with-force "${NODES[@]}" -u hacluster \
-p "${HA_CLUSTER_PASSWORD:-changeme}" >/dev/null 2>&1; then
echo "ERROR: Node authentication failed. Verify hacluster password matches." >&2
exit 1
fi
if ! pcs cluster setup --name "${CLUSTER_NAME}" "${NODES[@]}" \
--start --enable --with-force; then
echo "ERROR: Cluster setup failed" >&2
exit 1
fi
echo "[4/6] Configuring fencing..."
for i in "${!NODES[@]}"; do
local node="${NODES[$i]}"
local ipmi_addr="${IPMI_ADDRESSES[$i]:-}"
if [[ -n "${ipmi_addr}" ]]; then
echo " → Creating fence device for ${node} (IPMI: ${ipmi_addr})"
pcs stonith create "fence-${node}" \
stonith:fence_ipmilan \
ipaddr="${ipmi_addr}" \
login="${IPMI_LOGIN:-admin}" \
passwd="${IPMI_PASS:-changeme}" \
pcmk_host_list="${node}" \
power_wait="5" \
op monitor interval="60s" || true
fi
done
pcs property set stonith-enabled=true 2>/dev/null || true
pcs property set pcmk_delay_base=30 2>/dev/null || true
pcs property set pcmk_delay_max=60 2>/dev/null || true
echo "[5/6] Configuring quorum..."
if [[ ${#NODES[@]} -eq 2 ]] && [[ -z "${QDEVICE_NODE}" ]]; then
echo " WARNING: Two-node cluster without qdevice — split-brain risk"
echo " Apply corosync two_node=1 in quorum configuration."
pcs property set no-quorum-policy=stop 2>/dev/null || true
elif [[ -n "${QDEVICE_NODE}" ]]; then
echo " → Setting up qdevice on ${QDEVICE_NODE}"
pcs qdevice setup alibi --init --nodes "${NODES[*]}" 2>/dev/null || true
pcs property set no-quorum-policy=ignore 2>/dev/null || true
fi
echo "[6/6] Verifying cluster state..."
sleep 5
if ! pcs cluster status >/dev/null 2>&1; then
echo "ERROR: Cluster is not responding. Check corosync and pacemaker logs." >&2
exit 1
fi
echo ""
echo "=== Cluster ${CLUSTER_NAME} Ready ==="
pcs status nodes || true
pcs stonith list || true
pcs property show stonith-enabled no-quorum-policy || true
Pattern 2: Constraint Configuration (BAD vs. GOOD)
BAD — Using resource groups when specific control is needed, missing ordering
sudo pcs resource group add web-stack VirtualIP Apache WebSite
sudo pcs constraint colocation add apache-with-vip VirtualIP Apache INFINITY
GOOD — Explicit colocation AND ordering with resource-level options
sudo pcs resource create VirtualIP ocf:heartbeat:IPaddr2 \
ip="192.168.1.200" \
cidr_netmask="24" \
op monitor interval="30s" \
meta failure-timeout="90s"
sudo pcs resource create Apache ocf:heartbeat:apache \
configfile="/etc/httpd/conf/httpd.conf" \
op monitor interval="60s" timeout="60s" \
op start timeout="60s" \
op stop timeout="60s" \
meta failure-timeout="90s"
sudo pcs constraint colocation add apache-with-vip \
VirtualIP Apache INFINITY
sudo pcs constraint order VirtualIP then Apache \
kind=Mandatory score=INFINITY
sudo pcs constraint location VirtualIP prefers node1 INFINITY
pcs constraint list --full
crm_simulate -s
Pattern 3: Cluster Diagnostic and Recovery Script
Bash — Automated cluster diagnostics with safe recovery
#!/usr/bin/env bash
set -euo pipefail
readonly REPORT_DIR="/var/tmp/pacemaker-diagnostics"
readonly TIMESTAMP="$(date +%Y%m%d-%H%M%S)"
mkdir -p "${REPORT_DIR}/${TIMESTAMP}"
echo "=== Pacemaker Cluster Diagnostics ==="
echo "Timestamp: ${TIMESTAMP}"
collect() {
local label="$1"; shift
echo "[+] Collecting: ${label}"
"$@" > "${REPORT_DIR}/${TIMESTAMP}/${label}.log" 2>&1 || true
}
collect "nodes_status" pcs status nodes
collect "full_status" pcs status
collect "resources_status" pcs status resources
collect "corosync_status" pcs status corosync
collect "quorum_status" pcs status quorum
collect "stonith_list" pcs stonith list
collect "stonith_config" pcs stonith config 2>/dev/null || true
collect "constraints" pcs constraint list --full
collect "properties" pcs property show
collect "cib_dump" pcs cluster cib 2>/dev/null || true
if command -v crm_report &>/dev/null; then
echo "[+] Generating crm_report..."
crm_report -f "${REPORT_DIR}/${TIMESTAMP}/crm_report.tar.gz" 2>/dev/null || true
else
echo "[-] crm_report not available — install pacemaker-cluster-libs"
fi
collect "corosync_log" journalctl -u corosync --no-pager -n 500
collect "pacemaker_log" journalctl -u pacemaker --no-pager -n 500
collect "pengine_log" journalctl -u pacemaker-ngd --no-pager -n 500 2>/dev/null || \
journalctl -u pacemaker-schedulerd --no-pager -n 500
echo ""
echo "=== Analysis ==="
STONITH_ENABLED=$(pcs property show stonith-enabled 2>/dev/null | grep -o 'stonith-enabled=[a-z]*' || echo "unknown")
echo "STONITH: ${STONITH_ENABLED}"
if [[ "${STONITH_ENABLED}" == "stonith-enabled=false" ]]; then
echo " WARNING: STONITH is DISABLED — production clusters require fencing!"
fi
FENCED_COUNT=$(pcs status | grep -c 'fencing' || true)
if [[ ${FENCED_COUNT} -gt 0 ]]; then
echo " WARNING: Nodes have been or are being fenced"
fi
echo ""
echo "Resource fail counts:"
crm_failcount show -r 2>/dev/null || echo " (crm_failcount not available)"
echo ""
echo "=== Recovery Options ==="
echo "1. Cleanup all resource failures: pcs resource cleanup"
echo "2. Cleanup specific resource: pcs resource cleanup <resource_name>"
echo "3. Clear failcount for a resource: crm_failcount delete -r <resource> [node]"
echo "4. Force stop/start of a resource: pcs resource manage <name>"
echo ""
echo "Diagnostic report saved to: ${REPORT_DIR}/${TIMESTAMP}/"
tar czf "${REPORT_DIR}/cluster-diagnostic-${TIMESTAMP}.tar.gz" \
-C "${REPORT_DIR}" "${TIMESTAMP}" 2>/dev/null || true
rm -rf "${REPORT_DIR}/${TIMESTAMP}"
echo "Compressed: ${REPORT_DIR}/cluster-diagnostic-${TIMESTAMP}.tar.gz"
Pattern 4: Promotable Clone Master/Slave Configuration (BAD vs. GOOD)
BAD — Missing notify and proper clone parameters causes failed failover
sudo pcs resource create db-storage ocf:linbit:drbd \
drbd_resource=db0 \
op monitor interval="30s" role=Slave
sudo pcs resource master db-master db-storage \
master-max=1 clone-max=2
sudo pcs resource create db-app ocf:heartbeat:lsb:postgresql \
op monitor interval="30s"
GOOD — Properly configured with all required parameters
sudo pcs resource create db-storage ocf:linbit:drbd \
drbd_resource=db0 \
op monitor interval="30s" role=Slave \
op monitor interval="20s" role=Master \
op promote interval="0s" role=Master \
op demote interval="0s" role=Slave
sudo pcs resource master db-master db-storage \
master-max=1 \
master-node-max=1 \
clone-max=2 \
clone-node-max=1 \
notify=true
sudo pcs resource create PostgreSQL ocf:heartbeat:pgsql \
pghost="/var/run/postgresql" \
pguser="postgres" \
repmode="sync" \
sync_level="1" \
op monitor interval="30s" timeout="60s" \
op start timeout="120s" \
op stop timeout="60s"
sudo pcs constraint colocation add pg-with-db-master \
PostgreSQL with db-master INFINITY
sudo pcs constraint order db-master then PostgreSQL kind=Mandatory
sudo pcs resource create DatabaseVIP ocf:heartbeat:IPaddr2 \
ip="192.168.1.50" cidr_netmask="24" \
op monitor interval="30s"
sudo pcs constraint colocation add vip-with-pg \
DatabaseVIP with PostgreSQL INFINITY
sudo pcs constraint order PostgreSQL then DatabaseVIP kind=Mandatory
Constraints
MUST DO
- MUST configure STONITH (fencing) on every production cluster — never run Pacemaker without fencing in production; Red Hat certification requires it
- MUST set
pcmk_delay_base and pcmk_delay_max properties for clusters with multiple simultaneous fence targets to prevent race conditions
- MUST define both colocation AND ordering constraints when resources must share a node — colocation alone does NOT guarantee startup sequence
- MUST use
pcs resource cleanup after recovering a node to clear stale failcounts and let the scheduler re-evaluate placements
- MUST test all fence devices with
pcs stonith fence <node> --force before declaring the cluster production-ready
- MUST configure quorum strategy appropriate to cluster size — two-node clusters need qdevice or corosync
two_node mode
- MUST use
pcs resource describe <type> and pcs stonith describe <type> to discover valid parameters before creating resources
- MUST use
crm_report -f report.tar.gz for diagnostics when troubleshooting unexpected behavior; include the report when seeking external support
- MUST enable monitor operations on every resource with appropriate intervals — unmonitored resources will not be recovered after failures
MUST NOT DO
- MUST NOT disable STONITH in production (
pcs property set stonith-enabled=false) — unfenced clusters risk data corruption from split-brain scenarios
- MUST NOT edit the CIB directly with
cibadmin for configuration changes — use pcs, crm_shadow, or cib-push --diff-against instead
- MUST NOT assume colocation implies ordering — they are separate constraint types; define both explicitly
- MUST NOT skip quorum planning for two-node clusters — without qdevice or
two_node: 1, split-brain will cause data corruption on shared storage
- MUST NOT use resource groups (
pcs resource group add) when you need per-resource control over monitor intervals, failure actions, or meta-options
- MUST NOT run
pcs cluster destroy on a node that is actively fencing — ensure resources have already migrated before destroying the cluster
- MUST NOT set
no-quorum-policy=fence as a default policy — this will fence ALL nodes if quorum is lost, causing cascading outages
- MUST NOT create promotable clones without
notify=true — dependent resources won't track promotion changes and may start on Slave nodes
Related Skills
| Skill | Purpose |
|---|
corosync | Configure Corosync messaging layer, membership, quorum, and network settings that Pacemaker depends on |
linux-services | Define systemd unit files that can be used as LSB or systemd OCF resource agents within Pacemaker |
storage-architecture | Design shared storage layouts (DRBD, LVM, multipath) for HA cluster workloads |
observability | Set up monitoring and alerting for cluster state, fencing events, and resource transitions |
Live References
Authoritative documentation links for Pacemaker HA clustering. The model follows markdown links at load time to resolve external references and inline content.