Releases: Ozark-Connect/unvr-nas-backup
v0.1.9
What's new
Retention pruning - The archive now has optional auto-cleanup via two new environment variables:
RETENTION_DAYS: delete all footage older than N days, unconditionallyRETENTION_PERCENT: when disk usage exceeds N%, prune the oldest footage until it drops below the threshold
Both are disabled by default (the archive grows indefinitely, same as before). When both are set, age-based pruning runs first, then disk-based pruning kicks in if usage is still over the threshold. Pruning always works oldest-first using the existing by-date/ directory structure, and cleans up both the by-camera/ files and by-date/ symlinks.
To enable, add to your .env:
RETENTION_DAYS=45
RETENTION_PERCENT=85
Bug fix - Retention now runs even when the DB query returns zero new recordings. Previously the early exit on "no new recordings" would have skipped the retention step entirely.
Upgrading
cd /opt/unvr-nas-backup
./scripts/update.shOr if you installed standalone:
cd /opt/unvr-nas-backup
docker compose pull && docker compose up -dv0.1.8
What's new
Remux failure tracking - Corrupt .ubv files that fail remux are now recorded in a persistent failures list (/staging/.remux-failures). On subsequent runs, these files are skipped at the SCP step instead of being re-copied and re-attempted every cycle. This was a real problem: a single bad file would burn bandwidth and Protect device I/O every cron interval, indefinitely. To retry all failures (e.g., after a remux binary update), delete the file inside the container or recreate the container.
Upgrading
cd /opt/unvr-nas-backup
./scripts/update.shOr if you installed standalone:
cd /opt/unvr-nas-backup
docker compose pull && docker compose up -dv0.1.7
What's new
AES-128-GCM cipher for SSH connections - SCP file transfers now use hardware-accelerated AES-128-GCM instead of the default ChaCha20-Poly1305 software cipher. Both the CloudKey Gen2+ and UCG-Fiber (and likely all current Protect devices) have ARMv8 hardware AES extensions, so the cipher work gets offloaded to dedicated silicon. Peak CPU during transfers is about the same, but transfers complete ~27% faster, reducing total CPU-time burned on the Protect device per backup cycle.
Performance profiling report - We profiled a CloudKey Gen2+ running 5 cameras with backups polling every minute for five minutes. The results: the device sat at 70-90% CPU idle throughout, the database query was too fast to even register at 10-second sampling intervals, and memory/swap/PostgreSQL activity were completely unchanged during backups. See docs/performance.md for the full data, cipher benchmarks, and methodology.
Upgrading
cd /opt/unvr-nas-backup
./scripts/update.shOr if you installed standalone:
cd /opt/unvr-nas-backup
docker compose pull && docker compose up -dThe AES-GCM cipher change takes effect automatically on container restart - no .env changes needed.
v0.1.6
Channel filtering & low-quality stream separation
No breaking changes. Existing channel 0 archives are unchanged and detected as duplicates (no re-download).
What's new
BACKUP_CHANNELSenv var - controls which recording channels to back up (comma-separated). Defaults to0(main high-res stream only), which means channel 2 (low-quality sub-stream) files are no longer downloaded by default.- Separate archive path for low-quality streams - if you opt in with
BACKUP_CHANNELS=0,2, channel 2 recordings are archived toby-camera/{Cam}/low-quality/{date}/with a_low-qualityfilename suffix, keeping them clearly separated from main stream recordings. - by-date symlinks for low-quality streams use
{Cam}-low-qualitynaming.
Why
Channel 2 segments are ~10 hours long (vs ~28 minutes for channel 0), so they close very slowly and were the main cause of the "long delay before recordings appear" issue. Most users only need the main stream.
Upgrading
Update the container, then you're done. The new default (BACKUP_CHANNELS=0) automatically skips low-quality channel 2 files with no config changes needed.
If you cloned the repo:
cd /opt/unvr-nas-backup
./scripts/update.shStandalone install:
cd /opt/unvr-nas-backup
docker compose pull && docker compose up -dIf you want to also back up low-quality streams, add this to your .env after upgrading:
BACKUP_CHANNELS=0,2
Your .env and archive are preserved in both cases.
v0.1.5
What's new
- Fix: lookback window now uses recording end time - The
BACKUP_HOURSwindow was filtering on when recordings started, which meant low-activity cameras whose ~1 GB segments take many hours to fill could be missed entirely. The query now filters on when recordings finished, so any recently completed segment is picked up regardless of when it started. - README - Clarified that low-activity cameras may have longer delays before recordings appear in the archive, and that
BACKUP_HOURSis based on end time.
Upgrading
If you cloned the repo:
cd /opt/unvr-nas-backup
./scripts/update.shStandalone install:
cd /opt/unvr-nas-backup
docker compose pull && docker compose up -dYour .env and archive are preserved in both cases.
v0.1.4
What's new
- Standalone install - no git required. Just curl the install script and run it:
mkdir -p /opt/unvr-nas-backup && cd /opt/unvr-nas-backup curl -fsSLO https://raw.githubusercontent.com/Ozark-Connect/unvr-nas-backup/main/install.sh chmod +x install.sh && ./install.sh
- Installer auto-downloads
compose.ymlif not present, so the full repo is no longer needed - Two-tier disk space warnings - warns at <100 GB free, critical warning at <10 GB free
- Update script no longer unnecessarily stops the container (
docker compose up -dhandles recreation) - README improvements - troubleshooting notes for healthcheck status and permissions, three quick start options (standalone, git clone, manual)
Upgrading
cd /opt/unvr-nas-backup
./scripts/update.shOr if standalone: docker compose pull && docker compose up -d
v0.1.3
Improvements
- Atomic lockfile - replaced TOCTOU lockfile check with
flockto eliminate race conditions - SCP fallback -
PROTECT_VIDEO_PATHnow used as fallback when the DB-reported folder path is not accessible - Input validation - numeric fields from the database are validated before use
- Env quoting fix - single quotes in environment variable values are now properly escaped
- Installer portability - replaced
sed -ibased.envgeneration with a direct heredoc write (works on macOS, BusyBox, etc.)
v0.1.1 — Initial Release
First public release of unvr-nas-backup.
Dockerized backup system that pulls continuous surveillance video from a UniFi Protect device (CloudKey, UCG, UDM, UNVR, etc.), remuxes .ubv to .mp4 with camera names, and archives to a NAS.
Features
- Automated backup via cron with configurable schedule
- SSH-based video retrieval from Protect devices
.ubv→.mp4remuxing (multi-arch: amd64/arm64)- Archive organized by camera and date with symlinks
- Batch throttling to avoid overwhelming the Protect device
- Interactive installer (
install.sh) and helper scripts (status.sh,run-now.sh,update.sh) - Health check with container status reporting
- Pre-built image at
ghcr.io/ozark-connect/unvr-nas-backup:latest
Tested Devices
- UniFi CloudKey Gen2+
- UniFi Cloud Gateway (UCG-Fiber, UCG-Ultra, UCG-Max, etc.)