Deploy Home Surveillance System

Deploy Home Surveillance System

When I ditched cloud-based camera subscriptions, I realized how much I was overpaying for basic motion detection and video storage. A self-hosted surveillance system running on a modest homelab setup costs pennies compared to recurring SaaS fees—and you own everything. I'll walk you through setting up Frigate with IP cameras, local storage, and a dashboard you actually control.

Why Self-Hosted Surveillance Makes Sense

Cloud surveillance ecosystems lock you into monthly subscriptions: $10 per camera, $50 for NVR features, $20 for AI detection. After a year with 4 cameras, you've spent $600. Self-hosting costs you the hardware once, then nothing. I prefer Frigate because it's open-source, uses real object detection (YOLO), and runs on commodity hardware—even a Raspberry Pi 4 with an SSD handles 4–6 cameras smoothly.

The real advantages are ownership, privacy, and flexibility. Your footage never touches the internet unless you choose. You can replay events at 3 AM without paying extra. You integrate with Home Assistant, trigger automations, and tinker endlessly.

Hardware and Network Planning

You'll need three layers: cameras, a local NVR/compute box, and storage. I run this stack on a small tower with a Ryzen 5 and 8GB RAM, but I've also tested it on a Beelink SER5 mini PC ($200). For cameras, I use a mix of budget PoE (Power over Ethernet) turrets from Dahua and Hikvision clones—around $40–80 per unit from AliExpress.

Essential hardware:

Before I deployed this, I tested camera compatibility in my network lab. Not every cheap IP camera firmware cooperates with RTSP streams. I recommend buying one camera first, confirm it works with Frigate in your environment, then scale.

Tip: Buy cameras with ONVIF support—it's a standard that guarantees compatibility with Frigate and other NVR software. Check the specs before purchasing from overseas sellers.

Setting Up Frigate with Docker Compose

Frigate is the brain of this system. It ingests RTSP streams from cameras, runs object detection, records events, and serves a web UI. I deploy it via Docker Compose because it isolates dependencies and makes updates trivial.

First, ensure your NVR box runs Ubuntu 22.04 LTS and has Docker + Docker Compose installed. Then create this configuration:

version: "3.8"
services:
  frigate:
    image: ghcr.io/blakeblackshear/frigate:stable
    container_name: frigate
    hostname: frigate
    restart: unless-stopped
    privileged: true
    shm_size: "256mb"
    devices:
      - /dev/dri/renderD128
    ports:
      - "5000:5000"
    environment:
      FRIGATE_RTSP_PASSWORD: changeme123
      PLUS_API_KEY: ""
    volumes:
      - ./config:/config
      - ./storage:/media/frigate
      - /etc/localtime:/etc/localtime:ro
      - /tmp/cache:/tmp/cache
    networks:
      - surveillance

  # Optional: Nginx reverse proxy for HTTPS
  nginx:
    image: nginx:latest
    container_name: frigate-nginx
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - ./certs:/etc/nginx/certs:ro
    depends_on:
      - frigate
    networks:
      - surveillance

networks:
  surveillance:
    driver: bridge

Create the directory structure and config file:

mkdir -p frigate/{config,storage,certs}
cd frigate

# Create a minimal config.yml
cat > config/config.yml << 'EOF'
logger:
  default: info

mqtt:
  enabled: false

database:
  path: /config/frigate.db

detectors:
  cpu:
    type: cpu

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

cameras:
  front_door:
    ffmpeg:
      inputs:
        - path: rtsp://username:[email protected]:554/stream1
          roles:
            - detect
            - record
    detect:
      width: 1280
      height: 720
    record:
      retain:
        days: 7
        mode: motion
    snapshots:
      retain:
        default: 10
        objects: 30

  garage:
    ffmpeg:
      inputs:
        - path: rtsp://username:[email protected]:554/stream1
          roles:
            - detect
            - record
    detect:
      width: 1280
      height: 720
    record:
      retain:
        days: 7
        mode: motion
    snapshots:
      retain:
        default: 10
        objects: 30

# Coral EdgeTPU GPU acceleration (optional)
# detectors:
#   coral:
#     type: edgetpu
#     device: usb
EOF

docker compose up -d

After 30 seconds, Frigate will start. Visit http://192.168.x.x:5000 from your network, and you'll see the dashboard with live feeds and event history. The first time is magical—you realize your cameras are actually working.

Watch out: RTSP credentials in plaintext in config.yml is a security risk if you expose Frigate to the internet. Always put it behind a reverse proxy with authentication (Caddy, Authelia, or Traefik). Never expose port 5000 directly to the WAN.

Fine-Tuning Detection and Storage

Out of the box, Frigate's CPU detector is accurate but slow—it processes every frame at reduced resolution. For 4 cameras at 1280×720, CPU detection introduces lag. I upgraded to a Coral USB EdgeTPU accelerator ($50), which runs YOLO inference at 100 FPS. Detection latency dropped from 800ms to 50ms.

Storage is your next bottleneck. Motion-only recording (my config default) saves 80% disk space compared to continuous recording. A 4TB HDD with motion-only retention retains 30+ days of activity. Adjust the retain.days and retain.mode values in config.yml based on your paranoia level.

I also recommend setting up a separate mount point for Frigate storage on a dedicated drive. This prevents your OS disk from filling up:

sudo fdisk -l
# Identify your storage device (e.g., /dev/sdb)

sudo mkfs.ext4 /dev/sdb1
sudo mkdir -p /mnt/frigate-storage
sudo mount /dev/sdb1 /mnt/frigate-storage
sudo chown $USER:$USER /mnt/frigate-storage

# Add to /etc/fstab for persistence
echo '/dev/sdb1 /mnt/frigate-storage ext4 defaults 0 2' | sudo tee -a /etc/fstab

Then update your Docker volumes to ./storage:/mnt/frigate-storage.

Securing and Exposing Frigate Remotely

If you want to check cameras from your phone while away, you need secure remote access. I use Caddy as a reverse proxy with Authelia for authentication. Both run in Docker on the same NVR box.

Create a Caddy config:

frigate.example.com {
  reverse_proxy localhost:5000
  reverse_proxy /api/* localhost:7814  # Authelia
  
  # Simple basic auth (replace with Authelia for OAuth2)
  basicauth /* {
    admin $2a$14$...hash...
  }
}

Or use Authelia for passwordless OIDC login. Either way, never expose Frigate raw. Bots probe open ports constantly, and default credentials are worthless on the public internet.

For remote access without exposing your home IP, use Tailscale. I run Tailscale on the NVR box, generate an auth token, and access Frigate via the private Tailscale IP from anywhere. This is my preferred method—zero port forwarding, encryption by default, and no DynDNS headaches.

Integrations with Home Assistant

If you run Home Assistant (and honestly, why wouldn't you?), Frigate integrates natively. Add this to your configuration.yaml:

frigate:
  client_id: frigate
  server_url: http://192.168.1.50:5000  # Your NVR box IP

Restart Home Assistant, and Frigate camera feeds, events, and clips appear in your automations. I trigger notifications when Frigate detects a person at the front door, turn on porch lights, and save a snapshot. This turns surveillance from passive recording into active security automation.

Budget Reality Check

A functional 4-camera self-hosted surveillance system costs roughly:

Versus cloud subscriptions at $40–50/month per camera ($1,920–2,400/year), you break even in 6–12 months and own the hardware indefinitely. If you're open to used or refurbished gear—which I recommend for homelabs—costs drop further. I found my NVR box on eBay for $180.

For reliable VPS backup storage (archive 30 days of footage to the cloud as an off-site redundancy), consider RackNerd VPS, which offers affordable storage starting at ~$15/month. A small KVM VPS with attached storage handles archival backups without breaking the bank.

Maintenance and Next Steps

Frigate requires minimal ongoing maintenance. Update the Docker image monthly, monitor disk usage, and occasionally review detection thresholds if you're getting false positives (raccoons triggering person alerts). I run a cron job that deletes footage older than 30 days automatically.

From here, you can add features: MQTT integration for smarter automations, license plate recognition (ALPR) via CodeProject.AI, or deep learning models that distinguish between actual threats and shadows. The open ecosystem encourages experimentation.

The biggest win? You've eliminated recurring fees and reclaimed privacy. Your footage stays home, your automations run locally, and your homelab just became genuinely useful.

Discussion

```