Docker Deployment Guide

March 11, 2026 · View on GitHub

This guide provides comprehensive information about deploying LightNVR using Docker.

Table of Contents

Quick Start

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

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

# Start the container (first run will build the image and pull the latest base layers)
docker compose up -d

# View logs
docker compose logs -f

# Access the web UI
# http://localhost:8080
# Default username: admin (password is auto-generated on first run - check logs)

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 stored in /var/lib/lightnvr/web and are automatically copied from /usr/share/lightnvr/web-template/ on first run. This ensures:

  • Web UI works immediately after container start
  • Updates to the container image update the web UI
  • Web assets are not lost when mounting data volumes

Network Configuration

Port Mapping

PortProtocolServiceDescription
8080TCPWeb UIMain web interface
8554TCPRTSPRTSP streaming server
8555TCP/UDPWebRTCWebRTC streaming
1984TCPgo2rtc APIgo2rtc 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

VariableDefaultDescription
TZUTCContainer timezone
GO2RTC_CONFIG_PERSISTtruePersist go2rtc config across restarts
LIGHTNVR_AUTO_INITtrueAuto-initialize config files on first run
LIGHTNVR_WEB_ROOT/var/lib/lightnvr/webWeb assets directory
LIGHTNVR_ONVIF_NETWORK(none)Override ONVIF discovery network (e.g., 192.168.1.0/24)

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/web/
    /var/lib/lightnvr/data/database/
    /var/lib/lightnvr/data/recordings/
    /var/lib/lightnvr/data/models/
    
  2. Copies Web Assets

    • Copies from /usr/share/lightnvr/web-template/ to /var/lib/lightnvr/web/
    • Only if web directory is empty
  3. Creates Default Configuration

    • lightnvr.ini with sensible defaults
    • go2rtc.yaml with WebRTC/STUN configuration
  4. Initializes Database

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

Default Credentials

  • Username: admin
  • Password: admin

⚠️ Change these immediately after first login!

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, see DOCKER_MIGRATION_GUIDE.md for detailed migration instructions.

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: