nut-relay
A lightweight NUT (Network UPS Tools) relay/repeater that lets you bypass the client connection limit enforced by many consumer/SMB UPS units with a built-in NUT server (e.g. UniFi UPS, some APC/CyberPower "smart" models).
Why this exists
Some UPS units expose a NUT server directly, but cap the number of simultaneous clients allowed to connect (for example, 3 clients on the UniFi UPS 2U). If you have more machines than that to monitor and gracefully shut down on power loss, you hit that wall fast.
nut-relay solves this by sitting between your UPS and all your machines:
- It connects to your UPS's NUT server as a single client (using up just one of the few available slots).
- It runs its own NUT server, which has no artificial client limit.
- All your machines then connect to the relay instead of the UPS directly.
Your UPS (NUT server, limited clients)
│ single connection (repeater mode)
▼
nut-relay (this project)
│ unlimited connections
├── client 1
├── client 2
├── client 3
└── ... as many as you need
Under the hood, this uses NUT's dummy-ups driver in repeater/netclient
mode — despite the name, it relays real, live data from your UPS; it is not a
simulated device.
Requirements
- Docker + Docker Compose on the machine that will run the relay
- Network access from that machine to your UPS's NUT server (default port
3493) - The UPS's NUT device name, host/IP, and a NUT username/password with read access (usually configured in your UPS's own management interface)
Server Setup (the relay)
1. Create a project folder
mkdir nut-relay && cd nut-relay
2. Create docker-compose.yml
services:
nut-relay:
image: git.ericampire.app/ericampire/nut-relay:latest
container_name: nut-relay
restart: unless-stopped
network_mode: host
volumes:
- ./config:/etc/nut
network_mode: hostis recommended so the container shares the host's network stack directly — this matters if your UPS lives on a different network segment/VLAN than Docker's default bridge network can reach. If your setup doesn't need that, you can instead use aports:mapping:ports: - "3493:3493"(don't combine both —
ports:is ignored/redundant undernetwork_mode: host)
If you'd rather build the image yourself instead of pulling the published
one, replace image: with build: . and make sure the Dockerfile and
entrypoint.sh from this repo are in the same folder.
3. Create your config folder
cp -r config.example config
This gives you config/nut.conf, config/ups.conf, config/upsd.conf, and
config/upsd.users as a starting point — edit each one for your setup below.
4. Edit config/ups.conf
Point it at your actual UPS. The port value must be in the form
<remote_ups_name>@<remote_host>:<remote_port> for repeater mode to work
(a plain filename like xxx.dev would instead run in static/dummy mode).
[relayups]
driver = dummy-ups
port = <your_ups_nut_name>@<your_ups_ip>:3493
desc = "NUT relay/repeater"
Not sure of your UPS's NUT device name? Run:
docker run --rm --network host <image> upsc -l <your_ups_ip>
5. Edit config/upsd.users
One account for the relay-to-UPS connection, and one unique account per client you plan to connect:
[relay-to-ups]
password = <your_ups_nut_password>
upsmon primary
[client-01]
password = <unique_strong_password>
upsmon secondary
[client-02]
password = <another_unique_password>
upsmon secondary
Generate strong passwords with:
openssl rand -base64 24
6. Start the relay
docker compose up -d
docker compose logs -f
7. Verify
docker exec -it nut-relay upsc relayups@localhost
You should see real live values (battery charge, load, status, etc.) — not
Dummy Manufacturer placeholder data. If you see placeholder data, double
check the port = line in ups.conf.
Client Setup (each machine to protect)
Install nut-client natively on each machine you want to monitor and shut
down gracefully on power loss.
1. Install
apt install nut-client -y # Debian/Ubuntu
2. /etc/nut/nut.conf
MODE=netclient
3. /etc/nut/upsmon.conf
MONITOR relayups@<relay_ip>:3493 1 client-01 <unique_strong_password> secondary
MINSUPPLIES 1
SHUTDOWNCMD "/sbin/shutdown -h +0"
POLLFREQ 5
POLLFREQALERT 5
DEADTIME 15
NOTIFYFLAG ONLINE SYSLOG
NOTIFYFLAG ONBATT SYSLOG+WALL
NOTIFYFLAG LOWBATT SYSLOG+WALL+EXEC
NOTIFYFLAG FSD SYSLOG+WALL+EXEC
NOTIFYFLAG COMMBAD SYSLOG+WALL
NOTIFYFLAG COMMOK SYSLOG
NOTIFYFLAG SHUTDOWN SYSLOG+WALL
Use the matching account name/password you created for this client in the
relay's upsd.users.
4. Enable and start
systemctl enable nut-client nut-monitor
systemctl restart nut-client nut-monitor
5. Verify
systemctl status nut-monitor
upsc relayups@<relay_ip>:3493
A successful connection log line looks like:
UPS: relayups@<relay_ip>:3493 (secondary) (power value 1)
How the shutdown actually triggers
A common misunderstanding: going on battery does not shut anything down.
Power loss → status = OB (on battery) → just a notification, nothing shuts down
Low battery → status = LOWBATT → relay sends FSD to all clients
→ each client runs its SHUTDOWNCMD
primary vs secondary in upsd.users controls who is allowed to trigger
the forced shutdown broadcast — it does not control individual timing. All
secondary clients receive the same shutdown signal at the same time, based
on the low-battery threshold configured on the UPS itself. If you need a
staggered/cascaded shutdown order instead, look into NUT's upssched
(not covered by this basic relay setup).
Security notes
- Never commit your real
config/folder — onlyconfig.example/should be version-controlled. - Give every client its own unique password; don't reuse one password across machines.
- The relay-to-UPS account only needs read access; it does not need to be able to trigger shutdowns on the source UPS.