Skip to content

Releases: Ozark-Connect/unvr-nas-backup

v0.1.9

06 Apr 15:21

Choose a tag to compare

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, unconditionally
  • RETENTION_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.sh

Or if you installed standalone:

cd /opt/unvr-nas-backup
docker compose pull && docker compose up -d

v0.1.8

12 Mar 19:23

Choose a tag to compare

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.sh

Or if you installed standalone:

cd /opt/unvr-nas-backup
docker compose pull && docker compose up -d

v0.1.7

20 Feb 07:44

Choose a tag to compare

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.sh

Or if you installed standalone:

cd /opt/unvr-nas-backup
docker compose pull && docker compose up -d

The AES-GCM cipher change takes effect automatically on container restart - no .env changes needed.

v0.1.6

20 Feb 06:37

Choose a tag to compare

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_CHANNELS env var - controls which recording channels to back up (comma-separated). Defaults to 0 (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 to by-camera/{Cam}/low-quality/{date}/ with a _low-quality filename suffix, keeping them clearly separated from main stream recordings.
  • by-date symlinks for low-quality streams use {Cam}-low-quality naming.

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.sh

Standalone install:

cd /opt/unvr-nas-backup
docker compose pull && docker compose up -d

If 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

20 Feb 06:17

Choose a tag to compare

What's new

  • Fix: lookback window now uses recording end time - The BACKUP_HOURS window 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_HOURS is based on end time.

Upgrading

If you cloned the repo:

cd /opt/unvr-nas-backup
./scripts/update.sh

Standalone install:

cd /opt/unvr-nas-backup
docker compose pull && docker compose up -d

Your .env and archive are preserved in both cases.

v0.1.4

20 Feb 05:37

Choose a tag to compare

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.yml if 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 -d handles 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.sh

Or if standalone: docker compose pull && docker compose up -d

v0.1.3

20 Feb 04:50

Choose a tag to compare

Improvements

  • Atomic lockfile - replaced TOCTOU lockfile check with flock to eliminate race conditions
  • SCP fallback - PROTECT_VIDEO_PATH now 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 -i based .env generation with a direct heredoc write (works on macOS, BusyBox, etc.)

v0.1.1 — Initial Release

20 Feb 04:34

Choose a tag to compare

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.mp4 remuxing (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.)