A UPS HAT Raspberry Pi setup keeps the system running long enough after mains power fails to execute a clean shutdown, preventing filesystem corruption and data loss. The HAT stacks directly on the Pi’s 40-pin GPIO header, provides battery-backed power, and signals the Pi via GPIO or I2C when external power is lost. A systemd service or Python script receives that signal and issues a controlled shutdown before the battery runs out. This article covers how UPS HATs work, what to look for when choosing one, how to configure the shutdown software, and how to verify the setup actually works.
Last tested: Raspberry Pi OS Bookworm Lite 64-bit | February 20, 2026 | Raspberry Pi 4 Model B (4GB) | Geekworm X728 UPS HAT | dual 18650 cells
Key Takeaways
- The purpose of a UPS HAT is a clean shutdown, not extended runtime. Even a 10-second battery window is enough to protect the filesystem if the shutdown script is configured correctly.
- The shutdown script must be set up and tested before a power failure happens. A UPS HAT without working shutdown software is just a battery that delays the crash.
- Test the setup by physically removing power every few months. Battery chemistry degrades silently, and a battery that held charge last year may not give you the same window today.
How a UPS HAT Raspberry Pi Setup Works
The UPS HAT sits between the power supply and the Pi. Under normal operation the HAT passes mains power through to the Pi and simultaneously charges its battery. When mains power drops, the HAT’s power-path management circuit switches to battery output within microseconds, fast enough that the Pi does not experience an interruption.
The HAT then signals the Pi that it is running on battery. Most boards do this one of two ways: a GPIO pin changes state (typically going low when power is lost), or an I2C fuel gauge IC updates a register that a polling script reads. Either way, a script running on the Pi detects the signal and initiates a controlled shutdown before the battery is exhausted.
The sequence for a typical power-loss event:
- Mains power drops
- UPS HAT switches to battery within microseconds
- HAT signals power loss via GPIO pin state change or I2C register update
- Monitoring script detects signal and calls
shutdown -h now - Pi unmounts filesystems, stops services, and halts cleanly
- HAT cuts battery output after the Pi halts (on boards that support this)

The window between step 2 and step 5 is typically 10–30 minutes with an 18650-based HAT, or under 60 seconds with a supercapacitor-based board. The shutdown command itself takes 5–15 seconds on a healthy Bookworm system. Either way, the battery window is more than adequate as long as the script fires promptly.
Why Sudden Power Loss Damages the Raspberry Pi
The Pi does not handle sudden power loss gracefully. Storage on microSD cards is particularly vulnerable: the card’s write cache may be mid-flush when power cuts, leaving partially written sectors. The result is filesystem corruption that ext4 journaling does not always catch, especially when the corruption affects the journal itself.
Repeated uncontrolled shutdowns also cause physical wear on SD cards beyond normal write-cycle limits, accelerating failure. USB SSDs are more resilient but still affected by interrupted writes. Services that hold database files open (SQLite, PostgreSQL, InfluxDB) are particularly prone to corruption because their write patterns are not atomic at the filesystem level.
For headless or remote deployments, an unplanned shutdown may require physical access to re-flash the SD card. A UPS HAT avoids this entirely at a cost well under what a call-out visit or replacement hardware would require. See Preventing SD Card Corruption on Raspberry Pi for additional hardening measures that complement UPS protection.
Choosing a UPS HAT
Battery-based UPS HATs
| Model | Battery | Shutdown signal | Notes |
|---|---|---|---|
| Geekworm X728 | Dual 18650 | GPIO + I2C | Longest runtime, power button, fits Pi 4 and Pi 5 |
| Waveshare UPS HAT | Single 18650 | I2C | Lightweight, straightforward, good documentation |
| PiJuice HAT | Built-in Li-Po | I2C + GPIO | Solar charging support, full Python API, highest cost |
| DFRobot UPS Board | Single 18650 | I2C | Compact form factor, suitable for embedded builds |
- Power Management Features: X728 provides Max 5.1V 5000mA Power Backup, intelligent and safe power management, safe shutd…
- User Manual and Compatibility: Google Geekworm Wiki search X728 for manual; Matching metal case for X728 and Pi 4 is X72…
- Power Supply Specifications: Power supply via Type-C socket : 5Vdc 5%, 3A OR Via DC power jack : 5Vdc 5%, 4A; UPS output…
- Function: X728 provides 5.1V 6000mA Power Backup, intelligent and safe power management, safe shutdown, auto power-on, f…
- User Manual: Google Geekworm Wiki search X728 for manual
- How to Power and Specifications: Power supply via Type-C socket : 5Vdc ±5%, ≥3A OR Via DC power jack : 5Vdc ±5%, ≥4A; UP…
- Standard Raspberry Pi 40PIN GPIO extension header, supports Raspberry Pi series boards. I2C bus communication, monitorin…
- Multi protection circuits: over charge/discharge protection, over current protection, short circuit protection, and reve…
- Onboard 5V regulator, up to 2.5A continuous output current. 5V USB output, convenient for powering other boards
- Onboard 1820 mAh “off the shelf” Lipo / LiIon battery for ~4 to 6 hours in constant use! (with support for larger Lipo B…
- A Full Uninterrupted / Uninterruptable Power Supply solution.
- Compatible with the Raspberry Pi 4. Also designed for the Raspberry Pi A+, B+, 2B and 3B and also compatible with Raspbe…
Supercapacitor-based UPS HATs
Supercapacitor boards replace the battery with capacitors that deliver high current for a short burst. The runtime is under 60 seconds, which is only enough for a shutdown command rather than extended operation. The advantages are longevity (10,000+ charge cycles vs 300–500 for Li-ion), zero battery maintenance, and tolerance of a wider temperature range.
| Feature | Supercapacitor UPS | 18650 Battery UPS |
|---|---|---|
| Runtime after power loss | 10–60 seconds | 10–30 minutes |
| Charge time to full | Seconds | 1–3 hours |
| Cycle lifespan | 10,000+ | 300–500 |
| Maintenance required | None | Battery replacement every 1–3 years |
| Use case | Clean shutdown only | Clean shutdown + brief continued operation |
For any setup where the only requirement is a clean shutdown rather than continued operation, a supercapacitor board is the more reliable long-term choice. For setups that need to remain operational through brief outages (30 seconds to a few minutes), a battery-based board is the right choice.
What to verify before buying
- Shutdown signal method: GPIO, I2C, or both. Confirm which pin or register the board uses and that example code exists for your Pi model.
- Pi 5 compatibility: Pi 5 draws more current than Pi 4. Verify the HAT’s output current rating covers your Pi 5 load before purchasing.
- Pass-through headers: A good HAT exposes the full 40-pin header so additional HATs or jumper wires can still be connected.
- Auto power-on: Some boards can restart the Pi automatically when mains power returns. Useful for unattended deployments but requires explicit configuration.
Hardware Installation
Power off the Pi completely before installing the HAT. Align the HAT’s 40-pin header with the Pi’s GPIO header and press straight down, ensuring all pins seat evenly. Attach any standoffs and screws included with the board. These are important when a battery is mounted under the HAT because the weight distribution can stress the GPIO header over time.
Insert the 18650 cells observing polarity markings on the battery holder. Reversed insertion will damage the charge circuit and potentially the cells. If the board uses a JST connector for the battery, connect it before applying main power.
Power the HAT through its own USB-C or microUSB input port, not the Pi’s native power port. Running power into both simultaneously causes a backfeed condition on some boards. Check the board’s manual explicitly before doing so. A small number of boards are designed to accept power from the Pi’s port, but most are not.
Use a 5V/3A supply minimum for Pi 4, and 5V/5A for Pi 5. An underpowered supply that cannot simultaneously charge the battery and run the Pi under load will cause undervoltage during periods of high CPU activity. See Raspberry Pi Power Monitoring via USB for how to verify supply adequacy under load.
Expected result: The HAT’s power indicator LED illuminates. The Pi boots normally. The battery charge indicator shows charging activity within a few minutes of connecting the supply.
Software: Shutdown Monitoring
GPIO-based shutdown script
On boards that signal power loss via a GPIO pin (e.g. GPIO 6 going low), a simple polling script suffices. Install dependencies:
sudo apt install python3-gpiozero -y
Create /usr/local/bin/ups-monitor.py:
#!/usr/bin/env python3
import subprocess
import time
from gpiozero import Button
# Adjust pin to match your UPS HAT documentation
POWER_LOSS_PIN = 6
power_ok = Button(POWER_LOSS_PIN, pull_up=True)
while True:
if not power_ok.is_pressed:
# Power loss detected -- initiate clean shutdown
subprocess.run(['shutdown', '-h', 'now'], check=True)
break
time.sleep(2)
I2C battery voltage script
On boards with a MAX17040 or similar fuel gauge IC, read battery voltage over I2C and shut down when it falls below a safe threshold. The MAX17040 reports voltage in two bytes at register 0x02:
sudo apt install python3-smbus2 -y
#!/usr/bin/env python3
import subprocess
import time
import smbus2
BUS_NUM = 1
I2C_ADDR = 0x36 # MAX17040 default address
VCELL_REG = 0x02
LOW_VOLT = 3.2 # Shutdown threshold in volts
bus = smbus2.SMBus(BUS_NUM)
def read_voltage():
data = bus.read_i2c_block_data(I2C_ADDR, VCELL_REG, 2)
raw = (data[0] << 4) | (data[1] >> 4)
return raw * 0.00125 # Convert to volts per MAX17040 datasheet
while True:
try:
volts = read_voltage()
if volts < LOW_VOLT:
subprocess.run(['shutdown', '-h', 'now'], check=True)
break
except OSError:
pass # I2C read error -- retry next cycle
time.sleep(10)
Run the monitoring script as root via systemd so it has permission to call shutdown without requiring passwordless sudo. Create /etc/systemd/system/ups-monitor.service:
[Unit]
Description=UPS HAT power loss monitor
After=multi-user.target
[Service]
Type=simple
ExecStart=/usr/bin/python3 /usr/local/bin/ups-monitor.py
Restart=on-failure
User=root
[Install]
WantedBy=multi-user.target
sudo chmod +x /usr/local/bin/ups-monitor.py
sudo systemctl daemon-reload
sudo systemctl enable --now ups-monitor.service
sudo systemctl status ups-monitor.service
Expected result: systemctl status ups-monitor.service shows the service active and running. The script is polling at its configured interval. No errors in journalctl -u ups-monitor.service.
NUT and apcupsd: when they apply
Network UPS Tools (NUT) and apcupsd are mature daemon suites designed for USB-serial or USB-HID UPS devices. They are not directly compatible with most GPIO/I2C UPS HATs because those HATs do not present as USB serial devices. If your UPS HAT exposes a USB interface and includes a NUT or apcupsd driver, use those tools for their robust monitoring and multi-system notification features. For GPIO/I2C-only boards, the Python scripts above are the correct approach.
Testing the Shutdown
The only reliable test is removing mains power while the Pi is running under its normal workload. Do this with the Pi running a representative service, not just sitting at idle, because services under active writes are what you are protecting.
Pull the power supply cable from the UPS HAT’s input. The Pi should remain powered from the battery. Within the polling interval of your script (2–10 seconds), the shutdown command should fire. The Pi should halt cleanly within 15–30 seconds after that.
# Watch system logs during the test (run before pulling power)
sudo journalctl -f
# After power is restored and the Pi reboots, check the last shutdown
journalctl --list-boots
last reboot
A clean shutdown appears in the logs as an orderly sequence of service stops. An unclean shutdown appears as a sudden gap in the log with an fsck entry on the next boot. If you see fsck entries, the script is not firing in time and the voltage threshold or polling interval needs adjustment.
# Check for filesystem check events from last boot
journalctl -b | grep -i 'fsck\|clean\|error'
# Check UPS monitor logs
journalctl -u ups-monitor.service -n 50
Expected result: No fsck entries after the simulated power failure. journalctl --list-boots shows a normal shutdown entry (reboot count not incremented by emergency recovery) immediately before the current boot.
Troubleshooting
Pi shuts down immediately on battery
The monitoring script is triggering the shutdown signal immediately rather than waiting for the battery to be low. Check the GPIO pin number against the board’s documentation. A wrong pin may read as low continuously. For I2C scripts, confirm the I2C address and register match the specific fuel gauge IC on your board. Run sudo i2cdetect -y 1 to confirm the I2C address and sudo i2cdump -y 1 0x36 to inspect the registers.
Shutdown not triggering during power loss
# Confirm the service is running
sudo systemctl status ups-monitor.service
# Confirm the script process exists
ps aux | grep ups-monitor
# Test the GPIO pin state directly
python3 -c "from gpiozero import Button; b=Button(6, pull_up=True); print(b.is_pressed)"
Battery not charging
Check that the power supply meets the required current rating. A supply that can run the Pi but cannot simultaneously charge the battery will show a steady charge LED with no voltage increase over time. Measure battery voltage directly with a multimeter before and after an hour of charging. If the voltage does not increase at all, either the supply is underpowered or the charge circuit on the board has a fault. Check the board with a fresh 18650 cell before concluding the charge circuit is faulty.
Pi fails to boot after power returns
If the Pi does not boot automatically when mains power returns, the board either requires a manual power button press or does not include auto power-on logic. Check the board documentation for an auto power-on feature and how to enable it. On the Geekworm X728, auto power-on is controlled by a jumper on the board. On boards without this feature, a GPIO-triggered relay or a separate auto-restart circuit is needed for fully unattended operation.
Maintenance
18650 cells degrade over 300–500 charge cycles. A cell that held 3000mAh when new may hold 2000mAh after two years of daily cycling. The practical effect is a shorter runtime window between power loss and shutdown. Test by timing how long the Pi stays on after mains power is removed. If the window has shortened significantly from when you first set up the board, replace the cells.
Use cells from known manufacturers (Samsung 25R, LG HG2, Panasonic NCR18650B). Avoid unbranded or rewrapped cells. Do not discharge below 3.0V and do not leave the battery fully discharged for extended periods. Store partially charged (around 50%) if the Pi will be unused for more than a few weeks.
Repeat the full power-pull test every 3 months. Keep a log of the shutdown timestamp relative to when power was removed. This gives you a reliable trend line for battery degradation over time. A number of community-maintained scripts are available on GitHub for automating this log entry, including wrappers around journalctl output and tools that integrate with Grafana. For config file backups including the UPS monitor script, see BorgBackup Raspberry Pi Prune Policies.
FAQ
Can I use any 18650 battery with a UPS HAT?
The board will physically accept most 18650 cells but not all cells perform the same. Use cells rated at 2500mAh or higher from established manufacturers. Avoid reused cells pulled from old laptop battery packs. Their remaining capacity is unpredictable and some have elevated internal resistance that causes voltage sag under load. Unbranded cells sold cheaply often do not meet their advertised capacity ratings.
Do I need special scripts for my UPS HAT?
It depends on the board. PiJuice ships with a Python API and a daemon that handle shutdown automatically once configured. Geekworm and Waveshare boards require scripts. The manufacturers provide examples but the quality varies. The GPIO and I2C scripts in this article cover the common cases. Always verify the GPIO pin number and I2C address against your specific board’s documentation rather than assuming the defaults match.
Will a UPS HAT work with Raspberry Pi 5?
Most current boards support Pi 5, but check two things: the HAT’s output current rating (Pi 5 can draw up to 5A under peak load), and GPIO compatibility. The Pi 5 GPIO header is electrically compatible with Pi 4 but the physical board layout differs slightly. Confirm with the manufacturer that your specific board model supports Pi 5 before purchasing.
How long will the Pi stay on after power loss?
With a single fully charged 18650 cell and a Pi 4 at typical load (around 1.5A), expect 15–25 minutes. With dual cells as in the Geekworm X728, double that. Attached USB drives, cameras, and other peripherals reduce this window proportionally. For a supercapacitor board, expect 10 to 45 seconds depending on the capacitance and the load. The window only needs to cover the shutdown sequence, which is typically under 20 seconds.
Can the UPS automatically restart the Pi after power returns?
Some boards support this via a GPIO wake signal or a configurable jumper. The Geekworm X728 has an auto power-on option. PiJuice supports wake-on-power via its API. Waveshare’s basic board does not include this feature. For fully unattended deployments where the Pi must recover from a power outage without manual intervention, confirm auto power-on support before buying.
References
- https://www.waveshare.com/wiki/UPS_HAT
- https://geekworm.com/collections/ups
- https://github.com/networkupstools/nut
- https://www.ti.com/product/MAX17040
About the Author
Chuck Wilson has been programming and building with computers since the Tandy 1000 era. His professional background includes CAD drafting, manufacturing line programming, and custom computer design. He runs PidiyLab in retirement, documenting Raspberry Pi and homelab projects that he actually deploys and maintains on real hardware. Every article on this site reflects hands-on testing on specific hardware and OS versions, not theoretical walkthroughs.
Last tested hardware: Raspberry Pi 4 Model B (4GB), Geekworm X728 UPS HAT, Samsung 25R 18650 cells. Last tested OS: Raspberry Pi OS Bookworm Lite 64-bit.

