Bridges third-party ONVIF cameras into UniFi Protect. It listens to ONVIF WS-PullPoint event streams and writes detection events (person, vehicle, animal, package) to the UniFi Protect PostgreSQL database so they appear natively in the Protect UI — including timeline clips, thumbnails, smart detection search, and security alarms.
It can also add smart detection to first-party UniFi cameras that lack native AI (e.g. G3 Instant). See First-party camera support.
If you're on Windows and don't want to touch SSH directly, download the
latest OnvifRecorderInstaller-v*.exe from
Releases
and run it. The GUI:
- Walks you through enabling SSH on your router (UniFi OS → System → Advanced → SSH).
- Tests the connection with the root password you set.
- Adds the apt repository + installs the package, streaming progress live.
- Saves the credentials (password encrypted with Windows DPAPI) in
HKCU\Software\OnvifRecorderInstallerso subsequent launches offer one-click upgrade, re-install, or uninstall.
The installer is unsigned, so Windows SmartScreen will prompt the first time — click More info → Run anyway.
SSH to your UniFi device and run:
curl -fsSL https://danielwoz.github.io/ubiquiti-protect-onvif-event-listener/install.sh | shThat's it. The installer:
- Installs the signing key to
/usr/share/keyrings/onvif-recorder-archive-keyring.gpg. - Detects your UniFi Protect release channel (Stable / Release Candidate / Early Access) and configures APT to pull matching builds.
apt-get install onvif-recorder, which auto-enables the systemd service plus two daily timers: channel sync and auto-updates.
Detections will appear in UniFi Protect within seconds of the first motion
event. Service logs land in journald (journalctl -u onvif-recorder).
What happens on first start:
- The Protect user ID is auto-discovered from the unifi-core database
and cached to
/var/lib/onvif-recorder/protect-user-id. - The Protect UI is live-patched so third-party cameras appear in the alarm creation picker.
- nginx is patched to expose the admin UI at
https://<device>/onvif/admin/. - Third-party ONVIF cameras are loaded from the Protect database and smart detection flags are enabled for them.
No flags are required for the default setup.
After install, manage the package without SSH at
https://<device>/onvif/admin/:
- Force an update check
- Switch release channel (stable / rc / early-access)
- Enable / disable auto-updates and change the schedule
- Uninstall the package
Auto-updates are on by default. The onvif-recorder-autoupdate.timer runs
daily with up to 2h random jitter; onvif-recorder-channel.timer keeps the
APT suite in sync with your Protect channel. Both can be disabled via the
admin UI or systemctl disable --now <timer>.
apt-get remove onvif-recorder # stops service, rolls back DB changes
apt-get purge onvif-recorder # also removes /etc/onvif-recorder and statePrior releases shipped as a raw binary at /root/onvif_recorder. The .deb
postinst migrates an existing manual install automatically on first upgrade —
no action required.
For day-to-day changes — toggling NanoDet override, picking which first-party
cameras get smart-detect, raising the per-camera rate limit — use the admin
UI: open https://<router-ip>/onvif/admin/ while logged in to UniFi OS.
Save & restart applies the change live.
The form covers detection, rate limiting, first-party cameras (tickbox list),
RTSP audio, and verbose logging. Anything not set in the UI falls back to
the command-line flag. See FLAGS.md for the full reference and
the more advanced flags that aren't in the UI (database paths, change log,
rollback, etc.).
Admin UI -> Logging -> verbose -> true -> Save & restart
equivalent to:
echo 'ONVIF_RECORDER_FLAGS="--verbose"' \
> /etc/default/onvif-recorder.local
systemctl restart onvif-recorderAdmin UI -> Detection -> camera_object_types
-> 192.168.1.108=animal -> Save & restart
By default, object detection notifications will arrive with no thumbnail attached. In order to receive thumbnails as well, you'll need to enable the global alarm manager.
Details on how to do this and the associated limitations are in THUMBNAILS.md
Check the log for errors:
journalctl -u onvif-recorder -fEnable verbose output to see per-camera lifecycle events:
systemctl stop onvif-recorder
/usr/bin/onvif-recorder --verboseCamera not working? Capture a raw diagnostic log and open a GitHub issue:
/usr/bin/onvif-recorder --verbose --raw_log=/tmp/onvif-raw.jsonl
# Let it run 60+ seconds, then Ctrl+C
# Attach /tmp/onvif-raw.jsonl to your issueCamera went offline and did not reconnect? The recorder automatically retries. After 3 consecutive failures it pauses for up to 1 hour, then resumes. If a camera reboot takes longer than expected, restart the service:
systemctl restart onvif-recorder- Ubuntu 24.04 x86_64 build host (Ubuntu 22.04 also works)
- Bazelisk installed at
~/.local/bin/bazel - Git with submodule support
git clone --recurse-submodules https://github.com/danielwoz/ubiquiti-protect-onvif-event-listener.git
cd ubiquiti-protect-onvif-event-listenerIf you already cloned without --recurse-submodules:
git submodule update --init --recursivemkdir -p ~/.local/bin
curl -fsSL https://github.com/bazelbuild/bazelisk/releases/latest/download/bazelisk-linux-amd64 \
-o ~/.local/bin/bazel
chmod +x ~/.local/bin/bazelAll C libraries are built from source via Bazel. Only build tools are needed:
sudo apt-get install -y \
build-essential clang cmake ninja-build perl \
python3-cpplint# x86_64 native binary
scripts/bz build --config=x86 //:onvif_recorder
# ARM64 release binary (PGO + ThinLTO, for Dream Router / Dream Machine)
scripts/bz build --config=arm64_release //:onvif_recorder
# All tests
scripts/bz test --config=x86 //test:allThe ARM64 build downloads its own sysroot automatically — no manual cross-toolchain setup is required.
The ARM64 binary is almost entirely statically linked:
libm.so.6 libc.so.6 ld-linux-aarch64.so.1 libgcc_s.so.1
libm.so.6 libc.so.6 ld-linux-x86-64.so.1 libgcc_s.so.1
libldap.so.2 liblber.so.2
main.cpp — Binary entry point
onvif_listener.hpp/.cpp — WS-PullPoint ONVIF subscription library
detection_recorder.hpp/.cpp — Detection -> PostgreSQL recorder
alarm_notifier.hpp/.cpp — Protect automation trigger + history
motion_poller.hpp/.cpp — First-party camera motion -> smart detect poller
camera_change_log.hpp/.cpp — Cameras-table change log and rollback
protect_ui_patch.hpp/.cpp — Live-patch Protect UI alarm picker
ubv_thumbnail.hpp/.cpp — UBV container encode/decode
jpeg_crop.hpp/.cpp — JPEG decode/crop/re-encode via libjpeg
object_detect.hpp/.cpp — NanoDet-M object detection via NCNN
unifi_camera_config.hpp/.cpp — Load camera credentials from Protect DB