Repository navigation
Shutdown Targets
!!! warning "This per-UPS shutdown_targets[] array is LEGACY"
New enrollments use the per-machine monitored_machines[].shutdown_method
model (none/native/serial/ssh, via monitor add --method ...) —
see Configuration. shutdown_targets[] is still parsed
so pre-existing config files keep loading, but a new entry should not be
written here. kind: "serial" maps onto shutdown_method: "serial";
kind: "remote" maps onto shutdown_method: "ssh"; kind: "local" has no
monitored_machines equivalent — the watcher host is never enrolled as a
machine, so a local target stays the only way to describe its own
shutdown.
A shutdown target is a machine the orchestrator knows how to power down. Each
UPS can have any number, and target entries are only transports: they say how
to reach a device. The top-level shutdown policy decides whether any target
command is allowed to run. That policy is disabled by default.
The host running the daemon is protected separately by NUT's own
upsmon SHUTDOWNCMD; orchestrator targets are optional extra actions for the
devices a UPS feeds.
!!! note "SSH is the fallback, not the primary path"
Native NUT (upsmon secondary, enrolled via monitor add) is the primary
graceful-shutdown mechanism; the remote (SSH) / serial targets here are a
configurable, default-off backup. See
SSH vs. native NUT for the full analysis, the
failure modes, and when each path applies.
Deferred: a native-plus-deadman regime, where a legacy target fired only as
a last resort strictly below a native secondary's own LB point, was scoped for
this phase and dropped — it may return in a future phase. (It was also
never actually implemented: battery_below/runtime_below on a target are
parsed but not consulted at runtime — see Triggers, below.)
These were live-environment unknowns resolved to defaults, not blockers:
- DEADTIME 30 on secondaries (the ≥3×POLLFREQ floor for POLLFREQ 5).
-
Per-host nft scope — each secondary's source IP added to the dedicated
table inet ups_orchestratorset, not a broad allow. -
Serial-cable presence unknown → legacy target
kinddefaults toremote; switch toserialwhere a console cable exists (network-independent, survives a dark switch). -
nutgroup present → snippet/secret files at0640 root:nut, with a0600 root:rootfallback where the group is unavailable.
-
serial: send the command over a serial console (device+baud) into a passwordless or auto-login getty. This needs no network, so it keeps working during an outage when the router/switch is also dark. It's the best primary path for a server you can reach over a console cable. -
remote: run the command over SSH.hostcan be a real hostname or anssh_configHost alias; leaveuserempty to connect as justssh <alias>, which keeps the real hostname/port/key in~/.ssh/config. -
local: the host the daemon runs on. Local targets run after every enabled serial/remote target on the same UPS, so the watcher dies last.
Auto-shutdown is controlled from one top-level surface:
-
shutdown.enabledmust betrue. - The relevant group must be enabled:
-
shutdown.external.enabledforserialandremote. -
shutdown.internal.enabledforlocal.
-
- The UPS must be on battery when
require_power_outageis true. - The UPS must have been on battery for at least
min_on_battery_seconds. - The UPS must be close to empty by the group's central
battery_belowandruntime_belowthresholds.
If battery percent and runtime are both available, both must be at or below the group threshold. This prevents a shutdown at, for example, 50% battery while the UPS still reports 30 minutes of runtime. If one reading is unavailable, the available threshold is used.
An explicit remote_shutdown event still respects this policy; it does not
bypass disabled groups or the close-to-empty gate.
!!! danger "Match your console's ACTUAL baud — a wrong value is silent"
Read the real rate off the far end: systemctl cat serial-getty@<tty>.service
on that machine. stty only configures the local
tty and returns success at essentially any recognised rate on the same
physical line, so a wrong baud here sends garbage down the wire and the
orchestrator reports success (rc 0) anyway — it cannot detect a far-end
speed mismatch, only a rejected local line configuration. Declare the real
speed of the console you are wiring to, not a value copied from an example.
{
"name": "bigserver",
"kind": "serial",
"enabled": true,
"device": "/dev/serial/by-id/usb-FTDI_FT232R_USB_UART-if00-port0",
"baud": 9600,
"cmd": "sudo /sbin/shutdown -h now"
}- serial: an auto-login getty on the target's serial port. Scope the auto-login to that serial tty only; don't loosen SSH or the physical console.
-
remote: key-based SSH (BatchMode) and a passwordless
shutdownfor the SSH user. -
local: a
sudoers.drule for passwordlessshutdown/poweroff, whichdeploy/install.shwrites for the run user.