I run Frigate NVR on a dedicated Proxmox LXC container (CT115) with 7 Reolink cameras, a Google Coral USB TPU for detection, and recordings going to my QNAP NAS over NFS. It has been rock solid since I set it up — until today, when I noticed something was off. The live view in the Frigate web interface was blank, and the most recent recorded footage was from 7 hours ago. No alerts, no errors, nothing. Frigate had silently died.
In this post I walk through how I diagnosed the root cause using Docker and system logs, and how I set up a proper watchdog with automatic recovery and a Home Assistant badge that appears the moment something goes wrong.
What Happened: A Silent Freeze
The first thing I did was check the Proxmox system journal on CT115. The journal showed nothing from Frigate between 15:48 and 20:49 — just hourly cron jobs firing normally. The container was running. Docker had not restarted it. But internally, the Frigate Python process had completely frozen.
The Docker logs told the real story. From 18:21 onward, the only entries were nginx health probe requests every 60 seconds — all returning 400 because there was no valid request body, just the internal watchdog ping. The Frigate application itself had stopped logging entirely. It was alive enough that Docker’s restart policy did not kick in, but dead inside.
When I triggered a manual restart at 20:47, the shutdown sequence revealed the problem clearly:
20:47:16 frigate.app INFO: Stopping...
20:49:16 Service Frigate exited with code 256 (by signal 9)
Signal 9 is SIGKILL — a forced kill. The s6 process supervisor sent SIGTERM at 20:47, waited the full 2-minute grace period, and then had to forcibly terminate the process because it never responded. The Frigate Python process was completely locked and could not even handle a shutdown signal.
I also checked the Proxmox kernel log for NFS errors around 15:48 — nothing. No mount timeouts, no stale handles, no QNAP hiccups. The most likely explanation is an internal Python deadlock inside Frigate after extended uptime — the container had accumulated over two weeks of CPU time before this happened.
The Problem with Docker’s Built-in Restart Policy
My Frigate container runs with restart: unless-stopped. That works fine when the container actually exits — Docker notices and restarts it within seconds. However, a frozen internal process is invisible to Docker. The container stays “running” from Docker’s perspective, so no restart is triggered. You can be blind for hours without knowing it.
The fix is an external watchdog that actively checks whether Frigate is actually responding, not just whether the container process exists.
Setting Up the Watchdog
I went with a simple cron-based approach on CT115 itself. Every 5 minutes, it hits the Frigate API. If there is no response within 5 seconds, it restarts the container and logs the event.
Step 1 — Create the watchdog script on CT115
cat > /usr/local/bin/frigate-watchdog.sh << 'EOF'
#!/bin/bash
if ! curl -sf --max-time 5 http://localhost:5000/api/version > /dev/null 2>&1; then
echo "$(date '+%Y-%m-%d %H:%M:%S'): Frigate unresponsive - restarting container" >> /var/log/frigate-watchdog.log
docker restart frigate >> /var/log/frigate-watchdog.log 2>&1
echo "$(date '+%Y-%m-%d %H:%M:%S'): Restart complete" >> /var/log/frigate-watchdog.log
fi
EOF
chmod +x /usr/local/bin/frigate-watchdog.sh
Step 2 — Add it to crontab
(crontab -l 2>/dev/null; echo '*/5 * * * * /usr/local/bin/frigate-watchdog.sh') | crontab -
With this in place, a frozen Frigate process gets detected and restarted within 5 minutes instead of going unnoticed for hours.
Surfacing It in Home Assistant
I wanted visibility without noise — no notifications, just a badge that appears in my HA header when a watchdog restart has occurred in the last 24 hours. Invisible during normal operation, immediately obvious when something went wrong.
Step 1 — Create a JSON status script on CT115
#!/bin/bash
LOG=/var/log/frigate-watchdog.log
if [ ! -f "$LOG" ]; then
echo '{"restarts_24h": 0, "last_restart": "never", "total_restarts": 0}'
exit 0
fi
SINCE=$(date -d "24 hours ago" "+%Y-%m-%d %H:%M:%S")
RESTARTS_24H=$(grep "restarting container" "$LOG" | awk -v since="$SINCE" '$0 >= since' | wc -l)
TOTAL=$(grep -c "restarting container" "$LOG" || echo 0)
LAST=$(grep "restarting container" "$LOG" | tail -1 | awk '{print $1, $2}')
[ -z "$LAST" ] && LAST="never"
echo "{"restarts_24h": $RESTARTS_24H, "last_restart": "$LAST", "total_restarts": $TOTAL}"
Step 2 — Reuse the existing HA SSH key
I reused the same SSH key that already authenticates HA to my QNAP (qnap_hass). I added its public key to /root/.ssh/authorized_keys on CT115, then verified it worked from the HA terminal:
ssh -i /config/.ssh/qnap_hass -o StrictHostKeyChecking=no root@192.168.1.15 '/usr/local/bin/frigate-watchdog-status.sh'
Step 3 — command_line sensor in configuration.yaml
command_line:
- sensor:
name: "Frigate Watchdog"
unique_id: sensor_frigate_watchdog
command: "ssh -i /config/.ssh/qnap_hass -o StrictHostKeyChecking=no root@192.168.1.15 '/usr/local/bin/frigate-watchdog-status.sh'"
scan_interval: 300
value_template: "{{ value_json.restarts_24h }}"
unit_of_measurement: "restarts"
json_attributes:
- last_restart
- total_restarts
Step 4 — Template sensor
template:
- sensor:
- name: "Frigate Watchdog Status"
unique_id: sensor_frigate_watchdog_status
state: >
{%- set restarts = states('sensor.frigate_watchdog') | int(0) -%}
{%- if restarts > 0 -%}restarted{%- else -%}ok{%- endif -%}
icon: >
{%- if states('sensor.frigate_watchdog') | int(0) > 0 -%}
mdi:cctv-off
{%- else -%}
mdi:cctv
{%- endif -%}
attributes:
last_restart: "{{ state_attr('sensor.frigate_watchdog', 'last_restart') }}"
total_restarts: "{{ state_attr('sensor.frigate_watchdog', 'total_restarts') }}"
Step 5 — Header badge
type: entity
entity: sensor.frigate_watchdog
name: Frigate
icon: mdi:cctv-off
visibility:
- condition: numeric_state
entity: sensor.frigate_watchdog
above: 0
The Result
The badge sits silently in my header at zero restarts. If Frigate ever freezes again, the watchdog detects it within 5 minutes, restarts the container, writes a timestamped entry to the log, and the badge appears in HA showing how many times it has happened in the last 24 hours — along with the exact timestamp of the last restart as an attribute.
No more discovering a 7-hour recording gap by accident.
