-
Notifications
You must be signed in to change notification settings - Fork 33
Docker Compose Guide
This page documents the secure Compose profiles shipped with Nutify 0.3.0.
The default profile supports remote NUT servers and directly attached USB HID UPS devices. Stable serial or non-USB device paths use an explicit scoped override. Environment-managed OIDC is a separate advanced override so the simple profile stays easy to start and maintain.
| Host | Image tag |
|---|---|
| AMD64/x86_64 | dartsteven/nutify:latest-amd64 |
| Apple Silicon | dartsteven/nutify:latest-mac-arm64 |
| Raspberry Pi 3, 32-bit OS | dartsteven/nutify:latest-raspberrypi3-armv7 |
| Raspberry Pi 4, 32-bit OS | dartsteven/nutify:latest-raspberrypi4-armv7 |
| Raspberry Pi 4/5, 64-bit OS | dartsteven/nutify:latest-raspberrypi5-arm64 |
Raspberry Pi 3 requires a 32-bit operating system for the supported ARMv7 image.
The supplied docker-compose.yaml does not:
- use
privileged: true - mount the complete host
/devtree - grant
SYS_ADMIN,SYS_RAWIO, orMKNOD - include a default or hardcoded
SECRET_KEY
It drops all capabilities, adds only the capabilities needed during startup and
service supervision, enables no-new-privileges, and runs the web application as
the unprivileged nut user. USB support is constrained to character-device
major 189 and /dev/bus/usb; udev metadata is mounted read-only. Startup grants
the nut runtime user only the numeric groups found on those USB device nodes
and never changes host device ownership or modes.
Use this default for network targets and USB HID targets detected by
nut-scanner.
Copy the example file and generate a unique secret:
cp .env.example .env
printf 'SECRET_KEY=%s\n' "$(openssl rand -hex 32)" > .envKeep .env private. Do not commit it and do not change SECRET_KEY between
restarts. Existing encrypted provider credentials and password recovery depend on
this key.
Optional exact reverse-proxy origins can be added later:
SOCKETIO_ALLOWED_ORIGINS=https://nutify.example.com,https://nutify.example.netWildcards are rejected. A correctly configured proxy should still forward WebSocket upgrades; Nutify uses authenticated HTTP snapshots when Socket.IO is temporarily unavailable.
Repository file: docker-compose.yaml
services:
nut:
image: dartsteven/nutify:latest-amd64
container_name: Nutify
cap_drop:
- ALL
cap_add:
- CHOWN
- DAC_OVERRIDE
- FOWNER
- KILL
- SETGID
- SETUID
security_opt:
- no-new-privileges:true
device_cgroup_rules:
- "c 189:* rwm"
volumes:
- ./Nutify/logs:/app/nutify/logs
- ./Nutify/instance:/app/nutify/instance
- ./Nutify/ssl:/app/ssl
- ./Nutify/etc/nut:/etc/nut
- /dev/bus/usb:/dev/bus/usb:rw
- /run/udev:/run/udev:ro
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:-}
LOG: "true"
LOG_LEVEL: INFO
LOG_WERKZEUG: "true"
ENABLE_LOG_STARTUP: Y
SSL_ENABLED: "false"
SOCKETIO_ALLOWED_ORIGINS: ${SOCKETIO_ALLOWED_ORIGINS:-}
ports:
- "3493:3493"
- "5050:5050"
- "443:443"
restart: alwaysUse the complete repository file rather than copying only this explanatory excerpt.
The default docker-compose.yaml starts with Nutify local authentication. Run:
docker compose up -dAn administrator can later configure optional OIDC from System -> Authentication. This stores the encrypted configuration in Nutify's database and requires no Compose changes.
Repository override: docker-compose.oidc.yaml
Use this profile when deployment automation must own the OIDC configuration.
Add the required values to the private .env file:
OIDC_ISSUER=https://sso.example.com/application/o/nutify/
OIDC_CLIENT_ID=nutify
OIDC_CLIENT_SECRET=replace-with-provider-secret
OIDC_REDIRECT_URI=https://nutify.example.com/auth/oidc/callback
OIDC_ADMIN_GROUP=nutify-admins
OIDC_USER_GROUP=nutify-usersStart the base file and advanced override together:
docker compose \
-f docker-compose.yaml \
-f docker-compose.oidc.yaml \
up -dThis sets OIDC_CONFIG_SOURCE=environment, makes the web Authentication page
read-only, requires HTTPS, requires an explicit administrator group, and keeps
OIDC_ALLOW_ALL_USERS=false. Local recovery remains available at
/auth/login?local=1.
Then open http://<host>:5050 and complete the setup wizard.
Useful commands:
docker compose ps
docker compose logs -f
docker compose downThis override is unrelated to Nutify's Single Monitor and Multi Monitor profiles. Monitoring profile selection happens in the Wizard and controls target behavior. Hardware exposure happens at the Docker boundary before the container starts.
The Wizard can select a NUT driver and device port, but it cannot add a host
device to an already running container or change Docker device-cgroup rules.
Use docker-compose.hardware.yaml only when the NUT driver inside Nutify must
open a stable serial or non-USB path. It is unnecessary for USB HID devices and
remote or host-managed NUT servers.
For one directly attached serial UPS, set stable device values in .env:
NUTIFY_DEVICE=/dev/serial/by-id/your-ups-device
NUTIFY_DEVICE_CONTAINER=/dev/nutify-ups
NUTIFY_DEVICE_GID=20NUTIFY_DEVICE_GID must be the numeric host group that owns the device. Start
with the scoped override:
docker compose \
-f docker-compose.yaml \
-f docker-compose.hardware.yaml \
up -dConfigure the NUT driver port as /dev/nutify-ups. The override exposes only the
selected device; it does not expose all host devices. This narrower permission
is why the hardware override exists instead of enabling privileged mode in the
default Compose profile.
A stable udev alias can be created on Linux:
SUBSYSTEM=="tty", ATTRS{idVendor}=="VVVV", ATTRS{idProduct}=="PPPP", SYMLINK+="nutify-ups", GROUP="dialout", MODE="0660"
Replace VVVV and PPPP with values from udevadm info.
The base Compose file enables the Wizard's Auto-detect with nut-scanner and
manual Detected USB Port picker. It mounts only /dev/bus/usb, permits only
Linux USB character devices (major 189), and mounts /run/udev read-only. It
does not use privileged mode, map all of /dev, or grant hardware-management
capabilities.
Nutify detects the host USB device GIDs at startup and adds only its unprivileged
runtime users to matching container groups. If startup logs report that no USB
GID was found, set the numeric owner group explicitly in .env:
NUTIFY_USB_GID=46Find the value on the Linux host with stat -c '%g' /dev/bus/usb/BBB/DDD.
GID 0 is rejected: never add the application user to the root group. If the
device is owned by root:root, install the host NUT udev rules or create a
dedicated group/rule with mode 0660, then set NUTIFY_USB_GID to that group's
numeric ID. Recreate the container after changing .env. If Docker itself
cannot expose the host USB bus (for example, some non-Linux Docker Desktop
environments), run NUT on a Linux host and use Network Client mode.
| Host path | Container path | Purpose |
|---|---|---|
./Nutify/logs |
/app/nutify/logs |
Application logs |
./Nutify/instance |
/app/nutify/instance |
SQLite DB and runtime state |
./Nutify/ssl |
/app/ssl |
TLS certificate/key |
./Nutify/etc/nut |
/etc/nut |
Generated NUT configuration |
/dev/bus/usb |
/dev/bus/usb |
USB HID discovery and driver access |
/run/udev |
/run/udev |
Read-only USB identity metadata |
Back up these directories before replacing an image or changing topology.
Update:
docker compose pull
docker compose up -dRollback by restoring the previous image tag and running docker compose up -d.
Validate login, target connectivity, Event Matrix routes, and report delivery
after either operation.
Nutify 0.3.0 is not database-compatible with previous releases. A clean
instance directory is required when crossing that boundary.
- Open
http://<host>:5050. - Create the first administrator.
- Select the correct Single/Multi topology.
- Test and save the target connection.
- Confirm target telemetry in the TopBar and dashboard.
- Configure Provider entries globally, then Event Matrix/Reporter per target.
Container does not start:
- confirm
.envcontains non-emptySECRET_KEY - confirm selected image tag matches host architecture
- inspect
docker compose logs
Remote UPS does not connect:
- confirm host, port, UPS identifier, monitor username, and password
- confirm port
3493is reachable from the container - confirm remote
upsdACL allows the container network
Direct device fails:
- for USB HID, confirm
/dev/bus/usbexists in the container and inspect startup logs for the detected USB GID - if automatic USB GID detection fails, set
NUTIFY_USB_GIDto the numeric host group and recreate the container - for serial/non-USB, confirm the stable host path and
NUTIFY_DEVICE_GID - confirm driver port equals
NUTIFY_DEVICE_CONTAINER - confirm no host service already owns the serial device
- use a random, persistent
SECRET_KEY - use strong, unique administrator credentials
- expose only required ports to trusted networks
- use exact reverse-proxy origins, never wildcards
- expose at most one required hardware device
- never add privileged mode or broad host device mounts
- rotate SMTP, ntfy, Telegram, and webhook credentials after exposure
- Main + Topbar + Multi-UPS Guide
- Multi-UPS Monitoring Guide
- Sidebar Data Pages Guide
- Reports and Notifications End-to-End Guide