A simple, single-binary IP camera recorder. Think of it as a dashcam for your property.
Find a file
2026-09-13 12:30:57 +01:00
public fix: real camera online status — checks latest.jpg modification time 2026-08-10 07:03:31 +01:00
.gitignore fix: go2rtc config path absolute, gitignore generated file 2026-08-06 13:16:02 +01:00
api.go fix: online check also looks for active recorder .part files 2026-08-10 07:23:24 +01:00
auth.go refactor: viewer always listens, access blocked at request level when disabled 2026-08-06 19:31:10 +01:00
CHANGELOG.md chore: v0.4.0 — cleanup, changelog, version bump 2026-08-06 18:30:49 +01:00
cleaner.go docs: MIT license, license headers, README with project story and roadmap 2026-08-06 10:48:35 +01:00
config.go fix: default storage to $HOME/nvr_data, not /mnt/recordings 2026-08-06 19:43:01 +01:00
config.json.sample fix: default storage to $HOME/nvr_data, not /mnt/recordings 2026-08-06 19:43:01 +01:00
deploy_nvr.sh feat: storage path in Settings UI, deploy creates directory 2026-08-06 19:41:14 +01:00
go.mod refactor: pivot to JSON config — no yaml dependency, jq in bash, greenfield detection 2026-08-06 14:54:43 +01:00
go.sum refactor: pivot to JSON config — no yaml dependency, jq in bash, greenfield detection 2026-08-06 14:54:43 +01:00
go2rtc.go fix: go2rtc config path absolute, gitignore generated file 2026-08-06 13:16:02 +01:00
LICENSE docs: MIT license, license headers, README with project story and roadmap 2026-08-06 10:48:35 +01:00
main.go refactor: viewer always listens, access blocked at request level when disabled 2026-08-06 19:31:10 +01:00
onvif.go docs: MIT license, license headers, README with project story and roadmap 2026-08-06 10:48:35 +01:00
README.md docs: document live streaming architecture (go2rtc passthrough) 2026-09-13 12:30:57 +01:00
recorder.go fix: use -timeout (RTSP socket I/O) instead of invalid -rw_timeout 2026-09-12 20:30:32 +01:00
remove.sh fix: graceful stop via systemctl + targeted kill, no pkill -9 2026-08-06 15:23:09 +01:00
server.go feat: camera config proxy — access cameras remotely through NextNVR 2026-08-10 06:47:02 +01:00
snapshot.go docs: MIT license, license headers, README with project story and roadmap 2026-08-06 10:48:35 +01:00
VERSION chore: v0.4.0 — cleanup, changelog, version bump 2026-08-06 18:30:49 +01:00

NextNVR

A simple, single-binary IP camera recorder. Think of it as a dashcam for your property.


Why NextNVR?

The open-source NVR landscape is rich with incredible projects — Frigate, Shinobi, ZoneMinder, Blue Iris alternatives — backed by passionate communities. They're powerful. They're feature-packed. But they've also become complex. Docker containers, AI pipelines, multi-service architectures, cloud integrations — for many users, it's too much.

NextNVR is different. It does one thing: continuously record your IP cameras to disk and let you review the footage. No AI, no cloud, no Docker — just a single Go binary you can run on a Raspberry Pi 5, an old thin client, or a VM. Deploy in 30 seconds — download, run, done.

This project was built with the help of AI — designed through conversation, refined through iteration, and shaped by the simple principle that surveillance software should be boring. It should just work.


Quick Install

curl -sSLO https://git.lohmar.co.uk/cclohmar/NextNVR/raw/branch/main/deploy_nvr.sh
chmod +x deploy_nvr.sh
./deploy_nvr.sh

The script asks where to store recordings, then installs everything. Press Enter for the default (~/nvr_data) or type your own path.

To update an existing installation — run the same script again. It stops the service, pulls the latest code, rebuilds, and restarts.

To pre-set the storage path:

STORAGE_PATH=/mnt/recordings ./deploy_nvr.sh

Supported Systems

Distro Package Manager Architecture
Debian 12+ / Ubuntu 22.04+ apt amd64, arm64
Fedora 40+ / RHEL 9+ dnf / yum amd64, arm64
Arch Linux pacman amd64, arm64
Alpine Linux apk amd64, arm64
Raspberry Pi 5 any above arm64

What It Does

Feature Detail
📹 Recording Up to 8 cameras simultaneously, 5-minute MP4 clips, zero transcoding
💾 Storage Set any path — local disk, USB drive, or network mount
🖥️ Live Wall 4×2 grid with auto-refreshing snapshots. Click for full live stream
🔍 Playback Browse recordings by camera and date with snapshot thumbnails
⚙️ Setup Wizard ONVIF network scan + camera naming. Add cameras manually too.
🔐 Two-Port Auth :8080 with optional master login, :8090 always-open viewer for LAN
🔒 100% Local Everything runs on your hardware. No cloud, no subscriptions, no telemetry

How Live Streaming Works

NextNVR doesn't transcode video itself. It delegates all live-stream conversion to go2rtc, a separate binary it launches as a child process. The camera does the encoding — NextNVR and go2rtc pass the stream through unchanged.

IP Camera (RTSP)
   │  sub stream:  /Streaming/Channels/102  (live view)
   │  main stream: /Streaming/Channels/101  (recording)
   ▼
go2rtc (child process, listens on :1984)
   │  holds persistent RTSP connections, re-serves as WebRTC / MSE / MJPEG
   ▼
NextNVR (proxies /go2rtc/ through its own port for a single browser origin)

Two kinds of "live"

View Mechanism Notes
Live Wall grid 1 fps JPEG snapshot polling Not a video stream. The snapshot engine grabs a frame every second and writes latest.jpg; the browser refreshes it every 3 seconds. Cheap — ideal for an overview.
Focus view (click a tile) Real stream via go2rtc Opens go2rtc's stream.html player, which negotiates WebRTC → MSE → MJPEG. Uses the camera substream for low bandwidth.

Codec & resolution

  • Zero transcoding. The recorder uses -c copy (stream-copy); go2rtc passes the codec through unchanged. The browser receives exactly what the camera encodes.
  • Codec = your camera's codec. Set cameras to H.264 — WebRTC support for H.265/HEVC is poor, and NextNVR will not re-encode it.
  • Live view uses the substream ({id}_sub); the main stream ({id}_main) is recorded to disk but is not shown in the live view.
  • Ports: go2rtc runs on :1984; NextNVR reverse-proxies /go2rtc/ through its own port so a single origin serves the browser (no mixed-content issues behind HTTPS).

Where to Store Recordings

NextNVR saves video clips and snapshots to a directory you choose. By default this is ~/nvr_data. You can change it in Settings → Storage path or by editing config.json.

Local disk

# Use the system drive (default)
Storage path: /home/pi/nvr_data

# Use a second internal drive
sudo mkdir /data/nvr
sudo chown $USER:$USER /data/nvr
# Then set: /data/nvr

USB drive

# Plug in the drive, find it:
lsblk
# Mount it:
sudo mkdir /mnt/usb-drive
sudo mount /dev/sdb1 /mnt/usb-drive
# Make it permanent (/etc/fstab):
# /dev/sdb1 /mnt/usb-drive ext4 defaults 0 2
# Then set: /mnt/usb-drive/nvr

Network share (NAS / NFS)

# Install NFS client:
sudo apt install nfs-common
# Mount the share:
sudo mkdir /mnt/nas
sudo mount 192.168.1.100:/volume1/nvr /mnt/nas
# Make it permanent (/etc/fstab):
# 192.168.1.100:/volume1/nvr /mnt/nas nfs defaults 0 0
# Then set: /mnt/nas

Authentication

NextNVR runs two HTTP servers:

Port Audience Auth Access
8080 You (admin) Optional master login Live Wall, Playback, Settings
8090 Guests / LAN None Live Wall only

Auth is disabled by default. To enable it, open Settings → 🔐 Authentication card, set a master username and password, check "Require login", and save. Port 8090 stays open regardless — ideal for a QR code at a guesthouse: "Watch what we watch."

To reset the master password, edit /opt/nextnvr/config.json and set "auth": {"enabled": false}, then restart.


Removing

curl -sSLO https://git.lohmar.co.uk/cclohmar/NextNVR/raw/branch/main/remove.sh
chmod +x remove.sh
./remove.sh

Stops the service, removes /opt/nextnvr/, cleans up systemd. Recordings on disk are untouched.


Remote Access (Optional)

  1. Dynamic DNS — use DuckDNS, No-IP, or Cloudflare for a domain name
  2. Port forwarding — forward port 8080 to the NextNVR server (and 8090 if you want LAN guests to have open access)
  3. For production, put NextNVR behind a reverse proxy (Zoraxy, Nginx) with HTTPS

Roadmap

Feature Status
Continuous recording (-c copy, zero transcoding) ✅ Done
ONVIF auto-discovery + web setup wizard ✅ Done
Live wall with snapshot grid + focus view ✅ Done
Playback browser with date presets ✅ Done
7-day retention with auto-cleanup ✅ Done
Two-port authentication (master + viewer) ✅ Done
NAS / Network storage (point recordings to any mounted path — NFS, SMB, USB) ✅ Done
Camera-triggered recording (ONVIF motion/person detection events via API) 🔜 Planned
Multi-platform builds (Raspberry Pi, ARM SBCs) 🔜 Planned

Not Planned

  • ❌ Cloud connectivity of any kind
  • ❌ AI/ML object detection (use your camera's built-in detection)
  • ❌ Mobile apps (the web UI is responsive and PWA-ready)

Server Commands

sudo systemctl status nextnvr      # is it running?
sudo systemctl restart nextnvr     # restart
sudo journalctl -u nextnvr -f      # watch logs

Config file: /opt/nextnvr/config.json Recordings: /mnt/recordings/


License

MIT — see LICENSE

Built with ❤️ and AI in Mindelo, Portugal. 🇵🇹