A robust, universal, and configurable Bash script that safely shuts down a Linux-based system by monitoring a remote NUT (Network UPS Tools) server. It's designed to be a reliable replacement for inflexible or buggy built-in UPS clients. It now includes an optional "Hub Mode" for centrally managing the configuration of multiple clients from a single API endpoint.
Many appliance-like operating systems, such as Synology DSM, Proxmox VE, or TrueNAS, offer built-in UPS support. However, this support can be surprisingly brittle, especially in custom setups (like using a non-Synology device as a NUT server for a Synology client).
This project was born out of frustration with a common failure mode: upon receiving a "Low Battery" signal, the system would start a shutdown procedure but hang indefinitely, becoming unresponsive. This is often caused by a flawed "Standby Mode" or "Safe Mode" implementation that fails to complete. A system in this hung state requires a hard power cycle to recover, which is a critical issue for remote or unattended servers.
This script bypasses the native, problematic shutdown logic entirely. Instead of relying on the host OS's UPS integration, it uses a simple and direct approach:
- It runs periodically via cron.
- It directly queries the NUT server using the REST API
/upscendpoint. - If a critical
On Battery, Low Batterystate is detected, it initiates a configurable countdown. - If power is restored during the countdown, the shutdown is cleanly cancelled.
- If the countdown completes, it executes the universally reliable
/sbin/shutdown -h nowcommand.
This method provides predictable, robust, and platform-agnostic protection against data loss during a power failure.
- Reliable Shutdown: Uses the low-level
shutdowncommand, avoiding complex and fragile "standby" modes. - Centralized Configuration (Hub Mode): Optionally, the script can fetch its configuration (
SHUTDOWN_DELAY_MINUTES) from a central REST API endpoint. This is perfect for managing multiple client machines from a single location. - Configurable Delay: Set a grace period (in minutes) before shutdown, giving short power outages a chance to resolve.
- Smart Cancellation: Automatically cancels the pending shutdown if mains power is restored.
- Resilient Fallback: If the central API hub is unreachable, the script automatically falls back to using its last known good configuration from the local
ups.envfile, ensuring uninterrupted protection. - Universal Compatibility: Works on virtually any Linux-based system with Bash, curl and jq (Synology DSM, Proxmox, Debian, Ubuntu, etc.).
- Lightweight & Stateless: Has minimal dependencies and uses a simple flag file for state management, requiring no complex daemons.
- Easy to Configure: All settings are managed in a simple, external
ups.envfile for standalone mode, with an override for hub mode. - Automated Updates: Includes an optional update script to pull the latest version from the official repository.
- Active Status Reporting: In Hub Mode, the script reports its status (
online,shutdown_pending) back to theUPS_Server_DockerAPI on every run. This provides a live, accurate view of the client's health in the central dashboard.
This project is the perfect companion to the UPS_Server_Docker application. The new Hub Mode feature is specifically designed to work with the REST API provided by UPS_Server_Docker.
The script's logic runs every minute and follows these steps:
- Load Configuration: The script requires
API_SERVER_URIandAPI_TOKENto be defined inups.env. It attempts to fetch its configuration from the central API. If successful, it uses the fetched settings and caches them back toups.env. If the API is unreachable, the script falls back to the local configuration in theups.envfile. - Check UPS Status: The script queries the
/upscAPI endpoint to get the UPS status from the configured UPS server. - Detect Low Battery: If the status is
OB LB(On Battery, Low Battery), the script checks for the existence of a flag file (/tmp/ups_shutdown_pending.flag).- If the flag doesn't exist, it's the first sign of trouble. The script writes the current timestamp into the flag file and logs that the countdown has begun.
- If the flag exists, the script calculates the time elapsed since the timestamp in the file. If the elapsed time exceeds the configured delay, it executes
shutdown -h now.
- Detect Power Restoration: If the status is
OL(On Line) and the flag file exists, the script knows a shutdown was pending. It logs the cancellation and simply deletes the flag file, effectively resetting the state.
Follow these steps to set up the monitor on a new system.
Ensure the following packages are installed on your system:
bash(usually installed by default)cronor another task schedulercurl(for hub mode and the update script)jq(required for hub mode to parse API responses)- a configured UPS Server with REST API (see: UPS_Server_Docker)
gitfor cloning the project from GitHub
First, place the script files in a persistent location on your server.
For most Linux systems (Proxmox, Debian, etc.), a good choice is /opt/ups-monitor:
# Example using git
git clone https://github.com/MarekWo/UPS_monitor.git /opt/ups-monitor
cd /opt/ups-monitorFor Synology DSM Users:
The Synology operating system does not have an /opt directory. The best practice is to place scripts on a data volume to ensure they survive system updates. The recommended location is a dedicated scripts folder on volume1.
# Create the directory on your data volume
sudo mkdir -p /volume1/scripts/ups-monitor
# Clone the repository into the new directory
git clone https://github.com/MarekWo/UPS_monitor.git /volume1/scripts/ups-monitor
cd /volume1/scripts/ups-monitorCreate your local configuration file by copying the provided example. This file will be ignored by git, so your settings are safe.
cp ups.env.example ups.envNow, edit the newly created ups.env with your specific settings:
# ups.env
# --- Required API Configuration ---
# This feature is designed to work with the UPS_Server_Docker project.
# The address of your central UPS Hub API server.
API_SERVER_URI="http://<UPS_SERVER_IP>:5000"
# The secret token to authenticate with the API Hub.
# The API_TOKEN value MUST exactly match the API_TOKEN set in the server's power_manager.conf file.
API_TOKEN="your_secret_api_token_here"
# --- Fallback Configuration ---
# Used if the API Hub is unreachable or does not provide configuration.
# The delay in minutes before shutdown after a low battery is detected.
SHUTDOWN_DELAY_MINUTES=5Make the main script and the update script executable.
chmod +x ups_monitor.sh
chmod +x update.shThe script needs to run every minute to be effective. You must schedule it to run as the root user, as the shutdown command requires root privileges.
For most Linux systems: Open the root user's crontab for editing:
sudo crontab -eAdd the following line, making sure the path is correct for your installation, then save and exit:
# Run the UPS monitor script every minute
* * * * * /opt/ups-monitor/ups_monitor.shFor Synology DSM Users:
- Go to
Control Panel>Task Scheduler. - Click
Create>Scheduled Task>User-defined script. - In the General tab:
- Task:
UPS Monitor - User:
root
- Task:
- In the Schedule tab:
- Run on the following days:
Daily - Frequency:
Every 1 minute
- Run on the following days:
- In the Task Settings tab, enter the full path to the script in the
Run commandbox:bash /volume1/scripts/ups-monitor/ups_monitor.sh - Click
OKto save the task.
To update the script to the latest version from your repository, simply run the update.sh script.
# Navigate to your script directory and run the updater
# (e.g., /opt/ups-monitor or /volume1/scripts/ups-monitor)
cd /path/to/your/script/directory
./update.shYou can also automate this by adding a second cron job (or another Task Scheduler entry on Synology) to run the updater daily, for example, at 3:00 AM.
After setup, you can test the script's functionality:
- Run it manually: Execute
/path/to/your/script/ups_monitor.shand check for any errors. - Check the logs:
- For systemd-based systems (Debian, Proxmox):
journalctl -f | grep UPS_Shutdown_Script - For Synology DSM: Open the
Log Centerapplication or check the log file directly withtail -f /var/log/messages | grep UPS_Shutdown_Script
- For systemd-based systems (Debian, Proxmox):
- Simulate a power failure: Change the state of your (dummy) NUT server to
OB LBand watch the logs. The script should log that the countdown has started. - Simulate power restoration: Before the delay is over, change the NUT server state back to
OL. The script should log that the shutdown has been cancelled.
This is a community-driven project. If you have an idea for an improvement or find a bug, please feel free to fork the repository, make your changes, and submit a pull request. You can also open an issue to start a discussion.
IMPORTANT: Starting with version 4.3.0, this project has undergone significant changes to improve reliability and simplify deployment.
- API-Only Operation: The Linux script now exclusively uses REST API calls instead of the
upsccommand - Mandatory API Configuration:
API_SERVER_URIandAPI_TOKENare now required inups.env - No More NUT Client Dependency: The
nut-clientpackage is no longer required on client machines
-
Run the update.sh script:
# Navigate to your script directory and run the updater cd /path/to/your/script/directory # for example /opt/ups-monitor ./update.sh
-
Update Your Configuration File:
# Add these required fields to your ups.env file: API_SERVER_URI="http://your-ups-server:5000" API_TOKEN="your_api_token_here"
-
Remove NUT Client (Optional):
# On Debian/Ubuntu systems: sudo apt remove nut-client # On other systems, consult your package manager documentation
-
Test the New Version: Run the script manually to ensure it connects to your UPS server API:
./ups_monitor.sh
-
Verify Logs: Check your system logs to confirm the script is working correctly with API calls.
- β Simplified Installation: No need to install and configure NUT client tools
- β Better Error Handling: More detailed error messages for API connectivity issues
- β Unified Approach: Linux and Windows versions now use identical logic
- β
Reduced Dependencies: Only requires
curlandjq(already needed for Hub Mode)
This project is licensed under the MIT License. See the LICENSE file for details.