Cluster Guide

Build a high-availability cluster with ovios-cluster: a primary, one or more mirrored secondaries, and a floating IP that follows the primary
OviOS Linux v6 ovios-cluster ZFS Send/Receive · mTLS Heartbeats

1 How the Cluster Works

ovios-cluster builds an active/passive cluster on ZFS. One node is the primary: it serves your data and holds the floating virtual IP (VIP). The other nodes are replicas, which follow the primary and hold a mirror of its data. A replica can sit in the same rack or at your disaster-recovery site.

Primary
10.0.0.1
serves data · holds VIP 10.0.0.100
zfs send · recv
Replica
10.0.0.2
mirror of the primary
  • Replication is asynchronous, using zfs send -w | zfs recv, every 10 seconds by default.
  • Liveness is tracked with heartbeats over mutually authenticated TLS.
  • Witnesses are optional vote-only nodes. They hold no data, are never promoted and need no fencing, but they let the cluster tell a dead node from a network partition.
  • Failover moves the primary role, and the VIP, to a replica. It is manual by default and free. Automatic failover is a licensed feature.
ℹ️

HA and replication are separate tools. ovios-cluster mirrors a primary to secondaries and fails over. retadm replicates individual volumes and LUNs for backup, and works independently of a cluster.

What is free and what is licensed

Feature Free Licensed
Replication, retention, resume, status, metrics Yes
Fencing, quorum, epoch checks, receive guard, self-demotion Yes
Any number of nodes, witnesses, tiebreaker Yes
Planned maintenance with switchover (ovios-cluster switchover): a loss-free hand-over to a replica Yes
Manual failover (ovios-cluster promote) Yes
Automatic failover Subscription

A license unlocks the feature, not nodes, so adding or replacing nodes never needs a new license. Nothing that protects your data consults the license: it only decides whether a replica may promote itself automatically.

2 How It Keeps Data Safe

Failover is only useful if it never makes things worse. These are the protections built into the cluster.

Risk Protection
Two primaries (split brain) Every promotion raises a cluster epoch, saved before anything is exposed. The higher epoch always wins, so a returning old primary demotes itself instead of serving.
Old primary keeps writing Promotion fences it first (for example an IPMI power-off). A primary that loses quorum for lease_seconds also steps down by itself, and replicas wait longer than that before promoting.
A cut-off replica promotes wrongly It needs a strict majority of votes, and no reachable voter may still see the primary.
Replica too far behind Promotion is refused if the newest replicated snapshot is older than max_replica_lag_seconds.
Old primary overwrites the new one The receiving node refuses streams from a sender with an older epoch, an unknown sender, or for an excluded dataset.
Destructive "self-healing" A failed transfer is retried and resumed, but never destroys snapshots or forces a receive. Divergence is reported as DIVERGED and fixed only by an explicit resync.
Spoofed peers Heartbeats use mutual TLS with a per-cluster CA, and SSH host keys are pinned. A changed host key is a hard error.
⚠️

Rehearse before you rely on it. Build a staging pair first and test a failover, including pulling the primary's power, before using the cluster for production data.

3 Prerequisites

Requirement Details
Two or more OviOS nodes At least a primary and a replica, on IPv4 addresses and able to reach each other.
A free VIP An unused address on the same network as the nodes, optional but normally what clients connect to.
Root SSH key access The primary must reach every node as root without a password. See Passwordless SSH in the Replication Guide. The first contact pins the node's host key.
ZFS pools with the same names Each replica needs the destination pool for every replicated dataset. Replication fails with destination pool does not exist otherwise. Witnesses need no pools.
ovios-cluster on every node Installed at /usr/bin/ovios-cluster. add checks this, because the receiving side runs it over SSH as a guard.
For automatic failover A quorum of three or more voters (a witness node, or a tiebreaker IP), a fence_cmd on every data node, and a license.
💡

Two nodes cannot tell a dead peer from a partition. Add a witness (add --witness) or set a --tiebreaker IP such as your default gateway. Without one, automatic failover is rejected by validation, and a lone primary keeps serving when its peer dies.

ℹ️

On a stateless (live) node the cluster configuration is part of the node configuration saved to your pools, so it comes back at every boot. See the Live-OS guide.

4 Step 1 — Create the Cluster

All commands in this guide are run in the ovios-shell, where oc is a shorthand for ovios-cluster. On the node that should start as primary, run ovios-cluster init. It creates the cluster CA, a node identity and the configuration, and makes this node the primary.

PRIMARY — 10.0.0.1
ovios-shell ➤ ovios-cluster init --vip 10.0.0.100 --tiebreaker 10.0.0.254

Cluster initialized. Node 5d1f0c3a-... (10.0.0.1) is primary at epoch 1.
Cluster ID: <id> (needed to request a license for automatic failover)
Next: start the cluster daemon with 'options cluster.enable on' (or 'service ovios-cluster enable') in ovios-shell,
      then 'ovios-cluster add <ip>' for each other node.

Then start the cluster daemon with the cluster.enable option, or the equivalent service command, and check that it is running:

ovios-shell ➤ options cluster.enable on
option cluster.enable  on
ovios-shell ➤ service ovios-cluster status
ℹ️

service ovios-cluster enable does the same as options cluster.enable on: it starts the daemon and keeps it enabled, including across stateless reboots. The flag is saved in the node configuration, so config sync carries it to the other nodes.

Option Meaning
--vip The floating IP that follows the primary. Optional.
--interface Interface for the VIP. Default: the default-route interface.
--tiebreaker An IP (for example the gateway) that counts as an extra vote, so a two-node cluster can tell a partition from a dead peer.
--hbport Heartbeat port. Default 9064.
--force Overwrite an existing configuration, creating a new CA and identity.

Note the Cluster ID. A license is bound to it, not to hardware.

5 Step 2 — Add Nodes

Run ovios-cluster add on the primary for every other node. It pins the node's SSH host key, checks that the address really belongs to that host, checks that ovios-cluster (and, for data nodes, ZFS) is installed, issues the node a certificate and starts its daemon.

PRIMARY — 10.0.0.1
# a data node (replica)
ovios-shell ➤ ovios-cluster add 10.0.0.2
Node 5d1f0c3a-... (10.0.0.2) added as replica.

# a witness: votes only, no data, never promoted
ovios-shell ➤ ovios-cluster add --witness 10.0.0.3
⚠️

Cloned machines. If a node was cloned from another, add refuses a duplicate node ID. Remove /etc/ovios-cluster/node-id on the new machine and try again.

6 Step 3 — Configure and Sync

The configuration lives in /etc/ovios-cluster/ovios-cluster.yaml. Edit it on the primary only, then run ovios-cluster sync. Sync validates the file and pushes it to every node, and running daemons pick the change up automatically. An invalid file is rejected and the previous configuration stays in force.

The settings you will most often change:

# services started (in order) on promotion, stopped (in reverse) on demotion
services:
  - ovios-nfs.service
  - ovios-smb.service
  - ovios-iscsi.service

# every data node that may be fenced needs a command that really powers it off
nodes:
  - id: 5d1f0c3a-...
    ip: 10.0.0.1
    fence_cmd: ipmitool -I lanplus -H 10.0.1.1 -U admin -P secret chassis power off
  - id: 5d1f0c3a-...
    ip: 10.0.0.2
    fence_cmd: ipmitool -I lanplus -H 10.0.1.2 -U admin -P secret chassis power off
ovios-shell ➤ ovios-cluster sync

Editing the configuration safely

On the primary, service ovios-cluster edit opens the file in $VISUAL / $EDITOR (nano or vi if unset) on a private copy. When you close the editor the copy is checked with ovios-cluster sync --check --file; an invalid file is never saved and you can edit again or discard. A valid file replaces the live one (the old one is kept as ovios-cluster.yaml.bak) and you are asked whether to push it to the other nodes. If the live file changed while you were editing, it is not overwritten and your edit is kept in a temporary file.

ovios-shell ➤ service ovios-cluster edit

The check rejects bad YAML, misspelt (unknown) keys, inconsistent settings and automatic failover without a license. You can run it yourself on any file without pushing anything:

ovios-shell ➤ ovios-cluster sync --check --file /root/new.yaml
ℹ️

The edit command needs an ovios-cluster that supports sync --check. With an older binary it says so and saves nothing.

Configuration is synced automatically — including passwords

ovios-cluster sync above only pushes the cluster configuration file. The OviOS configuration (LUNs, volumes, shares, iSCSI targets, users and options) is a separate sync, and it keeps the replicas ready to take over:

🔄

1. It runs by itself, and can also be run by hand. The configuration is synced from the primary to the replicas automatically after every change you make on the primary: creating, mapping or unmapping a LUN, creating a volume, adding a share, and so on. You can also trigger it manually at any time, on the primary: sync-config cluster-sync.

🔑

2. Passwords are synced too. The passwords of root, ovios and every other local user, and the passwords of the SMB users, are copied from the primary to the replica(s) along with the configuration. Change passwords on the primary, so that the replicas, and the node that takes over after a failover, accept the same logins. Network settings and the ZFS host ID are never synced: each node keeps its own.

🔁

3. The same applies to retadm. With autosync.enable turned on, the configuration, including the passwords of local and SMB users, is also synced to your replication partners.

The cluster itself replicates data and dataset properties: sharesmb, sharenfs, mountpoint, quota and the LUN properties (type, mapping, id, target group, provisioning). ovios-cluster push-archive <file.tar.gz> distributes a configuration archive from the primary to every other data node.

ℹ️

Datasets of the operating system (rpool, bpool and their children) are excluded from replication by default. Add more under datasets.exclude if some data should not be mirrored.

7 Step 4 — Check Status

ovios-cluster status shows the whole cluster from any node (add --json for scripts).

ovios-shell ➤ ovios-cluster status
Cluster:   ovios-cluster
This node: e303ee93-c225-4e42-9e3d-068dfeecd858 (PRIMARY, applied: primary)
Primary:   e303ee93-c225-4e42-9e3d-068dfeecd858  epoch 21
Votes:     3/3  quorum
Failover:  automatic=true (licensed)
License:   expiring - license expires on 2026-11-12 (28 day(s) left): renew to keep automatic failover
Cluster ID: bcd94e2b-a6f6-432b-948d-77455f04fab7
VIP:       10.0.0.160 present locally=true
Last:      none: primary with valid lease

PEER                                  IP         TYPE     ALIVE  LAST    EPOCH  PRIMARY
3997fac1-40fa-4981-ad03-da5b7e18d981  10.0.0.25  data     true   0s ago  21     e303ee93-c225-4e42-9e3d-068dfeecd858
00c81f6a-30f5-4121-b54f-23d8469928a7  10.0.0.98  witness  true   0s ago  21     e303ee93-c225-4e42-9e3d-068dfeecd858

REPLICATION                           DATASET     LAG  STATE
3997fac1-40fa-4981-ad03-da5b7e18d981  data/lun01  14s  ok
3997fac1-40fa-4981-ad03-da5b7e18d981  data/lun02  1s   ok
3997fac1-40fa-4981-ad03-da5b7e18d981  data/lun03  14s  ok
3997fac1-40fa-4981-ad03-da5b7e18d981  data/vol01  14s  ok
3997fac1-40fa-4981-ad03-da5b7e18d981  data/vol02  13s  ok

This is a three-node cluster: the primary (this node), a data node holding the replicas, and a witness that only votes.

Replication state Meaning
ok Replicating normally. LAG is the age of the newest replicated snapshot.
error: … A transfer failed. It is retried automatically and resumed from a token.
DIVERGED The replica holds changes the primary does not have. Needs an explicit resync.
FENCED The receiving node refused the stream (older epoch, unknown sender or excluded dataset).
PROPERTIES NOT SYNCED Data is fine but a property could not be applied (for example a quota below current usage). The next cycle retries.

Cluster Stats page and prompt marker

On the primary only, the web dashboard shows a Cluster Stats button. It opens a page with the cluster status, votes, VIP and failover state, every node with its heartbeat age, the replication state of each dataset (lag, diverged, errors) and the last lines of the cluster log. It refreshes by itself, and on a replica or a node outside a cluster the page does not exist.

In the shell, the prompt shows the node's role — [PRIMARY] in red or [replica] in green — so you do not run a command on the wrong node. See shell.prompt in the Options Guide.

Metrics for Prometheus are available when metrics_listen is set: epoch, role, quorum votes, peer state, and per-dataset lag, divergence and errors.

8 Planned Maintenance (Switchover)

To do maintenance on the current primary, hand the primary role to a replica first with ovios-cluster switchover. It is loss-free: the primary stops serving, sends the replica its final state, and the replica takes over the VIP and the services. Nothing is fenced or powered off, and no write is lost. Switchover is free and needs no license.

PRIMARY — the node you want to do maintenance on
# 1. rehearse: runs every check and the pre-sync, but does not hand over
ovios-shell ➤ ovios-cluster switchover --check

# 2. do it (it asks for confirmation; -y skips it)
ovios-shell ➤ ovios-cluster switchover

# or choose the replica yourself (node ID or IP)
ovios-shell ➤ ovios-cluster switchover --host 10.0.0.2

Without --host, the cluster picks the healthiest replica: the one with the smallest replication lag, with the configuration order breaking ties.

What it checks first

The replica must be reachable, agree on who the primary is, replicate cleanly (no divergence, no errors, within max_replica_lag_seconds), and confirm over SSH that it can take over.

What it does

  1. Waits for replication. It blocks the next replication cycle and never interrupts a transfer in flight. Every ten seconds it says what is sending, what is queued and what replication is doing right now.
  2. Pre-sync while this node still serves, so that most data is already on the replica, and a replica that cannot receive is found before anything is stopped. If it fails, nothing has changed.
  3. Stops serving: services, VIP and datasets (read-only), step by step. This is the point of no return.
  4. Final sync of the now-final state to the replicas. It is short, thanks to the pre-sync.
  5. Hand-over. The replica takes over at the next epoch once it holds the final snapshot, and its own steps are shown while the old primary waits.
⚠️

Clients are disconnected from step 3 on, typically for seconds up to a minute. iSCSI initiators reconnect to the virtual IP on the new primary.

If something goes wrong, or you interrupt it

  • If anything fails before the replica has taken over, the old primary resumes as primary.
  • If it cannot be known whether the replica promoted (no answer, no heartbeat), the old primary stays down on purpose and says so. ovios-cluster promote on it resumes it.
  • Ctrl-C during steps 1 and 2 gives up: nothing is changed and replication continues. If your terminal swallows Ctrl-C (the OviOS shell does), run ovios-cluster switchover --abort from another session.
  • From the line "Point of no return" on, Ctrl-C is ignored. Stopping halfway could leave the cluster without a primary, so the switchover runs to the end, or undoes itself if something fails, even if the command is closed or the SSH session drops. Follow it with ovios-cluster status: the Last: line shows the step in progress.

When the maintenance is done, you can switch back the same way, from the new primary.

9 Manual Failover

Manual failover is free and needs no license. Run ovios-cluster promote on the replica that should take over.

ovios-shell ➤ ovios-cluster promote

Promotion runs as a sequence with rollback: safety checks, the pre_promote hook, fencing of the old primary, saving the new epoch, then mounting datasets, bringing up the VIP (with an ARP announcement), starting the configured services and running the post_promote hook. If a step fails, the local steps are undone and the epoch is never reused.

Without --force, promotion is refused while:

  • the primary is still reachable,
  • there is no quorum,
  • another node still sees the primary, or
  • the replicated data is older than max_replica_lag_seconds.
🛑

--force skips those checks and continues even if fencing fails. Use it only after you have confirmed that the old primary is really down.

ℹ️

Planned maintenance? Do not use promote on a healthy cluster. Use ovios-cluster switchover, which hands the role over without losing a write.

10 Manual Demotion

Normally the cluster demotes a node by itself. If it did not — for example a former primary that came back with writable datasets or the VIP still up — run ovios-cluster demote on that node. It stops replication and the configured services, removes the VIP, and unmounts the datasets and sets them readonly.

ovios-shell ➤ ovios-cluster demote
🛑

If the cluster still names this node as primary, demote refuses unless you add --force, because the cluster is then left without a serving primary. The node stays down until another node is promoted or promote is run on it again. Restarting the daemon also ends a forced demotion of the primary. Normally, promote another node first.

11 Automatic Failover

With a license, a replica promotes itself when the primary goes down, with no manual step, and only when it can be done safely: it needs a majority of votes, no voter may still see the primary, the old primary is fenced, and the data is recent enough. Only the first live candidate in configuration order promotes.

  1. Show the cluster ID and the license state:
    ovios-shell ➤ ovios-cluster license
  2. Email the cluster ID to ovi@ovios.org and ask for a free 30-day test license. You receive a license file.
  3. On the primary, install it. It is verified, installed and pushed to every node, and takes effect without a restart:
    ovios-shell ➤ ovios-cluster license install acme.license
  4. On the primary, set automatic_failover: true in the configuration and sync. This needs a quorum (a witness or tiebreaker) and a fence_cmd on every data node, and is refused without a valid license:
    ovios-shell ➤ ovios-cluster sync
  5. Confirm with ovios-cluster status: the Failover line should read automatic=true (licensed).
ℹ️

A primary that cannot serve on the VIP. The primary checks every 30 seconds that the VIP is assigned, that its link has a carrier, and, if you list vip_check_ports, that each of those TCP ports accepts a connection. A problem is shown as DEGRADED: virtual IP: … in status and in the ovios_cluster_vip_healthy metric. With automatic failover, a problem that lasts vip_failure_seconds (default 60) makes the primary hand over to a healthy replica using the same path as a planned switchover. The old primary is alive, so nothing is fenced and no data is lost. Without automatic failover the problem is only reported.

If a license expires

An expiring license never stops a running primary or replication.

  1. 30 days before expiry: status and the log warn. Everything keeps working.
  2. After expiry, during the grace period (7 days by default, set per license): still works, with a louder warning.
  3. After the grace period: automatic failover turns off and the cluster behaves as manual failover. status shows configured but NOT ACTIVE, and promote works as usual. Installing a renewal re-enables it immediately.

Licenses are signed documents verified on your own systems, so no connection to us is needed. License terms are in the Terms & License.

12 After a Failover

Replication is asynchronous, so the old primary may hold writes that were never replicated. When it comes back it demotes itself, and replication reports it as DIVERGED. Datasets are never overwritten without an explicit decision from you.

  1. Run ovios-cluster status on the new primary and find the diverged replica.
  2. Check what is on the old primary and decide whether any of that data should be kept.
  3. Roll the old primary back and re-sync it from the new primary. This discards its divergent changes:
    ovios-shell ➤ ovios-cluster resync 10.0.0.1 --discard-divergent-data
    resync accepts a node's ID or its IP address.
💡

Automatic rollback. If you would rather not run resync by hand, set replication.auto_rollback: true. The primary then rolls back the node that was primary before it (only that node, and only to the last common snapshot, never a full replace) when it returns. What that node wrote after losing the role is discarded. A returning node that was not the previous primary, or has no common snapshot, still needs resync.

Restarting the daemon never changes a node's role and never drops the VIP.

13 Command Reference

Command Where Description
init [--vip] [--tiebreaker] First node Create a cluster; this node becomes the primary.
add <ip> [--witness] [--fence-cmd CMD] Primary Enrol a data node, or a vote-only witness. A data node needs a fence command while automatic failover is on.
sync Primary Validate the configuration and push it to every node.
sync --check [--file F] Any node Only validate (YAML, unknown keys, rules, license); pushes nothing. Used by service ovios-cluster edit.
status [--json] Any node Show roles, quorum, peers, license and replication state.
switchover [--host H] [--check] [-y] / --abort Primary Planned, loss-free hand-over of the primary role to a replica. --check only rehearses, --abort gives up a switchover that is still waiting for replication.
promote [--force] The replica to take over Manual failover, when the primary is down.
demote [--force] The node to demote Stop services, drop the VIP, unmount the datasets and set them readonly. --force is needed if the cluster still names this node primary.
resync <node> --discard-divergent-data Primary Roll a diverged replica back and re-sync it. Discards its changes.
push-archive <file.tar.gz> Primary Extract a configuration archive on every other data node.
license / license install <file> Primary Show the cluster ID and license state, or install a license (it is pushed to every node).
remove <node-id-or-ip> Primary Remove a node from the cluster.
destroy --yes [--all] Any node Dismantle the cluster configuration on this node (with --all, every node). Your datasets are kept.

14 Key Options

Set in /etc/ovios-cluster/ovios-cluster.yaml on the primary, then run ovios-cluster sync. Unknown keys are an error, so a misspelt option is not silently ignored. To check a file without pushing it, run ovios-cluster sync --check.

Option Default Description
Network and VIP
vip none The floating address that follows the primary, as an IP or in CIDR form. Needs interface.
interface default route Interface that carries the VIP. A node can override it with nodes[].interface.
vip_check_ports none TCP ports your clients use on the VIP (for example 3260, 445, 2049). The primary checks that each accepts a connection. Needs vip.
vip_failure_seconds 60 How long VIP trouble may last before the primary hands over to a healthy replica (automatic failover only). Minimum 30.
heartbeatport "9064" Port of the mutual-TLS heartbeat, bound to the node's own address.
Failover
automatic_failover false Licensed. Needs a quorum, a fence_cmd on every data node and at least two data nodes.
heartbeat_interval 2 s Seconds between heartbeats and between engine ticks.
lease_seconds 15 A primary without quorum steps down after this; a replica waits this plus half again before promoting. At least three times heartbeat_interval.
max_replica_lag_seconds 300 promote, automatic failover and switchover refuse a replica whose newest replicated snapshot is older.
tiebreaker none An IP, such as the gateway, counted as an extra vote so a two-node cluster can tell a dead peer from a partition.
nodes[].fence_cmd none Shell command that powers the node off (for example ipmitool). Has 90 seconds. Required on every data node with automatic failover. A switchover or VIP hand-over never fences.
Replication
datasets.exclude boot and root pools Datasets that are never replicated. The receiving node refuses them too.
replication.interval 10 s Seconds between replication cycles.
replication.snapshot_prefix ovios Prefix of the snapshots the cluster creates and prunes.
replication.workers 4 Transfers that run at the same time.
replication.timeout_seconds 3600 Longest one dataset-to-node transfer may run. An interrupted transfer resumes from its token.
replication.independent_datasets false Each dataset transfers on its own, so one huge dataset no longer delays the others. Keep workers at least as high as the number of datasets.
replication.auto_rollback false Roll the previous primary back to the last common snapshot when it returns with writes that never reached the new primary. Those writes are discarded.
replication.retention.keep_last 12 Newest snapshots kept. A node that is away keeps its base as a bookmark or a pinned snapshot, so it can catch up incrementally.
Services, hooks and monitoring
services none Units started in order on promotion and stopped in reverse on demotion.
hooks none pre_promote (a failure aborts), post_promote, pre_demote, post_demote. Run with OVIOS_NODE_ID, OVIOS_ROLE, OVIOS_EPOCH, OVIOS_VIP and OVIOS_CLUSTER set.
metrics_listen off Address of the Prometheus endpoint, for example ":9100". See Alerting and Metrics.
binary_path /usr/bin/ovios-cluster Where the binary is on every node. The receive guard runs it over SSH.

15 Alerting and Metrics

Every node can expose its cluster state as Prometheus metrics, so you can graph it and get alerted before a problem reaches your clients. Metrics are free. All of them are gauges, and each carries the label node="<this node's ID>".

Turn on the endpoint

Set metrics_listen in the cluster configuration on the primary, sync it, and restart the daemon (a change of this option needs a restart). The endpoint is then http://<node>:9100/metrics.

# /etc/ovios-cluster/ovios-cluster.yaml
metrics_listen: ":9100"
ovios-shell ➤ ovios-cluster sync
ovios-shell ➤ service ovios-cluster restart    # on every node
⚠️

Mind what you bind to. 127.0.0.1:9100 answers only requests made on the node itself, so a Prometheus server on another machine gets connection refused. The setting is part of the cluster configuration, which is the same on every node, so use ":9100" (all addresses) rather than one node's IP. The endpoint is plain HTTP without authentication, so restrict who can reach the port with a firewall. Check that it listens with ss -ltn | grep 9100. 9100 is only an example: use any free port.

Which metrics are exposed

This node

Metric Meaning
ovios_cluster_epoch Current cluster epoch.
ovios_cluster_is_primary 1 if this node is the primary.
ovios_cluster_role_applied 1 if the machine matches the role in the cluster state (VIP, services and mounts as they should be).
ovios_cluster_suspended 1 if this primary stepped down after losing quorum.
ovios_cluster_degraded 1 if the node reports any degraded condition (failed services, VIP problem).
ovios_cluster_votes_have Voters reachable, including this node.
ovios_cluster_votes_total Total voters.
ovios_cluster_quorum 1 if a strict majority of voters is reachable.

VIP

Metric Meaning
ovios_cluster_vip_present 1 if the VIP is configured on this node. Expect 1 on the primary and 0 on replicas.
ovios_cluster_vip_healthy 1 if there is no VIP problem: the address is assigned, the link has a carrier and every port in vip_check_ports answers. Always 1 on replicas.

License

Metric Meaning
ovios_cluster_license_autofailover 1 if the license currently permits automatic failover.
ovios_cluster_license_expiry_timestamp_seconds Unix time the license expires. Only present when a license is installed.

Per peer

With the extra label peer="<id>": one series per other node, witness included.

Metric Meaning
ovios_cluster_peer_up 1 if an authenticated heartbeat from the peer arrived recently.
ovios_cluster_peer_ssh_up 1 if the peer's SSH port answers at its configured address. 0 means replication, switchover and sync to it will fail. Appears about 2 seconds after startup, then is checked every 30 seconds.
ovios_cluster_peer_heartbeat_on_configured_address 1 if the peer's heartbeats come from its configured address, 0 if from another one. Only present for alive peers.

Replication

With the extra labels target="<node id>" and dataset="<name>". Meaningful on the primary.

Metric Meaning
ovios_cluster_replication_lag_seconds Seconds since the last successful replication. -1 if it never succeeded.
ovios_cluster_replication_diverged 1 if the target holds data the primary does not and needs resync.
ovios_cluster_replication_error 1 if the last attempt failed.
ovios_cluster_replication_props_error 1 if the data is in sync but the properties could not be copied.
ℹ️

Events are in the log, not in metrics. There are no counters for failovers, switchovers, VIP hand-overs, fence calls or replication throughput. Those events are in the daemon log (election.promote, promote.done, switchover.*, vip.handover).

💡

Peer status never triggers a failover. A peer counts as alive when an authenticated heartbeat arrives from any address, so a single dead address does not cause a failover. But replication, switchover, sync, add and remove use SSH to the node's configured address, which can fail while heartbeats look fine. Alert on ovios_cluster_peer_ssh_up and ovios_cluster_peer_heartbeat_on_configured_address to catch that. For production, put the heartbeat on its own redundant network.

Set up Prometheus

OviOS can run the Prometheus service on the node itself: options prometheus.enable on starts it now and at every boot (port 9090, manage it with service prometheus, configuration in /etc/prometheus, which ships with a prometheus.yml). You can equally use a Prometheus server elsewhere. Either way, it has to reach port 9100 of every node, witnesses included.

💾

Keep your Prometheus files in /etc/prometheus. That whole directory is persisted across reboots, so it is the place for prometheus.yml, the alert rules file and any other Prometheus file you add. Files saved anywhere else are lost at the next reboot of a stateless (live) node.

  1. Edit the /etc/prometheus/prometheus.yml that ships with OviOS: add the nodes as a scrape job (under scrape_configs), and point rule_files at your rules:
    scrape_configs:
      - job_name: ovios-cluster
        scrape_interval: 15s
        static_configs:
          - targets: ['10.0.0.1:9100', '10.0.0.2:9100', '10.0.0.3:9100']
    
    rule_files:
      - /etc/prometheus/rules/ovios-cluster.yml
  2. Save the alert rules below as /etc/prometheus/rules/ovios-cluster.yml, inside the persisted /etc/prometheus directory.
  3. To be notified (email, chat, paging), point Prometheus at an Alertmanager. Without one, firing alerts are still shown on the Alerts page of Prometheus:
    alerting:
      alertmanagers:
        - static_configs:
            - targets: ['alertmanager.example.com:9093']
  4. Restart Prometheus and check that all targets are UP under Status → Targets:
    ovios-shell ➤ service prometheus restart

Example alert rules

A starting point that covers quorum, roles, the VIP, peers, replication and the license. Adjust the for: durations and thresholds to your environment. In particular, set the replication lag threshold to match your max_replica_lag_seconds, and drop the license alerts if you run without a license.

groups:
  - name: ovios_cluster_alerts
    rules:

      # --- CLUSTER HEALTH & QUORUM ---
      - alert: OviosClusterQuorumLost
        expr: ovios_cluster_quorum == 0
        for: 1m
        labels:
          severity: critical
        annotations:
          summary: "Cluster quorum lost on node {{ $labels.node }}"
          description: "A strict majority of voters is no longer reachable. Failover and write actions may fail."

      - alert: OviosClusterMissingVoters
        expr: ovios_cluster_votes_have < ovios_cluster_votes_total
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "Cluster is missing active voters"
          description: "Node {{ $labels.node }} reaches only {{ $value }} of its voters."

      - alert: OviosClusterNoSinglePrimary
        expr: sum(ovios_cluster_is_primary) != 1
        for: 1m
        labels:
          severity: critical
        annotations:
          summary: "The cluster has no primary, or more than one"
          description: "{{ $value }} nodes report that they are the primary."

      - alert: OviosClusterNodeDegraded
        expr: ovios_cluster_degraded == 1
        for: 2m
        labels:
          severity: warning
        annotations:
          summary: "Node {{ $labels.node }} reports a degraded condition"
          description: "Check ovios-cluster status and the daemon log on the node."

      - alert: OviosClusterRoleMismatch
        expr: ovios_cluster_role_applied == 0
        for: 2m
        labels:
          severity: major
        annotations:
          summary: "Node role mismatch on {{ $labels.node }}"
          description: "The local machine does not match the role assigned in the cluster state."

      - alert: OviosClusterPrimarySuspended
        expr: ovios_cluster_suspended == 1
        for: 1m
        labels:
          severity: critical
        annotations:
          summary: "Primary suspended on {{ $labels.node }}"
          description: "This primary has stepped down after losing quorum."

      - alert: OviosClusterVipUnhealthy
        expr: ovios_cluster_vip_healthy == 0
        for: 1m
        labels:
          severity: critical
        annotations:
          summary: "Node {{ $labels.node }} cannot serve clients on the VIP"
          description: "The VIP is not assigned, its link has no carrier, or a checked port does not answer."

      # --- PEER & CONNECTIVITY ---
      - alert: OviosClusterPeerDown
        expr: ovios_cluster_peer_up == 0
        for: 2m
        labels:
          severity: critical
        annotations:
          summary: "Cluster peer {{ $labels.peer }} is down"
          description: "Node {{ $labels.node }} has lost heartbeat connectivity to peer {{ $labels.peer }}."

      - alert: OviosClusterPeerSSHDown
        expr: ovios_cluster_peer_ssh_up == 0
        for: 2m
        labels:
          severity: major
        annotations:
          summary: "Cluster peer {{ $labels.peer }} SSH unreachable"
          description: "SSH to peer {{ $labels.peer }} failed from node {{ $labels.node }}. Replication and switchover will fail."

      # --- REPLICATION & STORAGE ---
      - alert: OviosReplicationError
        expr: ovios_cluster_replication_error == 1
        for: 5m
        labels:
          severity: major
        annotations:
          summary: "Replication error on dataset {{ $labels.dataset }}"
          description: "Replication from {{ $labels.node }} to target {{ $labels.target }} is failing for dataset {{ $labels.dataset }}."

      - alert: OviosReplicationDiverged
        expr: ovios_cluster_replication_diverged == 1
        for: 1m
        labels:
          severity: critical
        annotations:
          summary: "Replication diverged for dataset {{ $labels.dataset }}"
          description: "{{ $labels.target }} holds data that {{ $labels.node }} does not. It needs resync."

      - alert: OviosReplicationHighLag
        expr: ovios_cluster_replication_lag_seconds > 3600
        for: 10m
        labels:
          severity: warning
        annotations:
          summary: "High replication lag on dataset {{ $labels.dataset }}"
          description: "Replication lag has exceeded 1 hour (currently {{ $value }} seconds)."

      - alert: OviosReplicationNeverSucceeded
        expr: ovios_cluster_replication_lag_seconds == -1
        for: 10m
        labels:
          severity: warning
        annotations:
          summary: "Replication of {{ $labels.dataset }} has never succeeded"
          description: "No successful replication of {{ $labels.dataset }} to {{ $labels.target }} yet."

      # --- LICENSE ---
      # Only useful if you hold a license: a free cluster always reports 0.
      - alert: OviosLicenseNoAutoFailover
        expr: ovios_cluster_license_autofailover == 0
        for: 15m
        labels:
          severity: warning
        annotations:
          summary: "Automatic failover is not permitted by the license"
          description: "The installed license, or the lack of one, does not permit automatic failover."

      - alert: OviosLicenseExpiringSoon
        expr: (ovios_cluster_license_expiry_timestamp_seconds - time()) < (30 * 24 * 3600)
        for: 1h
        labels:
          severity: warning
        annotations:
          summary: "OviOS cluster license expires within 30 days"
          description: "The license seen by node {{ $labels.node }} expires in less than 30 days. Please renew."
Alert What to do
Quorum lost, missing voters, peer down Find the node or the network path that is down. A primary without quorum steps down by itself after lease_seconds.
No single primary Run ovios-cluster status on each node. With no primary, promote a replica once you are sure the old one is down. Two primaries should not happen: the higher epoch wins.
Peer SSH unreachable Fix SSH to the peer's configured address before you need a switchover or a sync.
VIP unhealthy, node degraded Clients may not be able to connect. See the DEGRADED: line of status.
Replication error, high lag Check the network and the daemon log. A replica that is too far behind is refused for failover.
Replication diverged See After a Failover: decide what to keep, then resync.
License expiring Install the renewal with ovios-cluster license install. It takes effect at once.

16 Limits to Know

  • Asynchronous replication. A failover can lose up to the replication interval plus the cycle time of writes, bounded by max_replica_lag_seconds.
  • Active/passive. One primary serves data at a time.
  • IPv4 only for node addresses.
  • Node certificates are valid for 5 years and are not rotated automatically.
  • Root SSH. Nodes authenticate to each other over SSH with root's key.
  • Clocks. License dates are checked against the node's clock, so keep time in sync.

17 Troubleshooting

Symptom Cause and fix
add: "not installed on …" /usr/bin/ovios-cluster is missing on the new node. Install it there first.
add: "zfs is not installed" The node is not a data node. Use --witness if it should only vote.
add: "does not appear to be a local address" The IP you gave is not that host's own address. Check for NAT or a wrong address.
"destination pool … does not exist" Create the pool, with the same name, on the replica.
sync refuses the configuration Automatic failover needs a valid license, a quorum (witness or tiebreaker) and a fence_cmd on every data node.
promote refuses The primary is reachable, there is no quorum, another node still sees the primary, or the data is too old. Fix the cause, or use --force only after confirming the old primary is down.
switchover refuses The replica is unreachable, lags more than max_replica_lag_seconds, has divergence or replication errors, or cannot confirm over SSH that it can take over. Run switchover --check to see which.
A remote Prometheus gets "connection refused" metrics_listen is 127.0.0.1:…. Use ":9100", then restart the daemon.
Status shows "quorum not possible" Add a witness or set a tiebreaker.
Status shows "Daemon is NOT running" service ovios-cluster enable (or options cluster.enable on). Status falls back to the last known state from disk.
Status shows "configured but NOT ACTIVE" The license is missing, or expired past its grace period. Install or renew it.
💡

Want to see automatic failover work? Email ovi@ovios.org for a free 30-day test license.