Skip to content

Latest commit

 

History

History
616 lines (470 loc) · 16.9 KB

File metadata and controls

616 lines (470 loc) · 16.9 KB

Docker Deployment Guide

Verified against LightNVR 0.37.x.

This guide provides comprehensive information about deploying LightNVR using Docker.

On Windows? Everything here applies, but the host setup has its own pitfalls. Start with Running LightNVR on Windows with Podman + WSL2.

On Home Assistant OS or Supervised? Use the add-on instead — it handles networking, storage, and backups for you. See LightNVR on Home Assistant.

Table of Contents

Quick Start

Using Docker Compose (Recommended)

No clone or build required — the compose file pulls the published image. Save the repository's docker-compose.yml into an empty directory (or create it from the example below):

services:
  lightnvr:
    image: ghcr.io/opensensor/lightnvr:latest
    container_name: lightnvr
    restart: unless-stopped
    ports:
      - "8080:8080"     # Web UI
      - "8554:8554"     # RTSP
      - "8555:8555"     # WebRTC TCP
      - "8555:8555/udp" # WebRTC UDP
      - "1984:1984"     # go2rtc API
    volumes:
      # DO NOT mount /var/lib/lightnvr directly - it will overwrite web assets!
      - ./config:/etc/lightnvr
      - ./data:/var/lib/lightnvr/data
    environment:
      - TZ=UTC  # Change to your timezone (e.g., America/New_York)
      # ONVIF discovery network for containers, e.g. 192.168.1.0/24
      # - LIGHTNVR_ONVIF_NETWORK=192.168.1.0/24

Then start it:

# Pull the image and start the container
docker compose up -d

# View logs
docker compose logs -f

# Access the web UI
# http://localhost:8080
# Default credentials: admin / admin - change immediately, see First Run Experience

To build the image from source instead, clone the repository (with git submodule update --init --recursive) and switch the image: line in docker-compose.yml to the commented-out build: block — see Building from Source.

Using Docker Run

docker run -d \
  --name lightnvr \
  --restart unless-stopped \
  -p 8080:8080 \
  -p 8554:8554 \
  -p 8555:8555 \
  -p 8555:8555/udp \
  -p 1984:1984 \
  -v ./config:/etc/lightnvr \
  -v ./data:/var/lib/lightnvr/data \
  -e TZ=America/New_York \
  ghcr.io/opensensor/lightnvr:latest

Container Architecture

The LightNVR Docker container is built using a multi-stage build process:

  1. Builder Stage - Compiles LightNVR and go2rtc from source
  2. Runtime Stage - Minimal Debian-based image with only runtime dependencies

Key Features

  • Automatic Initialization - Creates default configs on first run
  • Persistent Configuration - Config files survive container restarts
  • Protected Web Assets - Web UI files stored in template location
  • Health Checks - Built-in health monitoring
  • Multi-Architecture - Supports amd64, arm64, and armv7

Volume Management

Volume Structure

The container uses two primary volume mounts:

/etc/lightnvr/              # Configuration files
├── lightnvr.ini            # Main configuration
└── go2rtc/
    └── go2rtc.yaml         # go2rtc configuration

/var/lib/lightnvr/data/     # Persistent data
├── database/
│   └── lightnvr.db         # SQLite database
├── recordings/
│   ├── hls/                # HLS recordings
│   └── mp4/                # MP4 recordings
└── models/                 # Object detection models

Important Volume Notes

⚠️ DO NOT mount /var/lib/lightnvr directly!

Mounting the entire /var/lib/lightnvr directory will overwrite the web assets and break the web UI. Always mount only the subdirectories you need:

✅ Correct:

volumes:
  - ./config:/etc/lightnvr
  - ./data:/var/lib/lightnvr/data

❌ Incorrect:

volumes:
  - ./config:/etc/lightnvr
  - ./data:/var/lib/lightnvr  # This will break the web UI!

NFS Volume Considerations

When using NFS volumes (common with NAS devices like Synology), there are some important considerations:

Permission Issues

NFS volumes may have different permission models than local filesystems. If you encounter "Operation not permitted" errors:

  1. Check NFS mount options: Ensure your NFS share is mounted with appropriate permissions

    volumes:
      config:
        driver_opts:
          type: "nfs"
          o: "nfsvers=4,addr=192.168.1.100,rw,nolock"  # Add nolock if needed
          device: ":/volume1/docker/lightnvr/config"
  2. UID/GID mapping: The container runs as root by default. Ensure your NFS export allows root access or map UIDs appropriately:

    • On Synology NAS: Enable "Map all users to admin" or "Squash" options in NFS permissions
    • Or use all_squash,anonuid=1000,anongid=1000 in NFS export options
  3. Directory pre-creation: For better reliability with NFS, pre-create the directory structure on your NAS:

    # On your NAS or NFS server
    mkdir -p /volume1/docker/lightnvr/config/go2rtc
    mkdir -p /volume1/docker/lightnvr/data/recordings/mp4
    mkdir -p /volume1/docker/lightnvr/data/database
    mkdir -p /volume1/docker/lightnvr/data/models
    chmod -R 755 /volume1/docker/lightnvr

Recording Issues on NFS

If recordings are not being created:

  1. Check write permissions: The container logs will show write permission test results on startup
  2. Verify NFS mount: Ensure the NFS volume is actually mounted inside the container:
    docker exec lightnvr-latest ls -la /var/lib/lightnvr/data/recordings/mp4
  3. Check available space: Ensure your NAS has sufficient free space
  4. Review logs: Check for "Failed to open input" or "Operation not permitted" errors:
    docker logs lightnvr-latest | grep -i error

Example NFS Configuration

Here's a complete example for Synology NAS with Traefik:

volumes:
  config:
    driver_opts:
      type: "nfs"
      o: "nfsvers=4,addr=${IpAddressNFS},rw,nolock"
      device: ":/${NFSVolumePath}/${SystemId}/lightnvr-config"

  data:
    driver_opts:
      type: "nfs"
      o: "nfsvers=4,addr=${IpAddressNFS},rw,nolock"
      device: ":/${NFSVolumePath}/${SystemId}/lightnvr-data"

Note: The nolock option can help with some NFS permission issues but may reduce file locking safety. Use with caution in production environments.

Web Assets

Web assets are baked into the image at /var/lib/lightnvr/www during the build. The entrypoint verifies index.html is present and refuses to start if it is not; there is no copy-on-first-run step. This means:

  • The web UI works immediately after container start
  • Pulling a new image updates the web UI
  • Never mount a volume over /var/lib/lightnvr — it would hide the assets and the container will fail to start. Mount /var/lib/lightnvr/data instead.

Network Configuration

Port Mapping

Port Protocol Service Description
8080 TCP Web UI Main web interface
8554 TCP RTSP RTSP streaming server
8555 TCP/UDP WebRTC WebRTC streaming
1984 TCP go2rtc API go2rtc REST API

Network Modes

Bridge Mode (Default)

services:
  lightnvr:
    ports:
      - "8080:8080"
      - "8554:8554"
      - "8555:8555"
      - "8555:8555/udp"
      - "1984:1984"

Host Mode (For Better Performance)

services:
  lightnvr:
    network_mode: host

Note: Host mode provides better performance but exposes all ports directly on the host.

Environment Variables

Available Variables

Variable Default Description
TZ UTC Container timezone
GO2RTC_CONFIG_PERSIST true Persist go2rtc config across restarts
LIGHTNVR_ONVIF_NETWORK (none) Override ONVIF discovery network (e.g., 192.168.1.0/24)
LIGHTNVR_MIGRATIONS_DIR (none) Override where database migrations are read from. Falls back to ./db/migrations, then /usr/share/lightnvr/migrations, then a path relative to the binary.

LIGHTNVR_AUTO_INIT and LIGHTNVR_WEB_ROOT are set by the image and by docker-compose.yml, but nothing reads them — neither the entrypoint nor LightNVR itself. Setting them has no effect. They are left in place because removing them from existing compose files would be a breaking-looking change for no gain; treat them as inert. Config initialization always runs when /etc/lightnvr/lightnvr.ini is absent, and the web root is fixed at /var/lib/lightnvr/www.

Example Usage

environment:
  - TZ=America/New_York
  - GO2RTC_CONFIG_PERSIST=true
  - LIGHTNVR_AUTO_INIT=true
  - LIGHTNVR_ONVIF_NETWORK=192.168.1.0/24

ONVIF Discovery in Containers

When running in a container, ONVIF auto-detection skips Docker bridge interfaces by default. To enable ONVIF camera discovery in containerized deployments, set the LIGHTNVR_ONVIF_NETWORK environment variable to specify which network to scan:

services:
  lightnvr:
    environment:
      # Specify the network where your cameras are located
      - LIGHTNVR_ONVIF_NETWORK=192.168.1.0/24

Network Priority:

  1. Explicit network parameter (API calls)
  2. LIGHTNVR_ONVIF_NETWORK environment variable
  3. discovery_network in config file ([onvif] section)
  4. Auto-detection (skips Docker interfaces)

Finding Your Network:

# On the Docker host, find your camera network
ip addr show

# Example: If your host IP is 192.168.1.100 with netmask 255.255.255.0
# Use: LIGHTNVR_ONVIF_NETWORK=192.168.1.0/24

First Run Experience

On first container start, the entrypoint script automatically:

  1. Creates Directory Structure

    /etc/lightnvr/
    /var/lib/lightnvr/data/database/
    /var/lib/lightnvr/data/recordings/
    /var/lib/lightnvr/data/models/
    
  2. Verifies Web Assets

    • Checks that /var/lib/lightnvr/www/index.html exists — the assets are baked into the image at build time and are not copied at startup
    • Startup fails with an error if they are missing, which normally means /var/lib/lightnvr was mounted over. Mount /var/lib/lightnvr/data, never /var/lib/lightnvr itself.
  3. Creates Default Configuration

    • lightnvr.ini with sensible defaults
    • go2rtc.yaml with WebRTC/STUN configuration
  4. Seeds Detection Models

    • Copies /usr/share/lightnvr/models/ into /var/lib/lightnvr/data/models/, only if the destination is empty
  5. Initializes Database

    • Creates SQLite database on first access
    • Sets up default admin user

Default Credentials

  • Username: admin
  • Password: admin

⚠️ The web server binds 0.0.0.0 inside the container, so once you publish port 8080 these credentials are the only thing in front of the first-login flow. LightNVR requires the default password to be replaced immediately after login and blocks the rest of the UI and password-authenticated APIs until that succeeds. You then sign in again with the new password; MFA, when enabled, follows on that sign-in.

To avoid the default ever being valid, set password in the [web] section of config/lightnvr.ini before the first start — the admin account is then created with that password instead. The setting is only read when the account is created; afterwards users are managed from the Users page.

Existing accounts are not flagged during an upgrade. API-key authentication and demo mode remain available while a freshly bootstrapped administrator is awaiting a password change.

WebRTC Configuration

The container includes pre-configured WebRTC support with STUN servers for NAT traversal.

Default go2rtc Configuration

webrtc:
  listen: :8555
  ice_servers:
    - urls: [stun:stun.l.google.com:19302]
  candidates:
    - "*:8555"
    - stun:stun.l.google.com:19302

Customizing WebRTC

Edit ./config/go2rtc/go2rtc.yaml:

webrtc:
  listen: :8555
  ice_servers:
    - urls: [stun:stun.l.google.com:19302]
    - urls: [turn:your-turn-server.com:3478]
      username: your-username
      credential: your-password
  candidates:
    - "YOUR_PUBLIC_IP:8555"

Restart the container to apply changes:

docker compose restart

Port Forwarding for WebRTC

If running behind NAT/firewall, forward these ports:

  • 8555/TCP - WebRTC signaling
  • 8555/UDP - WebRTC media

Troubleshooting

Web UI Not Loading

Symptom: Accessing http://localhost:8080 shows nothing or 404 error

Causes:

  1. Mounted /var/lib/lightnvr directly (overwrote web assets)
  2. Web assets not copied during initialization

Solution:

# Stop container
docker compose down

# Remove incorrect volume mount
# Edit docker-compose.yml to use /var/lib/lightnvr/data instead

# Remove web directory to force re-initialization
rm -rf ./data/web

# Start container
docker compose up -d

Database Lost on Restart

Symptom: All streams and settings disappear after container restart

Cause: Data volume not mounted correctly

Solution:

# Verify volume mounts
docker inspect lightnvr | grep -A 10 Mounts

# Should show:
# /etc/lightnvr
# /var/lib/lightnvr/data

WebRTC Not Working

Symptom: WebRTC streams fail to connect (ICE connection failures)

Causes:

  1. ICE candidates not resolving to the correct host IP address
  2. UDP port 8555 not forwarded
  3. Firewall blocking WebRTC
  4. STUN server not reachable

Solution:

The most common fix is setting your host machine's local IP as external_ip in ./config/lightnvr.ini:

[go2rtc]
external_ip = 192.168.1.100  ; Replace with your machine's local IP

To find your local IP:

# Linux
hostname -I | awk '{print $1}'

# macOS
ipconfig getifaddr en0    # or en1 for Wi-Fi

Then restart the container:

docker compose restart

If that doesn't resolve it, check go2rtc connectivity:

# Check if go2rtc is running
docker exec lightnvr ps aux | grep go2rtc

# Check go2rtc logs
docker exec lightnvr cat /var/log/lightnvr/go2rtc.log

# Test STUN connectivity
docker exec lightnvr nc -vzu stun.l.google.com 19302

go2rtc Config Keeps Resetting

Symptom: Changes to go2rtc.yaml are lost on restart

Cause: GO2RTC_CONFIG_PERSIST set to false

Solution:

environment:
  - GO2RTC_CONFIG_PERSIST=true

Advanced Configuration

Custom Configuration Path

Mount a custom config file:

docker run -d \
  --name lightnvr \
  -v /path/to/custom/lightnvr.ini:/etc/lightnvr/lightnvr.ini \
  -v ./data:/var/lib/lightnvr/data \
  ghcr.io/opensensor/lightnvr:latest

Running Multiple Instances

services:
  lightnvr-1:
    image: ghcr.io/opensensor/lightnvr:latest
    ports:
      - "8080:8080"
      - "8554:8554"
    volumes:
      - ./config-1:/etc/lightnvr
      - ./data-1:/var/lib/lightnvr/data

  lightnvr-2:
    image: ghcr.io/opensensor/lightnvr:latest
    ports:
      - "8081:8080"
      - "8555:8554"
    volumes:
      - ./config-2:/etc/lightnvr
      - ./data-2:/var/lib/lightnvr/data

Resource Limits

services:
  lightnvr:
    deploy:
      resources:
        limits:
          cpus: '2'
          memory: 2G
        reservations:
          cpus: '1'
          memory: 512M

Logging Configuration

services:
  lightnvr:
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

Migration from Previous Versions

If you're upgrading from an older version that mounted /var/lib/lightnvr directly, change the mount to /var/lib/lightnvr/data and move your existing content down one level:

docker compose down

# Your old volume held database/, recordings/ and models/ at its root, alongside
# the web assets. Only the first three should persist.
mkdir -p ./data
mv ./old-volume/database ./old-volume/recordings ./old-volume/models ./data/ 2>/dev/null

# Then point the volume at the data subdirectory:
#   - ./data:/var/lib/lightnvr/data
docker compose up -d

Mounting /var/lib/lightnvr itself hides the web assets baked into the image, and the container will refuse to start with a "Web assets not found" error.

Building from Source

# Clone repository
git clone https://github.com/opensensor/lightNVR.git
cd lightNVR

# Initialize submodules (required for go2rtc)
git submodule update --init --recursive

# Build image
docker build --pull -t lightnvr:local .

# Run locally built image
docker run -d \
  --name lightnvr \
  -p 8080:8080 \
  -v ./config:/etc/lightnvr \
  -v ./data:/var/lib/lightnvr/data \
  lightnvr:local

Support

For issues and questions: