Configure Home Surveillance System

Configure Home Surveillance System

I spent months trying cloud-based security cameras before realizing I was paying recurring fees for a solution that sent my footage to a corporation's servers. Now I run Frigate—an open-source NVR (Network Video Recorder)—on my homelab, and I control everything: the footage stays encrypted at home, I own the storage, and I can set up intelligent detection without subscriptions. In this guide, I'll walk you through building a self-hosted surveillance system that rivals commercial alternatives at a fraction of the cost.

Why Self-Host Surveillance?

When I evaluated my options, the math was clear. Cloud services charge $10–30 per camera monthly. A cheap IP camera costs $30–80 upfront. After a year, you've paid more for the service than the hardware itself. Beyond cost, self-hosting gives you:

The tradeoff? You manage the system. But if you've built a homelab, you can handle it.

Hardware You'll Need

I recommend this baseline setup:

Total? Under $1,000 for a 4-camera system with local storage. A month of cloud subscriptions often cost more.

Install Frigate with Docker Compose

Frigate is the heart of the system. It pulls RTSP streams from your cameras, runs object detection (people, cars, dogs), and records to disk. I deploy it via Docker Compose because it's reproducible and easy to update.

First, set up Docker and Docker Compose on your NVR server (Ubuntu 22.04 LTS is my go-to). Then create this directory structure:

mkdir -p ~/frigate/{config,storage/clips,storage/snapshots}
cd ~/frigate

Now, create your docker-compose.yml:

version: '3.8'

services:
  frigate:
    image: ghcr.io/blakeblackshear/frigate:stable
    container_name: frigate
    privileged: true
    restart: unless-stopped
    shm_size: '256mb'
    environment:
      - FRIGATE_UID=1000
      - FRIGATE_GID=1000
    ports:
      - "5000:5000"
      - "8554:8554"
      - "8555:8555/udp"
    volumes:
      - ./config:/config
      - ./storage:/media/frigate
      - /etc/localtime:/etc/localtime:ro
    devices:
      - /dev/bus/usb:/dev/bus/usb  # For Coral TPU
    networks:
      - frigate

  frigate-db:
    image: postgres:15-alpine
    container_name: frigate-db
    environment:
      POSTGRES_DB: frigate
      POSTGRES_PASSWORD: frigatepw
      POSTGRES_USER: frigate
    volumes:
      - ./db:/var/lib/postgresql/data
    restart: unless-stopped
    networks:
      - frigate

networks:
  frigate:
    driver: bridge

Next, create the core Frigate configuration at config/config.yml. This is where you define cameras, detection zones, and retention:

# config/config.yml
logger:
  default: info
  logs:
    frigate.event: debug

database:
  host: frigate-db
  port: 5432
  dbname: frigate
  user: frigate
  password: frigatepw

mqtt:
  enabled: false

detectors:
  coral:
    type: edgetpu
    device: usb

objects:
  track:
    - person
    - car
    - dog
    - cat

cameras:
  front_door:
    ffmpeg:
      inputs:
        - path: rtsp://192.168.1.100:554/stream1
          roles:
            - detect
            - record
    detect:
      width: 1280
      height: 720
      fps: 5
    objects:
      filters:
        person:
          min_area: 1500
          max_area: 100000
          threshold: 0.7
    zones:
      driveway:
        coordinates: 0,0,400,300,500,720,0,720
    record:
      enabled: true
      retain:
        days: 7
        mode: motion
    snapshots:
      enabled: true
      retain:
        default: 10
        objects:
          person: 30

  backyard:
    ffmpeg:
      inputs:
        - path: rtsp://192.168.1.101:554/stream1
          roles:
            - detect
            - record
    detect:
      width: 1280
      height: 720
      fps: 5
    record:
      enabled: true
      retain:
        days: 14
        mode: all
    snapshots:
      enabled: true

snapshots:
  enabled: true
  timestamp: false
  bounding_box: true

telemetry:
  enabled: true
Tip: Replace 192.168.1.100 and 192.168.1.101 with your actual camera IPs. Find them by logging into your router or using nmap -p 554 192.168.1.0/24 to scan for RTSP ports.

Start the containers:

docker compose up -d

Check logs to confirm Frigate connects to your cameras:

docker compose logs -f frigate

You should see successful RTSP connections. If a camera fails, verify the stream URL by opening it in VLC: File → Open Network Stream → rtsp://192.168.1.100:554/stream1.

Access the Web UI and Configure Zones

Open your browser to http://192.168.1.X:5000 (replace X with your NVR's IP). The Frigate interface loads live feeds, event history, and system stats. You'll see each camera stream immediately.

The real power comes from configurable zones. In the config above, I defined a "driveway" zone for the front door—Frigate only triggers alerts when a person enters that area. This cuts false alerts from passing cars or distant activity. Adjust coordinates by editing the config and restarting:

docker compose restart frigate

Recording and Retention Strategy

I use two retention modes: continuous recording for less critical cameras (backyard gets 14 days), and motion-based for high-traffic areas (front door records only when motion detected, keeps 7 days). This balances storage and coverage.

Calculate storage needs: a 1280×720 H.264 stream uses roughly 500MB–1GB per hour. Four cameras recording continuously = 48GB per day. A 4TB drive holds ~83 days. With motion-only recording, you can stretch that to months.

For larger setups, I recommend a NAS (TrueNAS or Unraid) over your network. Point Frigate to an NFS mount:

mount -t nfs 192.168.1.50:/mnt/surveillance /mnt/surveillance

Then update docker-compose.yml to bind that mount.

Watch out: Surveillance drives write 24/7. Consumer drives (like WD Blue) fail faster under this load. Use WD Purple or equivalent—the small cost difference pays for itself in lifespan.

Mobile Access and Notifications

For remote access, I use Tailscale VPN. Install Tailscale on your NVR and any phone/laptop, and you're encrypted end-to-end:

curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up

Get your Tailscale IP, then access Frigate from anywhere via https://YOUR_TAILSCALE_IP:5000.

For push notifications (person detected, car detected), integrate Frigate with Home Assistant. Home Assistant runs on your homelab, subscribes to Frigate MQTT events, and sends notifications to your phone. Or use Frigate's native webhooks to trigger IFTTT applets.

Performance Tuning with Coral TPU

Without a Coral accelerator, Frigate's CPU detection runs at 2–3 fps per camera on modest hardware. With a Coral TPU USB device, it jumps to 15+ fps while using 10% of the CPU. I consider it essential for systems with 3+ cameras.

To enable it, add the USB device to your compose file (done above) and set the detector model in config:

detectors:
  coral:
    type: edgetpu
    device: usb
    num_threads: 3

Frigate automatically downloads the correct model. You can override it for specific cameras or object types if needed.

Backup Strategy

I back up the Frigate config to my Git server weekly and export key events (person/car detections) to a secondary NAS monthly. The database is small (<100MB), so I include it in backups. Raw video stays local—it's massive and re-recordable, so no need to backup.

Use rsync or a cron job:

0 2 * * * rsync -av ~/frigate/config /mnt/backup/frigate-config

Next Steps: Extend Your System

Once Frigate is running smoothly, consider:

Self-hosted surveillance takes work, but the payoff is privacy, control, and a system that gets better as you customize it. Once you've eliminated those monthly cloud subscriptions, you'll wonder why you didn't self-host sooner.

```