# Multi-target Failover (https://portunus.bybee.dev/en/docs/features/multi-target-failover)



Available since v0.7.0. Extends `Rule.targets[]` from a single target to
an ordered list of up to 8.

## How it works [#how-it-works]

* **Lower priority number = higher preference**. The client always
  attempts the highest-preference healthy target first, then walks the
  remaining targets in priority order on failure.
* **Passive failure detection**: 3 consecutive connect failures within
  30 s flips a target to `Failed`; 2 consecutive successes flip it back.
* **Active probes** (optional, per rule): set
  `health_check_interval_secs` (range `1..=3600`) to drive periodic
  TCP-connect probes from the client.
* **All-failed fallback**: when every target is `Failed`, the client
  still attempts the highest-priority target rather than giving up.
* All health state is **client-side and ephemeral** — it does not
  survive a client restart.

## Push a multi-target rule [#push-a-multi-target-rule]

```sh
# Repeat --target; lower priority wins
portunus-server push-rule edge-01 8443 \
  --target primary.example.com:443@1 \
  --target backup.example.com:443@2 \
  --target standby.example.com:443@3

# Or pass a JSON literal
portunus-server push-rule edge-01 8443 --targets-json '[
  {"host":"primary.example.com","port":443,"priority":1},
  {"host":"backup.example.com","port":443,"priority":2}
]'
```

The HTTP API accepts a `targets[]` array on `POST /v1/rules`.
The Web UI exposes an "Add another target" button on the rule push form.

## Single-target rules are unchanged [#single-target-rules-are-unchanged]

Single-target rules stay byte-identical to v0.6.0 — multi-target lives in
a separate code path entered via `match targets.len()` at rule
activation, so existing throughput is unaffected.

## Per-target stats [#per-target-stats]

Per-target byte counters surface **only on demand** to keep the
Prometheus cardinality bounded:

```sh
# CLI (single-target rules print a no-op note and exit 0)
portunus-server rule-stats <id> --per-target

# HTTP
curl -H "Authorization: Bearer $PORTUNUS_OPERATOR_TOKEN" \
     'http://127.0.0.1:7080/v1/rules/<id>/stats?per_target=true'

# SSE (live stream)
curl -N -H "Authorization: Bearer $PORTUNUS_OPERATOR_TOKEN" \
     'http://127.0.0.1:7080/v1/rules/<id>/stats/stream?per_target=true'
```

The Web UI rule detail page shows health badges (`Healthy / Failed`),
last-failure / last-success timestamps, and per-target byte counters
that update on the existing 5-second SSE cadence.

## Default `/metrics` [#default-metrics]

One always-on counter:
`portunus_rule_target_failovers_total{client,rule,owner}` captures
Healthy↔Failed transitions per rule. Per-target byte counters are
**never** in the default `/metrics` series.

## Capability gate [#capability-gate]

Multi-target push to a v0.6.0-or-earlier client is rejected at the HTTP
layer with `422 multi_target_unsupported_by_client`.

## Performance [#performance]

Since v1.7.0 the live byte counters and the Linux `splice(2)` fast path
also apply to the multi-target failover path; per-target accounting is
reconciled centrally and closed correctly on error.
