Skip to content

Frigate NVR

Status: Production Namespace: frigate URL: https://frigate.skaggsfamily.us Deployed: January 2026

Overview

Frigate is a network video recorder (NVR) with real-time AI object detection using Google Coral TPU hardware acceleration. The deployment uses NFS storage for recordings and clips, with a file-based configuration approach that allows UI-driven configuration changes (like motion masks) to persist across restarts.

Architecture

Core Components

Component Version Purpose Storage Backend
Frigate 0.15.1 NVR with AI detection Hybrid (Ceph + NFS)
Coral TPU PCIe Accelerator Object detection inference N/A

Infrastructure Dependencies

  • Storage: Hybrid architecture (Ceph for config, NFS for recordings)
  • Ingress: Traefik IngressRoute with TLS
  • DNS: External-DNS for automatic DNS management
  • Certificate: Let's Encrypt TLS certificate via cert-manager
  • Hardware: Google Coral TPU (PCIe) on dedicated worker node

Node Scheduling

Frigate requires access to the Coral TPU hardware, which is passed through to a specific worker node. The deployment uses a node selector to ensure scheduling on the correct node:

nodeSelector:
  feature.node.kubernetes.io/coral-tpu: "true"

The Coral TPU device (/dev/apex_0) is mounted into the container with privileged security context to allow direct hardware access.

Storage Architecture

Design Principle

Configuration lives on Ceph for performance and reliability. Recordings live on NFS for capacity.

Volume Layout

Volume Size Storage Class Access Mode Purpose
frigate-config 5Gi ceph-block RWO Configuration, database, model files
frigate-media 500Gi nfs-storage-network RWX Recordings, clips, snapshots

Configuration Storage Strategy

Frigate's configuration is stored as a writable file (config.yml) in the config PVC rather than a Kubernetes ConfigMap. This allows:

  • UI-driven configuration changes (motion masks, zones, detection settings)
  • Changes persist across pod restarts
  • No GitOps commit required for operational tweaks

Configuration Approach

File-Based Configuration

The Frigate Helm chart normally creates a ConfigMap from inline values and mounts it at /config/config.yml. This makes the configuration read-only, preventing UI-based changes from persisting.

To enable writable configuration, the deployment uses Flux postRenderers to remove the ConfigMap volume mount after Helm renders the deployment:

# helmrelease.yaml
postRenderers:
  - kustomize:
      patches:
        - target:
            kind: Deployment
            name: frigate
          patch: |-
            - op: test
              path: /spec/template/spec/volumes/0/name
              value: configmap
            - op: remove
              path: /spec/template/spec/volumes/0
            - op: test
              path: /spec/template/spec/containers/0/volumeMounts/1/name
              value: configmap
            - op: remove
              path: /spec/template/spec/containers/0/volumeMounts/1

Why JSON Patch Operations?

  • The test operation ensures the patch targets the correct volume (safety check)
  • The remove operation deletes the ConfigMap volume and volumeMount
  • This allows the PVC-mounted /config directory to contain the authoritative config.yml

Helm Chart Settings

persistence:
  config:
    enabled: true
    existingClaim: frigate-config
    # Don't copy ConfigMap to volume on startup
    ephemeralWritableConfigYaml: false

The ephemeralWritableConfigYaml: false setting prevents the chart from copying any ConfigMap content to the PVC at startup, ensuring the PVC's config.yml is the single source of truth.

Deployment

Source Files

Location: apps/_bases/frigate/

Key files:

  • helmrelease.yaml - Main Frigate deployment with postRenderers
  • ingressroute.yaml - Traefik routing for web UI
  • dnsendpoint.yaml - DNS record for frigate.skaggsfamily.us
  • pvc.yaml - Config and media persistent volume claims
  • sealedsecret-camera-credentials.yaml - Camera RTSP credentials
  • sealedsecret-frigate-env.yaml - Environment variables
  • backup/ - VolSync backup configuration

Helm Chart

  • Repository: https://blakeblackshear.github.io/blakeshome-charts
  • Chart: frigate
  • Version: 7.5.1
  • Application Version: 0.15.1

Dependencies

  • infra-sources-helm - Helm repository
  • infra-namespaces - frigate namespace
  • infra-sealed-secrets - Secret decryption
  • infra-traefik - Ingress controller
  • infra-cert-manager - TLS certificates

Resource Allocation

resources:
  requests:
    cpu: 500m
    memory: 1Gi
  limits:
    cpu: 2000m
    memory: 4Gi

Rationale:

  • Object detection is CPU-intensive for decoding video streams
  • Memory needed for detection model and frame buffers
  • Coral TPU offloads inference, reducing CPU load significantly

Operations

Accessing the Service

# Open web UI
open https://frigate.skaggsfamily.us

# Direct pod access (debugging)
kubectl port-forward -n frigate svc/frigate 5000:5000

Monitoring

# Check pod status
kubectl get pods -n frigate

# View logs
kubectl logs -n frigate -l app.kubernetes.io/name=frigate --tail=100

# Check HelmRelease status
kubectl get helmrelease -n frigate

# Verify Coral TPU is detected
kubectl logs -n frigate -l app.kubernetes.io/name=frigate | grep -i coral

Editing Configuration

Configuration changes can be made through:

  1. Frigate Web UI (recommended for operational changes)
  2. Navigate to Settings in the web UI
  3. Make changes (motion masks, zones, detection settings)
  4. Click "Restart Frigate" when prompted
  5. Changes persist in the config PVC

  6. Direct File Edit (for major changes)

    # Edit config.yml directly in the PVC
    kubectl exec -n frigate deploy/frigate -- vi /config/config.yml
    
    # Restart to apply
    kubectl rollout restart -n frigate deploy/frigate
    

Setting Up Motion Masks

Motion masks prevent false detections in areas with constant motion (trees, reflections, etc.):

  1. Open Frigate web UI
  2. Navigate to the camera you want to configure
  3. Click "Debug" → "Motion Mask"
  4. Draw mask zones on the video feed
  5. Click "Save"
  6. Click "Restart Frigate" when prompted

The mask configuration is stored in /config/config.yml and persists across restarts.

Backup and Recovery

Backup Configuration

Config Volume (VolSync):

  • Method: Restic to MinIO S3
  • Schedule: Daily at 3:00 AM
  • Retention: 7 daily, 4 weekly, 6 monthly
  • Destination: s3://volsync-backups/frigate-config
apiVersion: volsync.backube/v1alpha1
kind: ReplicationSource
metadata:
  name: frigate-config-backup
  namespace: frigate
spec:
  sourcePVC: frigate-config
  trigger:
    schedule: "0 3 * * *"
  restic:
    repository: frigate-restic-secret
    copyMethod: Direct
    retain:
      daily: 7
      weekly: 4
      monthly: 6
    pruneIntervalDays: 7

Backup Verification

# Check VolSync backup status
kubectl get replicationsource -n frigate

# View last backup details
kubectl describe replicationsource frigate-config-backup -n frigate

# Check backup job logs
kubectl logs -n frigate -l volsync.backube/replicationSource=frigate-config-backup

Recovery Procedures

Config Volume Recovery:

  1. Create a ReplicationDestination to restore from backup
  2. Point the PVC at the restored data
  3. See VolSync documentation for detailed restore procedures

Recordings Recovery:

  • Media volume is not backed up (high volume, low value)
  • Recordings can be regenerated from camera feeds
  • Consider S3 lifecycle policies for long-term clip retention

Troubleshooting

Config Not Persisting

Symptom: Motion masks or other UI changes disappear after restart

Diagnosis:

# Check if config is read-only
kubectl logs -n frigate -l app.kubernetes.io/name=frigate | grep -i "read-only"

# Verify postRenderers removed ConfigMap mount
kubectl get deploy frigate -n frigate -o yaml | grep -A5 "volumeMounts"

Resolution: Ensure the HelmRelease has the correct postRenderers to remove the ConfigMap volume mount. The config PVC must be the only source for /config/config.yml.

Coral TPU Not Detected

Symptom: Detection running on CPU, high CPU usage

Diagnosis:

# Check device is mounted
kubectl exec -n frigate deploy/frigate -- ls -la /dev/apex_0

# Check Frigate logs for Coral
kubectl logs -n frigate -l app.kubernetes.io/name=frigate | grep -i "edge tpu"

Resolution:

  • Verify node has feature.node.kubernetes.io/coral-tpu: "true" label
  • Check pod is scheduled on correct node
  • Verify privileged security context is enabled

Camera Connection Issues

Symptom: Camera feed not loading, "No frames received"

Diagnosis:

# Check camera connectivity from pod
kubectl exec -n frigate deploy/frigate -- \
  ffprobe -v quiet -print_format json -show_streams rtsp://user:pass@camera-ip/stream

# Check Frigate logs for camera errors
kubectl logs -n frigate -l app.kubernetes.io/name=frigate | grep -i camera-name

Resolution:

  • Verify camera RTSP URL is correct
  • Check network connectivity from cluster to camera VLAN
  • Verify credentials in sealed secret

High Memory Usage

Symptom: Pod OOMKilled or memory pressure

Diagnosis:

# Check current memory usage
kubectl top pod -n frigate

# Check for memory-related events
kubectl describe pod -n frigate -l app.kubernetes.io/name=frigate | grep -A5 "Events"

Resolution:

  • Reduce number of cameras or detection resolution
  • Adjust detect settings in config (fps, resolution)
  • Increase memory limits if resources available

Hardware Configuration

Coral TPU Passthrough

The Coral TPU PCIe card is passed through from the Proxmox host to the Kubernetes worker node:

  1. Proxmox Host: Coral PCIe device added to VM
  2. Talos Node: Device appears as /dev/apex_0
  3. Frigate Pod: Device mounted with privileged access

Helm Values:

securityContext:
  privileged: true

coral:
  enabled: true
  hostPath: /dev/apex_0

Camera Requirements

  • RTSP stream support (H.264 recommended)
  • Dedicated substream for detection (lower resolution, 5-10 fps)
  • Main stream for recording (full resolution)

References


Last Updated: January 6, 2026