Nutify puts a web dashboard in front of Network UPS Tools for your UPS fleet
Modern web-based UPS monitoring system with real-time data visualization, alerts, and comprehensive reporting. Docker-ready with multi-architecture support.
At a glance
- What is it?
- Nutify is a Python web application that monitors Uninterruptible Power Supplies through Network UPS Tools, adding dashboards, alerts, scheduled reports and charts on top of a daemon most people run without looking at. Its Docker profile is written to avoid privileged mode and to expose only USB device nodes, and the current 0.3.0 release is explicitly a breaking one that wants a clean start.
- Who is it for?
- Nutify fits a home lab, small office or homelab with one UPS or a mixed fleet of local and remote units, and someone who wants graphs and alerts instead of a NUT daemon in a terminal. Skip it if your hardware is only reachable through a serial port you cannot map into a container, or if you are unwilling to recreate the database on the 0.3.0 upgrade, since the release is explicitly not backward compatible.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 60 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A web console bolted onto a daemon that already speaks UPS hardware
Nutify is built on top of Network UPS Tools, and that choice defines most of its behaviour. NUT is the long established open source project that talks to UPS hardware over USB and serial and exposes the state as a network service. Nutify is not a replacement for it. The README describes Nutify as an open source UPS monitoring and management platform built on top of NUT, and the compose file confirms the arrangement with a comment calling the service the main Nutify application and NUT monitoring service in one container.
So the division of labour is: NUT does the hardware conversation, Nutify does the interface. What you get in exchange for the extra layer is what the README lists, namely real time status, historical telemetry, alerts, scheduled reports and interactive charts through a web interface, and the claim is that it works from a single UPS setup through to mixed local and remote multi-UPS fleets, configured and operated from one place.
The dashboard screenshots in the README show the shape of the thing, with separate views for energy, battery, power and voltage, plus an energy details view. That is more than a status page, because the difference between knowing that a battery is at 40 percent and seeing the discharge curve over a week is the difference between a light that blinks and a report you can act on.
The project is Python, the licence is MIT, and the current version is 0.3.0, which the README labels as being in public testing. The repository is not archived, and the last push was on 2026-08-24.
The compose profile is written to avoid privileged containers
Most UPS monitoring containers need raw access to USB devices, and the usual way to get it is to run privileged or to mount the whole device tree. The Nutify profile explicitly does neither, and the README explains the constraint in a paragraph before the configuration.
It says the supplied compose profile avoids privileged mode, the complete host `/dev` tree, `SYS_ADMIN`, `SYS_RAWIO` and `MKNOD`, and that it exposes only Linux USB device nodes, major 189, plus read-only udev metadata so the wizard can discover and open directly attached USB HID UPS devices.
The configuration in the README shows how that is expressed.
services:
nut:
image: dartsteven/nutify:latest-amd64
container_name: Nutify
cap_drop:
- ALL
cap_add:
- CHOWN
- DAC_OVERRIDE
- FOWNER
- KILL
- SETGID
- SETUIDEvery capability is dropped and only the six needed ones are added back, with the comments in the repository's own compose file giving the reason for each, from setting ownership on bind mounted files to dropping services to the configured runtime user.
Two more hardening choices are visible. The `no-new-privileges` security option prevents processes from gaining further privileges after startup, and the device cgroup rule is limited to the USB bus major number rather than a device path, so the container can see USB devices without being handed the host's entire device directory.
The device access is still there, though. The compose file mounts `/dev/bus/usb` read write and `/run/udev` read only, with the second one described as read-only USB identity metadata. That is the trade the design makes, and it is a much smaller one than privileged mode.
Persistent volumes, a generated secret and three published ports
The compose file is explicit about what survives an image update.
environment:
SECRET_KEY: ${SECRET_KEY:?Set SECRET_KEY in .env}
NUTIFY_WEB_USER: nut
NUT_SERVICE_USER: nut
UDEV: "1"
SKIP_PERMCHECK: "true"
NUTIFY_USB_GID: ${NUTIFY_USB_GID:-}
SSL_ENABLED: "false"The volumes are bind mounts to a `./Nutify` directory on the host, split into `logs`, `instance` for the database and runtime state, `ssl` for an optional certificate and key, and `etc/nut` for the generated NUT configuration. The `NUTIFY_USB_GID` value is the one to set by hand, and the example environment file explains that Nutify detects it automatically and you should set it only when container startup reports no USB device GID.
The `SECRET_KEY` deserves attention because the example file says something easy to miss. It says to keep the same value across restarts, and that changing it invalidates encrypted data. That is a direct consequence of the guided SSO flow, which the README says encrypts the client secret, so a lost key means the stored SSO secret can no longer be read.
The README shows the key being generated once into a private `.env` file.
printf 'SECRET_KEY=%s\n' "$(openssl rand -hex 32)" > .envThree ports are published, 3493, 5050 and 443, and the README says to open the interface on the 5050 port.
- `http://localhost:5050`The SSL_ENABLED flag is off by default in the standard profile, which is the right choice when a reverse proxy terminates TLS, and the port 443 mapping is there for the case where the container serves it instead.
Images for arm64 and three generations of Raspberry Pi
The architecture table in the README is unusually specific, and it is the clearest sign of who this project is for.
The tag names follow a simple pattern, with the architecture in the suffix: `dartsteven/nutify:latest-amd64` for standard PCs, servers and most cloud VMs, `dartsteven/nutify:latest-mac-arm64` for Apple Silicon machines running Docker, `dartsteven/nutify:latest-raspberrypi3-armv7` for a Pi 3 on a 32-bit operating system, `dartsteven/nutify:latest-raspberrypi4-armv7` for a Pi 4 on 32-bit, and `dartsteven/nutify:latest-raspberrypi5-arm64` for a Pi 4 or 5 on a 64-bit operating system.
There is a further entry for the Raspberry Pi 4 with a 32-bit operating system, and one for the Raspberry Pi 4 or 5 with a 64-bit operating system, each with its own tag. The Raspberry Pi rows carry an explicit requirement about the operating system bitness, which is a real distinction rather than a formality, since a 32-bit userspace on a 64-bit board is a common way to run older Pi images.
The consequence for a homelab is that the Pi images are maintained separately, and the tag names have to be chosen carefully. The compose file in the repository even comments on the default, saying to select the tag matching the host CPU and that the default is for AMD64/x86_64.
For serial and non-USB devices, the README says an explicit Docker mapping may be required and points to a guide on optional direct hardware access, and the repository ships a separate `docker-compose.hardware.yaml` profile for that. The example environment file covers the same ground with `NUTIFY_DEVICE`, `NUTIFY_DEVICE_CONTAINER` pointing at `/dev/nutify-ups`, and `NUTIFY_DEVICE_GID`, and it warns that these values select the device path rather than choosing between single and multi UPS monitoring.
SSO with OIDC, configured from the database or from the environment
Authentication is the area where the project is most careful, and it is worth reading the two paths separately because they have different security postures.
The database path is the guided flow. The README says it encrypts the client secret, validates the discovery and signing keys, and requires a real browser login test before activation. Configuration happens in the application under System and Authentication, and the standard profile starts with a local Nutify username and password, with SSO available later without editing the compose file.
The environment path is for automated deployments, and the README points at an OIDC environment configuration guide. The example environment file lists the full set of variables, from `OIDC_CONFIG_SOURCE` and `OIDC_ENABLED` through the issuer, client id, client secret, redirect URI and scopes, to the username claim, the groups claim, the admin group, the user group and `OIDC_ALLOW_ALL_USERS=false`.
OIDC_CONFIG_SOURCE=database
OIDC_ENABLED=false
OIDC_ISSUER=
OIDC_CLIENT_ID=
OIDC_CLIENT_SECRET=
OIDC_ADMIN_GROUP=
OIDC_USER_GROUP=
OIDC_ALLOW_ALL_USERS=falseThe advanced profile is deliberately restrictive. The README says it requires HTTPS plus an explicit administrator group, and that it intentionally keeps `OIDC_ALLOW_ALL_USERS=false`, and the environment file carries a warning on the insecure HTTP flag that it is for local development only and should never be enabled for a production provider.
The practical rule is that both an administrator group and a user group have to be named, and the claim names have to match what your identity provider actually sends. A misconfigured `OIDC_USERNAME_CLAIM` is the failure mode that produces a login that succeeds and an account with no group membership.
Reverse proxies, allowed origins and a Socket.IO fallback
Running behind a reverse proxy is where a lot of real time web applications break, and the README addresses the specific failure modes rather than hand-waving.
The first is scheme and host propagation. When a proxy cannot preserve `X-Forwarded-Proto` and `X-Forwarded-Host`, the instruction is to set `SOCKETIO_ALLOWED_ORIGINS` to the exact public origin. Multiple origins are comma separated, and the README says wildcards are rejected. That is a strict setting, and it is strict on purpose, because an allow list with a wildcard in front of it is not an allow list.
SOCKETIO_ALLOWED_ORIGINS=The second is the transport itself. The README says proxies should still forward WebSocket upgrades for real time data, and that the dashboard falls back to authenticated HTTP snapshots while Socket.IO is unavailable. That fallback is a deliberate design choice. A monitoring dashboard that goes blank the moment a proxy misbehaves is worse than one that updates every few seconds, and the project treats the live channel as an enhancement rather than a requirement.
The third is the wizard. Nutify starts with local authentication, and SSO remains optional, so the first-run experience does not depend on an identity provider being reachable.
Taken together, the proxy behaviour is a reasonable position for a tool that people install on a home network and then inevitably put behind a reverse proxy with a hostname, because the alternative is a dashboard that silently refuses to connect and an error message that says only that the origin is not allowed.
Version 0.3.0 wants a clean start
The version notice in the README is unusually blunt, and it is the first thing an upgrading user needs to know.
- Version: `0.3.0` (Public Testing)The warning below it says the release is not backward compatible with previous releases, that the database must be recreated from scratch, and that it is strongly recommended to start from a completely clean environment using a new empty folder, with the instruction not to reuse files or data from older versions.
That is a large ask, and it is the price of a young project that is still reshaping its data model. The README points to the GitHub Releases page and to `changelog.md` for the full details, and says each GitHub Release includes deployment files, checksums, Docker image metadata and source archives.
The surrounding practice is disciplined. The repository carries a `SECURITY.md`, a `CODE_OF_CONDUCT.md` and a `CONTRIBUTING.md`, a CI workflow badge, a wiki badge and a discussions badge, and the top-level entries include a `Dockerfile/` directory, a `nutify/` directory, a `src/` directory, an `.env.example` and separate compose profiles for the standard, hardware and OIDC cases. A single tagged release exists, v0.3.0 on 2026-08-24, which matches the last push, so the project is at an early but not abandoned stage.
For someone deciding whether to run it, the picture is a young project with careful security defaults and a willingness to break compatibility rather than carry a database shape it no longer wants.
Editorial conclusion
Nutify fits a home lab, small office or homelab with one UPS or a mixed fleet of local and remote units, and someone who wants graphs and alerts instead of a NUT daemon in a terminal. Skip it if your hardware is only reachable through a serial port you cannot map into a container, or if you are unwilling to recreate the database on the 0.3.0 upgrade, since the release is explicitly not backward compatible. Verify first that your UPS is exposed as a USB HID device the container can see, that the host allows the device cgroup rule the profile needs, and that the ports you expose are the ones your reverse proxy expects.
Frequently asked questions
What does Nutify do and what does it need?
Nutify is a web application that monitors and manages UPS hardware through Network UPS Tools, adding real time status, historical telemetry, alerts, scheduled reports and interactive charts. The README says it covers everything from a single UPS to mixed local and remote multi-UPS fleets.
How do I generate the SECRET_KEY that Nutify requires?
The README shows writing a fresh random value into a private `.env` file with `printf 'SECRET_KEY=%s\n' "$(openssl rand -hex 32)" > .env`. The example environment file warns that the value must stay the same across restarts, because changing it invalidates encrypted data such as the stored SSO client secret.
Which Docker image should I use for my hardware?
The README lists a tag per architecture, `latest-amd64` for standard PCs and servers, `latest-mac-arm64` for Apple Silicon, and separate Raspberry Pi tags for the Pi 3 and Pi 4 on 32-bit systems and the Pi 4 or 5 on 64-bit systems. The compose file comments that the default tag is for AMD64/x86_64 and that the tag should match the host CPU.
Does the Nutify container need to run privileged?
No. The README says the supplied compose profile avoids privileged mode, the complete host `/dev` tree, `SYS_ADMIN`, `SYS_RAWIO` and `MKNOD`, and exposes only Linux USB device nodes with major 189 plus read-only udev metadata, with all capabilities dropped and a short list added back.
How does Nutify handle being behind a reverse proxy?
If the proxy cannot preserve `X-Forwarded-Proto` and `X-Forwarded-Host`, set `SOCKETIO_ALLOWED_ORIGINS` to the exact public origin, comma separated for several, with wildcards rejected. The README also says the dashboard falls back to authenticated HTTP snapshots while Socket.IO is unavailable.
Is it safe to upgrade to Nutify 0.3.0 in place?
No. The README warns that this release is not backward compatible with previous releases, that the database must be recreated from scratch, and that you should start from a completely clean environment in a new empty folder without reusing files or data from older versions.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/dartsteven-nutify)Was this useful?