Protect Your Homelab: Setting Up Network UPS Tools (NUT) Across Proxmox and Mac ๐
Ever had that sinking feeling when you realize your entire homelab just went down during a power outage? Yeah, me too. After one too many unexpected shutdowns (and the subsequent anxiety of checking if all my services came back up properly), I decided it was time to set up proper UPS monitoring with automatic graceful shutdowns. Let me walk you through how I configured Network UPS Tools (NUT) to protect both my Proxmox server and Mac over a Tailscale mesh network.
Why This Matters ๐ฏ
When the power goes out, your UPS keeps things running on battery – but that battery won’t last forever. Without proper monitoring, your servers will either:
- Run until the battery dies and everything crashes (bad!)
- Require manual intervention to shut down gracefully (impossible if you’re not home)
NUT solves this by monitoring your UPS and automatically shutting down your systems in the correct order when the battery gets low. Your Proxmox server and Mac will gracefully power down, preserving your data and avoiding corruption.
Key Considerations:
- Master-slave architecture: One system connects directly to the UPS (master), others monitor via network (slaves)
- Graceful shutdown ordering: Slaves shut down first, then the master
- Secure communication: Authentication between systems
- Network reliability: Tailscale provides consistent IPs even when traveling
What You’ll Need:
- CyberPower UPS with USB connection
- Proxmox server (or any Linux server)
- Mac (or any macOS device)
- Tailscale mesh network configured
- Root/sudo access to both systems
- About 30 minutes
Architecture Overview ๐
Here’s how the pieces fit together:
Proxmox Server (Master)
- UPS connected via USB cable
- Runs NUT server (
upsd) to share UPS data - Monitors UPS status locally
- Signals shutdown to Mac when battery is critical
- Shuts down last (after slaves)
Mac (Slave)
- Connects to Proxmox NUT server over Tailscale
- Monitors UPS status remotely
- Receives shutdown signal from Proxmox
- Shuts down first to give Proxmox time to finish
Think of it like a coordinated evacuation plan – the Mac gets the signal to leave first, and the Proxmox server makes sure everyone’s out before turning off the lights.
Part 1: Configure Proxmox Server (NUT Master) ๐ฅ๏ธ
The Proxmox server is physically connected to the UPS, so it becomes our “master” that monitors the hardware directly and shares that information with other systems.
Step 1: Detect Your UPS
First, let’s make sure Proxmox can see your UPS:
sudo nut-scanner -q
You should see output showing your CyberPower UPS with details like this (for example, I use this UPS – no affiliate link is attached):
[nutdev1]
driver = "usbhid-ups"
port = "auto"
vendorid = "0764"
productid = "0601"
product = "CP850PFCLCDa"
vendor = "CPS"
Pro Tip: Don’t worry about warning messages regarding SNMP, XML, or IPMI libraries – those are for enterprise network-attached UPS units. Your USB-connected UPS only needs the usbhid-ups driver.
Step 2: Install NUT
apt update
apt install nut
Step 3: Configure the UPS Driver
Create the UPS configuration file:
nano /etc/nut/ups.conf
Add this configuration (using your vendor/product IDs from the scanner):
maxretry = 3
[cyberpower]
driver = usbhid-ups
port = auto
vendorid = 0764
productid = 0601
desc = "CyberPower UPS"
Note: The section name [cyberpower] is your UPS identifier – you’ll reference this in other configuration files.
Step 4: Set NUT to Network Server Mode
Edit the NUT configuration:
nano /etc/nut/nut.conf
Change the mode to allow network clients:
MODE=netserver
Step 5: Configure the UPS Daemon
Edit the daemon configuration:
nano /etc/nut/upsd.conf
Add these lines to listen on all interfaces:
# Listen on all interfaces (needed for Tailscale clients)
LISTEN 0.0.0.0 3493
LISTEN :: 3493
MAXAGE 15
Step 6: Create User Accounts
This is where authentication happens. Edit the users file:
nano /etc/nut/upsd.users
Add two users – one for monitoring (used by Mac) and one for admin:
[upsmon]
password = your_secure_monitoring_password
upsmon master
[admin]
password = your_secure_admin_password
actions = SET
instcmds = ALL
Security Note: Replace these with strong, unique passwords. Store them in your password manager or secrets management system. The upsmon password will be needed when configuring the Mac.
Step 7: Configure Local Monitoring
Edit the monitoring configuration:
nano /etc/nut/upsmon.conf
Add this configuration:
# Monitor the local UPS
MONITOR cyberpower@localhost 1 upsmon your_secure_monitoring_password master
# Number of power supplies (1 for single UPS)
MINSUPPLIES 1
# Shutdown command
SHUTDOWNCMD "/sbin/shutdown -h +0"
# How long to wait for slaves before shutdown (seconds)
HOSTSYNC 15
# Time to wait before declaring battery low (seconds)
FINALDELAY 5
# Notifications
NOTIFYCMD /usr/sbin/upssched
NOTIFYFLAG ONBATT SYSLOG+WALL+EXEC
NOTIFYFLAG LOWBATT SYSLOG+WALL+EXEC
NOTIFYFLAG ONLINE SYSLOG+WALL+EXEC
NOTIFYFLAG REPLBATT SYSLOG+WALL+EXEC
NOTIFYFLAG FSD SYSLOG+WALL+EXEC
Step 8: Fix USB Permissions
Here’s where I initially got stuck – the NUT driver needs permission to access the USB device. Create a udev rule:
nano /etc/udev/rules.d/52-nut-usbups.rules
Add this line (using your vendor and product IDs):
SUBSYSTEM=="usb", ATTR{idVendor}=="0764", ATTR{idProduct}=="0601", MODE="0660", GROUP="nut"
Reload udev rules:
udevadm control --reload-rules
udevadm trigger
Step 9: Set File Permissions
chown root:nut /etc/nut/*
chmod 640 /etc/nut/upsd.users /etc/nut/upsmon.conf
Step 10: Start NUT Services
# Start the driver
upsdrvctl start
# Start upsd (UPS daemon)
systemctl start nut-server
systemctl enable nut-server
# Start upsmon (monitoring daemon)
systemctl start nut-monitor
systemctl enable nut-monitor
# Verify everything is running
systemctl status nut-server nut-monitor
Step 11: Test UPS Communication
# List available UPS devices
upsc -l
# Should output: cyberpower
# Get detailed UPS status
upsc cyberpower
# Check current status
upsc cyberpower ups.status
# Should output: OL (Online) when on AC power
Step 12: Configure Firewall
Allow NUT traffic from your Tailscale network:
# Add firewall rule for Tailscale subnet
iptables -A INPUT -s 100.0.0.0/8 -p tcp --dport 3493 -j ACCEPT
# Make it persistent
apt install iptables-persistent
iptables-save > /etc/iptables/rules.v4
Step 13: Get Your Tailscale IP
You’ll need this for the Mac configuration:
ip addr show tailscale0 | grep "inet "
Note the IP address (something like 100.x.x.x) – this is what the Mac will connect to.
Pro Tip: Test network access by querying the UPS via Tailscale IP:
upsc cyberpower@YOUR_PROXMOX_TAILSCALE_IP ups.status
This should return OL if everything is configured correctly.
Part 2: Configure Mac (NUT Slave) ๐
Now that the Proxmox server is broadcasting UPS status, let’s configure the Mac to monitor it and shut down gracefully when needed.
Step 1: Install NUT via Homebrew
brew install nut
Step 2: Verify Installation Paths
NUT on macOS installs binaries in different locations:
# Find upsmon (in sbin)
ls -la /opt/homebrew/sbin/upsmon
# Find upsc (in bin)
ls -la /opt/homebrew/bin/upsc
Step 3: Test Connection to Proxmox
Before configuring anything, verify the Mac can reach the Proxmox UPS server:
/opt/homebrew/bin/upsc cyberpower@YOUR_PROXMOX_TAILSCALE_IP ups.status
This should return OL if the connection works. If you get an error:
- Verify Tailscale is connected on both systems
- Check the firewall rule on Proxmox
- Confirm the Tailscale IP is correct
Step 4: Create Configuration Directory
sudo mkdir -p /opt/homebrew/etc/nut
Step 5: Configure upsmon
Create the monitoring configuration:
sudo nano /opt/homebrew/etc/nut/upsmon.conf
Add this configuration (replace YOUR_PROXMOX_TAILSCALE_IP with your actual Proxmox Tailscale IP):
# Monitor UPS on Proxmox server (slave mode)
MONITOR cyberpower@YOUR_PROXMOX_TAILSCALE_IP 1 upsmon your_secure_monitoring_password slave
# Minimum number of power supplies
MINSUPPLIES 1
# Shutdown command for macOS
SHUTDOWNCMD "/sbin/shutdown -h now"
# Polling frequency (seconds)
POLLFREQ 5
POLLFREQALERT 5
# Host sync time (seconds) - wait for master before shutting down
HOSTSYNC 15
# Dead time (assume UPS is gone after this many seconds)
DEADTIME 15
# Power down flag location
POWERDOWNFLAG /etc/killpower
# Run as root
RUN_AS_USER root
# Notifications
NOTIFYCMD /opt/homebrew/sbin/upssched
NOTIFYFLAG ONBATT SYSLOG+WALL+EXEC
NOTIFYFLAG LOWBATT SYSLOG+WALL+EXEC
NOTIFYFLAG ONLINE SYSLOG+WALL+EXEC
NOTIFYFLAG FSD SYSLOG+WALL+EXEC
NOTIFYFLAG COMMOK SYSLOG+WALL+EXEC
NOTIFYFLAG COMMBAD SYSLOG+WALL+EXEC
NOTIFYFLAG SHUTDOWN SYSLOG+WALL+EXEC
NOTIFYFLAG REPLBATT SYSLOG+WALL+EXEC
NOTIFYFLAG NOCOMM SYSLOG+WALL+EXEC
Critical: Use the exact same password you set in Proxmox’s /etc/nut/upsd.users file for the upsmon user.
Step 6: Set Permissions
sudo chown root:wheel /opt/homebrew/etc/nut/upsmon.conf
sudo chmod 640 /opt/homebrew/etc/nut/upsmon.conf
Step 7: Test Manual Connection
Before setting up the service, test that upsmon can connect:
sudo /opt/homebrew/sbin/upsmon -D
You should see output like:
Network UPS Tools upsmon 2.8.4
UPS: cyberpower@YOUR_PROXMOX_IP (slave) (power value 1)
Using power down flag file /etc/killpower
[D1] Logged into UPS cyberpower@YOUR_PROXMOX_IP
The key line is “Logged into UPS” – this confirms authentication succeeded!
Press Ctrl+C to stop the test.
Troubleshooting: If you see authentication errors, double-check:
- The password in
upsmon.confmatches Proxmox - The Proxmox firewall allows connections
- The Tailscale IP is correct
Step 8: Start as System Service
Here’s where Mac setup differs from Linux. Use Homebrew services to run as root:
# Start as system service (runs as root)
sudo brew services start nut
Verify it’s running:
sudo brew services list | grep nut
Should show:
nut started root /Library/LaunchDaemons/homebrew.mxcl.nut.plist
Step 9: Verify Service is Running
# Check processes
ps aux | grep upsmon
You should see upsmon running as root (not your user account). Two processes are normal – upsmon uses a parent-child architecture.
Step 10: Verify Connection on Proxmox
Back on your Proxmox server, check that the Mac connected:
journalctl -u nut-server --since "5 minutes ago" | grep "YOUR_MAC_MINI_TAILSCALE_IP"
You should see:
User upsmon@YOUR_MAC_IP logged into UPS [cyberpower]
This confirms the Mac successfully authenticated and is monitoring the UPS!
Testing Your Setup ๐งช
Now for the fun part – let’s verify everything works without actually pulling the plug (yet).
Quick Status Checks
On Mac:
# Check UPS status
/opt/homebrew/bin/upsc cyberpower@YOUR_PROXMOX_IP ups.status
# Check battery charge
/opt/homebrew/bin/upsc cyberpower@YOUR_PROXMOX_IP battery.charge
# Check UPS load
/opt/homebrew/bin/upsc cyberpower@YOUR_PROXMOX_IP ups.load
On Proxmox:
# Get full UPS details
upsc cyberpower
# Check connected clients
journalctl -u nut-server --since "10 minutes ago" | grep "logged into"
Understanding the Shutdown Sequence
Here’s what happens when the power goes out:
- Power Failure Detected: UPS switches to battery, status changes from
OL(Online) toOB(On Battery) - Battery Monitoring: Both systems continue monitoring battery level
- Low Battery Threshold: When battery reaches critical level (typically 20%), Proxmox detects
LOWBATTflag - Forced Shutdown Signal: Proxmox sends
FSD(Forced Shutdown) signal to all connected slaves - Mac Shuts Down: Mac receives FSD and executes
/sbin/shutdown -h now - Proxmox Waits: Proxmox waits
HOSTSYNC(15 seconds) for slaves to finish shutting down - Proxmox Shuts Down: After delay plus
FINALDELAY, Proxmox executes its own shutdown
Key Parameters Explained
- MINSUPPLIES 1: Minimum power supplies required (you have 1 UPS protecting your systems)
- HOSTSYNC 15: Wait 15 seconds for slave systems to shut down before master proceeds
- master vs slave: Proxmox is master (directly connected to UPS), Mac is slave (network client)
- POLLFREQ 5: Check UPS every 5 seconds when on AC power
- POLLFREQALERT 5: Check UPS every 5 seconds when on battery (more frequent monitoring)
Safe Testing (Optional)
Warning: This will actually trigger shutdown procedures. Only do this when you’re ready and all work is saved!
On Proxmox, you can simulate a forced shutdown:
upsmon -c fsd
Both systems should begin their shutdown sequences. The Mac will shut down first, followed by Proxmox after the HOSTSYNC delay.
Troubleshooting Common Issues ๐ง
Issue: “Driver not connected” on Proxmox
Symptoms: upsdrvctl start fails with permission errors
Solution: This is the USB permissions issue. Verify the udev rule exists:
cat /etc/udev/rules.d/52-nut-usbups.rules
If missing, recreate it and reload:
echo 'SUBSYSTEM=="usb", ATTR{idVendor}=="0764", ATTR{idProduct}=="0601", MODE="0660", GROUP="nut"' > /etc/udev/rules.d/52-nut-usbups.rules
udevadm control --reload-rules
Then physically unplug and replug the USB cable.
Issue: Mac Can’t Connect to Proxmox
Symptoms: upsc commands timeout or fail
Solution: Check these in order:
- Verify Tailscale is connected on both systems:
tailscale status - Test network connectivity:
nc -zv YOUR_PROXMOX_IP 3493 - Check Proxmox firewall:
iptables -L INPUT -n -v | grep 3493 - Verify upsd is listening:
ss -tlnp | grep 3493
Issue: Authentication Failed
Symptoms: “Access denied” or authentication errors
Solution:
- Verify passwords match between Mac’s
upsmon.confand Proxmox’supsd.users - Check file permissions:
ls -la /etc/nut/upsd.users - Restart nut-server after password changes:
systemctl restart nut-server
Issue: Mac Service Shows “error” Status
Symptoms: brew services list shows error for nut
Solution: The service needs to run as root:
brew services stop nut
sudo brew services start nut
Verify it’s running as root: ps aux | grep upsmon
Issue: Device Number Changes After Reboot
Symptoms: USB device appears at different /dev/bus/usb/ path after reboot
Solution: The udev rule automatically handles this by matching vendor/product IDs rather than device numbers. No action needed – it’s working as designed!
Pro Tips for Production Use ๐ก
1. Monitor Your Monitoring
Set up alerts to notify you if:
- The UPS connection is lost
- The UPS switches to battery
- Battery charge drops below threshold
You can integrate NUT with your existing monitoring stack (Uptime Kuma, Prometheus, etc.) or use the built-in notification system.
2. Test Periodically
Every few months, do a controlled test:
- Save all work
- Unplug the UPS from wall power
- Watch the monitoring logs
- Verify both systems receive notifications
- Let it run for a few minutes, then plug back in
This validates your setup still works and gives you confidence in your protection.
3. Document Your Passwords
Store your NUT passwords in your password manager with notes about:
- Which systems use which passwords
- Configuration file locations
- Tailscale IP addresses
Future you will thank present you when you need to add another system.
4. Consider Battery Replacement Alerts
Configure NUT to notify you when the UPS needs a battery replacement:
NOTIFYFLAG REPLBATT SYSLOG+WALL+EXEC
UPS batteries typically last 3-5 years, and this alert can save you from a nasty surprise during the next outage.
5. Adjust Thresholds for Your Runtime
The default low battery threshold might not suit your UPS capacity. Check your UPS runtime:
upsc cyberpower battery.runtime
This shows remaining runtime in seconds. Adjust your shutdown timing to give yourself margin – you don’t want to cut it too close!
Integration with Your Homelab ๐
Add to Your Dashboard
If you’re running Dashy, Heimdall, or Homer, add UPS monitoring to your dashboard. You can query NUT data via HTTP or create simple status indicators.
Backup Before Shutdown
Consider adding a pre-shutdown script that triggers one final backup before the system goes down. Edit the SHUTDOWNCMD in upsmon.conf to call a wrapper script that:
- Triggers emergency backups
- Sends notifications
- Then executes the actual shutdown
Container Considerations
If you’re running Docker containers or VMs, ensure they have graceful shutdown procedures. NUT’s shutdown is system-level, so containers should handle SIGTERM signals properly.
Final Thoughts ๐
Setting up NUT might feel like overkill until the day your power goes out and everything shuts down gracefully while you’re away on vacation. That peace of mind is worth the hour of configuration time!
The beauty of this setup is that it’s expandable – need to add another system? Just configure it as another slave client. Running multiple UPS units? NUT can handle that too. The Tailscale integration means your systems can communicate reliably even if you’re traveling or your network topology changes.
Remember:
- Test your setup periodically
- Monitor the monitoring (meta, I know)
- Keep those UPS batteries fresh
- Document your configuration
Your homelab is now protected! No more holding your breath during storms or worrying about power blips corrupting your carefully curated services. Set it, test it, and forget it – until the next time the power flickers and you smile knowing everything’s handled. โก๏ธ
Quick Reference Card:
Proxmox Commands:
- Check UPS status:
upsc cyberpower - View connected clients:
journalctl -u nut-server | grep "logged into" - Restart services:
systemctl restart nut-server nut-monitor
Mac Commands:
- Check UPS status:
/opt/homebrew/bin/upsc cyberpower@PROXMOX_IP - View service status:
sudo brew services list | grep nut - Check logs:
log show --predicate 'process == "upsmon"' --last 10m --info
Configuration Files:
- Proxmox:
/etc/nut/directory - Mac:
/opt/homebrew/etc/nut/directory
Default Port: TCP 3493