Local surveillance vs. proprietary cloud: why self-host your NVR
SaaS surveillance solutions — Reolink Cloud, Synology Surveillance Station connected to proprietary relays, Amazon Ring — record your streams on third-party servers. In exchange for a monthly subscription, you get a mobile app, remote storage and event detection. What you give up is control over what happens in front of your cameras.
Self-hosting your NVR on a VPS reverses the logic: your streams never leave your infrastructure, object detection (people, vehicles, animals) runs locally without sending any image to a third-party service, and recordings are stored on volumes you control. No subscription gates this functionality.
Frigate NVR is the open-source reference in this category. It receives RTSP or RTMP streams from your IP cameras, cuts clips on detected events, retains snapshots and exposes a REST API consumable by Home Assistant or any automation. Version 0.18, released in September 2026, replaces the mandatory config.yml file with a full graphical interface: configure cameras, detection zones and thresholds from a browser, without touching YAML if you prefer not to.
Concrete benefits of self-hosted Frigate on a VPS
- Video streams and recordings 100% local: no third party has access to your footage.
- AI object detection (people, vehicles, animals) running on your own CPU, with no API quota or limitation.
- Zero recurring subscription: you pay for your VPS, not a detection licence.
- Native integration with Home Assistant via MQTT and the Frigate REST API.
- Event clips, snapshots and timeline viewable from the Frigate web interface.
- Compatible with virtually all IP cameras on the market via the standard RTSP protocol.
- Per-camera configurable detection zones to filter false alerts (street, neighbour, tree).
- Full GUI configuration since v0.18 — YAML remains possible but is no longer required.
Sizing requirements: dimensioning your VPS for Frigate
Frigate is a CPU-intensive application in software-only mode. Object detection relies on a TensorFlow Lite model that runs on each analysed frame. The figures below apply to CPU-only mode, the only option available on a standard VPS.
For 1 to 2 cameras at 1080p: 2 vCPU and 2 GB RAM is the functional minimum. Detection will run, but with a noticeable delay on events. Recommended analysis rate: 5 frames per second maximum.
For 3 to 5 cameras at 1080p: 4 vCPU and 4 GB RAM provides a comfortable margin. Frigate can sustain 5 fps analysis per camera without saturation. The VPS Power plan (4 vCPU / 8 GB GB RAM) is a realistic baseline for this use case.
For more than 5 cameras or high analysis rates: 6 to 8 vCPU and 8 GB RAM or more. Hardware acceleration via a Coral TPU USB or GPU reduces CPU load drastically, but is not available on standard hosted VPS.
Storage: each 1080p camera generates on average 1 to 3 GB per hour in continuous recording mode, depending on H.264 or H.265 compression. For 24 hours per camera in event mode (Frigate only retains activity clips), budget 5 to 15 GB per camera per day depending on movement frequency. Plan for an extensible volume or mounted object storage.
Network: each incoming RTSP stream consumes between 1 and 4 Mbit/s depending on resolution and codec. A 1080p H.264 camera typically runs between 1.5 and 2.5 Mbit/s. Check the bandwidth included in your plan.
Required software: Docker Engine 24+ and Docker Compose v2, root access to the VPS, a DNS subdomain pointing to the server IP for HTTPS access, and a reverse proxy (Nginx Proxy Manager, Caddy or Traefik) for TLS termination.
Deploying Frigate NVR with Docker Compose
Create the configuration directory structure
Connect to your VPS via SSH and create the directory structure that Frigate will use for its configuration, recordings and snapshots:
mkdir -p /opt/frigate/{config,storage/recordings,storage/clips,storage/exports} cd /opt/frigateFrigate expects its configuration file at
/opt/frigate/config/config.yml. With v0.18, this file can remain minimal at startup — the GUI completes the configuration afterwards.Create a minimal startup configuration
Create
/opt/frigate/config/config.ymlwith the following content. This file tells Frigate where to store data; cameras and detection settings will be added via the graphical interface:mqtt: enabled: false record: enabled: true retain: days: 7 mode: motion snapshots: enabled: true retain: default: 10 detectors: cpu1: type: cpu num_threads: 3The
detectorskey declares a software detector on CPU.num_threadscontrols parallelisation: 3 threads is a good balance for 2 to 4 cameras. Increase it if you have more vCPUs available.Write the Docker Compose file
Create
/opt/frigate/docker-compose.ymlwith the following configuration:services: frigate: container_name: frigate image: ghcr.io/blakeblackshear/frigate:stable restart: unless-stopped shm_size: "256mb" volumes: - /opt/frigate/config:/config - /opt/frigate/storage:/media/frigate - /etc/localtime:/etc/localtime:ro ports: - "5000:5000" - "8554:8554" - "8555:8555/tcp" - "8555:8555/udp" environment: FRIGATE_RTSP_PASSWORD: "changeme" devices: - /dev/dri:/dev/driExposed ports: 5000 for the web interface and REST API, 8554 for the internal RTSP server (restreaming processed streams), 8555 TCP and UDP for WebRTC (real-time video playback in the browser). The
devicesblock enables GPU acceleration on VPS instances that expose it — remove it if iGPU is not available.Start Frigate and check the logs
From the
/opt/frigatedirectory, launch the container and monitor the startup logs:docker compose up -d docker compose logs -f frigateOn first start, Frigate downloads the TensorFlow Lite detection model (~6 MB) and initialises its SQLite database. The operation takes 30 to 90 seconds. The log should show
Frigate is runningand the resolution of each declared stream. If a camera does not open, the lineFailed to connect to camerawill identify the problematic stream.Access the interface at
http://<your-vps-ip>:5000for initial verification (HTTPS will be configured in the next step).Configure a reverse proxy for HTTPS access
Frigate does not handle TLS natively. Configure Nginx as a reverse proxy. If Nginx is already installed on the VPS, create
/etc/nginx/sites-available/frigate:server { listen 443 ssl; server_name frigate.yourdomain.com; ssl_certificate /etc/letsencrypt/live/frigate.yourdomain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/frigate.yourdomain.com/privkey.pem; location / { proxy_pass http://127.0.0.1:5000; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_read_timeout 86400; } }Obtain a Let's Encrypt certificate with
certbot --nginx -d frigate.yourdomain.com. Theproxy_read_timeout 86400directive is required for long-lived WebRTC streams.Add your cameras via the Frigate v0.18 GUI
Open the Frigate interface in your browser. Version 0.18 provides a camera addition wizard accessible from Configuration → Cameras → Add Camera. Fill in:
- Name of the camera (no spaces or special characters).
- RTSP URL of the main stream, for examplertsp://admin:[email protected]:554/stream1.
- Resolution detected automatically or specified manually.
- Detection profile: *Standard* for a balance between accuracy and CPU load, *Lightweight* for lower-spec VPS instances.The GUI generates and validates the corresponding YAML block before applying it to the configuration — you can always switch to raw YAML mode via the Advanced tab if you prefer to edit directly.
Verify that recordings and detection are working
From the Frigate dashboard, navigate to Events to confirm that detection events are being generated. Check the Recordings tab to validate that mp4 files are being written to
/opt/frigate/storage/recordings/.On the VPS, monitor CPU load in real time:
docker stats frigateFrigate will display container CPU usage. In CPU-only mode with 2 x 1080p cameras at 5 fps analysis, expect 60 to 90% of one vCPU. Adjust the
fpsparameter in each camera configuration if load is too high:detect.fps: 2reduces analysis frequency without disabling detection.
Configuring cameras and detection zones via the GUI
The central new feature of Frigate v0.18 is its visual detection zone editor. Until v0.14, defining a zone required manually calculating polygon coordinates in the YAML file — a tedious task that discouraged deployments.
Since v0.18, the Zones tab for each camera displays the live image and lets you draw polygons directly with your mouse. Each zone receives a name (e.g. entrance, garden, street) and object filters — you can specify that the street zone should ignore people but alert on vehicles, or that the entrance zone should trigger a notification whenever a person enters the perimeter.
Motion masks: the same interface lets you draw masks over zones generating too many false positives — a tree swaying in the wind, a flickering light. The mask prevents Frigate from analysing those pixels for detection, without affecting recording.
Confidence thresholds: each detectable object (person, car, dog, cat, bird…) has an adjustable confidence threshold between 0 and 1. The default of 0.5 is reasonable; lowering to 0.35 increases sensitivity at the cost of more false alerts.
Notification configuration: Frigate integrates with Home Assistant via MQTT or its REST API. Without Home Assistant, you can send alerts to any webhook via notify.webhook or use event clips directly from the interface.
Hardening access to the Frigate web interface
The Frigate interface does not include an authentication system in its stable version. Exposed directly to the internet, it is accessible without a password. Three complementary measures to apply before making the subdomain public:
HTTP Basic authentication via Nginx: add auth_basic and auth_basic_user_file to the location block of the Nginx virtual host. Generate the password file with htpasswd -c /etc/nginx/.htpasswd your_user.
IP restriction: if you access Frigate from a fixed IP (office, VPN), add allow 203.0.113.x; deny all; in the Nginx location block to block all external access.
VPN-only access: the most robust solution. Deploy WireGuard on the same VPS (or a dedicated VPS) and make port 5000 accessible only locally or via the VPN tunnel. The interface is then never directly exposed to the internet, and no reverse proxy is needed to protect it.
Troubleshooting: common errors
Failed to connect to camera: Connection refused or timeout on startup
Frigate cannot reach the RTSP stream. First verify the RTSP URL is correct by testing it with ffprobe from the VPS: ffprobe -v quiet -print_format json -show_streams rtsp://.... If the camera is on a private network separate from the VPS, it is not directly reachable — you need a tunnel or RTSP relay.
ffmpeg process crashed looping in logs
Most likely: unsupported codec (H.265 / HEVC on a Docker installation without optional libraries) or resolution too high for available bandwidth. Solution: force transcoding at input by adding input_args: preset-rtsp-restream to the camera configuration, or reduce camera resolution to 1280×720.
Insufficient shared memory at container startup
Frigate uses Linux shared memory to share frames between its processes. The error means shm_size is too low. General rule: 256 MB for 2 cameras, 512 MB for 4, 1 GB for 8+. Adjust the shm_size directive in docker-compose.yml accordingly.
Database is locked or random crashes
Frigate uses SQLite, which handles network volumes (NFS, CIFS) poorly. If you mount /opt/frigate/storage from network storage, move only the SQLite database to a local volume: db_path: /tmp/frigate.db in the configuration. Recordings can remain on the remote volume.
Detection does not trigger despite visible motion
First check that the detection zone covers the area of interest in the visual editor. Then consult the Debug tab for the camera in the GUI: it overlays detection rectangles in real time on the stream. If no rectangles appear, the model is running but finding nothing — the confidence threshold may be too high or objects too small in the camera's field of view.
Going further
Frigate NVR turns a standard VPS into a sovereign surveillance station. Object detection runs locally, your recordings stay on your volumes, and v0.18 makes the installation accessible without YAML expertise.
If you are extending your self-hosted stack, the articles below cover complementary services that integrate well with Frigate from a centralised dashboard: Jellyfin for your media library, Pi-hole for DNS filtering and Homepage as a dashboard for all your services. un VPS Power covers the majority of 3 to 5 camera CPU-only configurations with headroom for other stack containers.