Compare commits
4 Commits
7addbfdc27
...
da015ea091
| Author | SHA1 | Date |
|---|---|---|
|
|
da015ea091 | 4 days ago |
|
|
825d861f82 | 4 days ago |
|
|
ee66d557de | 4 days ago |
|
|
6096e5d0ab | 4 days ago |
16 changed files with 397 additions and 9 deletions
@ -0,0 +1,48 @@ |
|||||
|
[Unit] |
||||
|
Description=Asterisk - HTTP server for the directory of the phones |
||||
|
Documentation=https://busybox.net/downloads/BusyBox.html |
||||
|
|
||||
|
Wants=asterisk-build.service |
||||
|
After=network.target asterisk-build.service var-lib-virtiofs-data.mount |
||||
|
Requires=var-lib-virtiofs-data.mount |
||||
|
|
||||
|
# Static files only, operator-provided on the precious-data mount, like the |
||||
|
# TFTP root and for the same reasons. A separate directory from the TFTP root: |
||||
|
# the configuration files of the phones hold their SIP passwords, and HTTP, |
||||
|
# unlike TFTP, is easily reached from another network. |
||||
|
|
||||
|
# Start/stop this unit when the target is started/stopped |
||||
|
PartOf=asterisk.target |
||||
|
|
||||
|
[Container] |
||||
|
ContainerName=asterisk-http |
||||
|
Image=localhost/asterisk:latest |
||||
|
AutoUpdate=local |
||||
|
|
||||
|
User=10036 |
||||
|
Group=10000 |
||||
|
|
||||
|
# HTTP listens on 80/tcp, a privileged port. |
||||
|
AddCapability=CAP_NET_BIND_SERVICE |
||||
|
|
||||
|
# Host networking, as the two other containers: the phones reach the same |
||||
|
# address for SIP, TFTP and HTTP. |
||||
|
Network=host |
||||
|
|
||||
|
# busybox httpd: -f stays in the foreground, -v logs each request. It reads |
||||
|
# /etc/httpd.conf of the image for the MIME type of XML files, and lists no |
||||
|
# directory. |
||||
|
Entrypoint=/usr/sbin/httpd |
||||
|
Exec=-f -v -p 80 -h /var/lib/virtiofs/data/asterisk/http |
||||
|
|
||||
|
# Read-only: the phones fetch, they never upload. |
||||
|
Volume=/var/lib/virtiofs/data/asterisk/http:/var/lib/virtiofs/data/asterisk/http:ro,z |
||||
|
|
||||
|
[Service] |
||||
|
Restart=always |
||||
|
RestartSec=10 |
||||
|
TimeoutStartSec=300 |
||||
|
TimeoutStopSec=30 |
||||
|
|
||||
|
[Install] |
||||
|
WantedBy=asterisk.target |
||||
@ -1,3 +1,7 @@ |
|||||
; Default transfer and pickup settings. The phones transfer with SIP REFER, |
; Default transfer and pickup settings. The phones transfer with SIP REFER, |
||||
; handled by res_pjsip_refer, not with DTMF feature codes. |
; handled by res_pjsip_refer, not with DTMF feature codes. |
||||
[general] |
[general] |
||||
|
; Dialled by a phone, picks up a call ringing on a phone of its pickup groups |
||||
|
; (named_pickup_group and named_call_group of pjsip.conf). Handled by chan_pjsip |
||||
|
; before the dialplan. *8 is the default, written here to be found. |
||||
|
pickupexten = *8 |
||||
|
|||||
@ -0,0 +1,10 @@ |
|||||
|
##
|
||||
|
## Makefile for Frigate quadlet
|
||||
|
##
|
||||
|
|
||||
|
# Frigate runs as root inside its container (s6-overlay, nginx, go2rtc), so its
|
||||
|
# files belong to root on the host. PROJECT_UID and PROJECT_GID keep their
|
||||
|
# default, 0.
|
||||
|
|
||||
|
# Include common Makefile
|
||||
|
include ../../scripts/common.mk |
||||
@ -0,0 +1,100 @@ |
|||||
|
# Podman Quadlet: Frigate |
||||
|
|
||||
|
## Overview |
||||
|
|
||||
|
[Frigate](https://frigate.video/) is a network video recorder with object detection for IP cameras. |
||||
|
This cookbook runs it as a Podman Quadlet. |
||||
|
|
||||
|
This cookbook: |
||||
|
|
||||
|
- Runs the upstream image `ghcr.io/blakeblackshear/frigate`, pinned to a full version (`0.18.2`). |
||||
|
The manifest is multi-architecture (amd64 + arm64). |
||||
|
- Runs Frigate as root inside its container, as the image requires (s6-overlay). |
||||
|
- Stores the configuration, the database and the recordings as **precious data** on the virtiofs |
||||
|
mount `/var/lib/virtiofs/data/frigate`, so that they inherit the hypervisor's ZFS snapshots, |
||||
|
quota and backups. |
||||
|
- Seeds the configuration file on first boot, then never touches it again: Frigate owns it, and its |
||||
|
web interface writes the cameras, the masks and the zones back to it. |
||||
|
- Binds the unauthenticated listeners of Frigate and go2rtc to the loopback. |
||||
|
- Detects objects with OpenVINO on the CPU, the default of a new Frigate 0.18 configuration. |
||||
|
- Uses host networking. |
||||
|
|
||||
|
It does not provide MQTT, a reverse proxy, nor any camera definition. |
||||
|
|
||||
|
## Prerequisites |
||||
|
|
||||
|
- The `base` cookbook (implicit dependency), for the virtiofs mount. |
||||
|
- A virtiofs mount on `/var/lib/virtiofs/data`, sized for the recordings. |
||||
|
- Network access at the first boot, to pull the image (about 2 GB on arm64). |
||||
|
- CPU and memory in proportion to the cameras. Frigate's own figures are for hardware detectors; |
||||
|
on a CPU, measure the inference time it reports under *System metrics*. |
||||
|
|
||||
|
## Files |
||||
|
|
||||
|
| Path | Content | |
||||
|
|------|---------| |
||||
|
| `/etc/quadlets/frigate/config.yml` | Seed of the configuration file. Shipped by the cookbook; an operator may replace it before the first boot. | |
||||
|
| `/etc/quadlets/frigate/init.sh` | Copies the seed when Frigate has no configuration file yet. | |
||||
|
| `/var/lib/virtiofs/data/frigate/config/` | Mounted on `/config`: `config.yml`, the SQLite database, the model cache, the backup of the configuration made before each migration. | |
||||
|
| `/var/lib/virtiofs/data/frigate/media/` | Mounted on `/media/frigate`: recordings, clips, snapshots, exports. | |
||||
|
|
||||
|
`frigate.service` only starts once `/var/lib/virtiofs/data/frigate/config/config.yml` exists. On a |
||||
|
fresh system, `frigate-init.service` creates it from the seed. Once it exists, the seed is never |
||||
|
read again: changing `/etc/quadlets/frigate/config.yml` has no effect on a running installation. |
||||
|
|
||||
|
## The seed |
||||
|
|
||||
|
The seed is the default configuration of Frigate 0.18, plus three restrictions: |
||||
|
|
||||
|
- `networking.listen.internal: 127.0.0.1:5000`. Port 5000 serves the UI and the API without |
||||
|
authentication. The health check and nginx reach it over the loopback; nothing else needs it. |
||||
|
- `go2rtc.api.listen: 127.0.0.1:1984`. The go2rtc API is not authenticated, and it accepts new |
||||
|
stream sources, `exec:` included. It listens on every address by default. |
||||
|
- `go2rtc.rtsp.listen: 127.0.0.1:8554`. The RTSP restream is not authenticated either. |
||||
|
|
||||
|
MQTT is disabled. `cameras` is empty: cameras are added from the web interface, or by editing |
||||
|
`config.yml` on the data mount. |
||||
|
|
||||
|
## Ports |
||||
|
|
||||
|
| Port | Bound to | Use | |
||||
|
|------|----------|-----| |
||||
|
| `8971/tcp` | all | Web interface and API, authenticated, over TLS with a self-signed certificate | |
||||
|
| `8555/tcp+udp` | all | WebRTC, for live views in a browser | |
||||
|
| `5000/tcp` | loopback | Web interface and API, **unauthenticated** | |
||||
|
| `1984/tcp` | loopback | go2rtc API, **unauthenticated** | |
||||
|
| `8554/tcp` | loopback | RTSP restream, unauthenticated | |
||||
|
|
||||
|
## First login |
||||
|
|
||||
|
Frigate creates an `admin` user at its first start and prints its password in the journal: |
||||
|
|
||||
|
```sh |
||||
|
journalctl -u frigate.service | grep -A1 -i password |
||||
|
``` |
||||
|
|
||||
|
Change it under *Settings > Users*. To print a new one, set `auth.reset_admin_password: true` in |
||||
|
`config.yml` and restart Frigate. |
||||
|
|
||||
|
## Upgrades |
||||
|
|
||||
|
The tag is pinned in `frigate.image` and `frigate.container`, and both must change together. A new |
||||
|
minor version migrates `config.yml` in place at its first start, after copying it to |
||||
|
`backup_config.yaml`. There is no migration back: going back to an older version means restoring |
||||
|
the data mount from a snapshot taken before the upgrade. |
||||
|
|
||||
|
## Usage |
||||
|
|
||||
|
```sh |
||||
|
sudo systemctl start frigate.target |
||||
|
sudo systemctl status frigate.service |
||||
|
sudo podman logs -f frigate |
||||
|
curl -k https://localhost:8971/ |
||||
|
``` |
||||
|
|
||||
|
## Shared memory |
||||
|
|
||||
|
A camera detecting at 720p takes about 35 MB of shared memory: 1280 × 720 × 1.5 bytes for each of |
||||
|
the 25 frames Frigate keeps. Logs and nginx cache take up to 50 MB more. `ShmSize=512m` thus covers |
||||
|
eight such cameras and two added while Frigate runs. A shared memory too small shows as `Bus error` |
||||
|
in the journal. The calculator in the upstream installation guide gives the size for other setups. |
||||
@ -0,0 +1,42 @@ |
|||||
|
# Seed of /var/lib/virtiofs/data/frigate/config/config.yml, copied once by |
||||
|
# frigate-init.service. Frigate owns the file afterwards: cameras, masks and |
||||
|
# zones are added from its web interface. An operator who needs other defaults |
||||
|
# provides /etc/quadlets/frigate/config.yml before the first boot. |
||||
|
|
||||
|
mqtt: |
||||
|
enabled: false |
||||
|
|
||||
|
# Port 5000 serves the UI and the API WITHOUT authentication. It is meant for |
||||
|
# the container itself (health check, nginx) and goes on the loopback. The |
||||
|
# authenticated UI stays on 8971, over TLS. |
||||
|
networking: |
||||
|
listen: |
||||
|
internal: 127.0.0.1:5000 |
||||
|
|
||||
|
# go2rtc listens on every address by default. Its API on 1984 is not |
||||
|
# authenticated and accepts new stream sources, "exec:" included; the RTSP |
||||
|
# restream on 8554 is not authenticated either. Frigate reaches both over the |
||||
|
# loopback. WebRTC (8555) stays open: the browsers connect to it directly. |
||||
|
go2rtc: |
||||
|
api: |
||||
|
listen: 127.0.0.1:1984 |
||||
|
rtsp: |
||||
|
listen: 127.0.0.1:8554 |
||||
|
|
||||
|
# The default of a new Frigate 0.18 configuration: OpenVINO on the CPU, with the |
||||
|
# SSDLite MobileNet v2 model shipped in the image (amd64 and arm64). |
||||
|
detectors: |
||||
|
ov: |
||||
|
type: openvino |
||||
|
device: CPU |
||||
|
model: |
||||
|
width: 300 |
||||
|
height: 300 |
||||
|
input_tensor: nhwc |
||||
|
input_pixel_format: bgr |
||||
|
path: /openvino-model/ssdlite_mobilenet_v2.xml |
||||
|
labelmap_path: /openvino-model/coco_91cl_bkgr.txt |
||||
|
|
||||
|
cameras: {} |
||||
|
|
||||
|
version: 0.18-0 |
||||
@ -0,0 +1,20 @@ |
|||||
|
#!/bin/sh |
||||
|
# |
||||
|
# Seed the Frigate configuration file on first boot. |
||||
|
# |
||||
|
# Frigate owns config.yml: the web interface writes the cameras, the masks and |
||||
|
# the zones back to it, and a new minor version migrates it in place. So this |
||||
|
# script only ever creates it when it is absent, and never touches it again. |
||||
|
# |
||||
|
set -eu |
||||
|
|
||||
|
SRC=/etc/quadlets/frigate/config.yml |
||||
|
DST=/var/lib/virtiofs/data/frigate/config/config.yml |
||||
|
|
||||
|
if [ -e "$DST" ]; then |
||||
|
echo "Keeping existing $DST (Frigate owns it)" |
||||
|
exit 0 |
||||
|
fi |
||||
|
|
||||
|
echo "Seeding $DST from $SRC" |
||||
|
install -m 0600 -o root -g root "$SRC" "$DST" |
||||
@ -0,0 +1,24 @@ |
|||||
|
[Unit] |
||||
|
Description=Frigate - Seed the configuration file on first boot |
||||
|
Documentation=https://docs.frigate.video/configuration/ |
||||
|
After=var-lib-virtiofs-data.mount systemd-tmpfiles-setup.service |
||||
|
Requires=var-lib-virtiofs-data.mount |
||||
|
|
||||
|
# Only run if there is a seed to copy |
||||
|
ConditionPathExists=/etc/quadlets/frigate/config.yml |
||||
|
|
||||
|
# Start/stop this unit when the target is started/stopped, before the service |
||||
|
PartOf=frigate.target |
||||
|
Before=frigate.service |
||||
|
|
||||
|
[Service] |
||||
|
Type=oneshot |
||||
|
RemainAfterExit=yes |
||||
|
|
||||
|
# Copy the seed on first boot only (see init.sh). Frigate owns config.yml |
||||
|
# afterwards: its web interface writes every change back to it, and it migrates |
||||
|
# the file in place on an upgrade. |
||||
|
ExecStart=/etc/quadlets/frigate/init.sh |
||||
|
|
||||
|
[Install] |
||||
|
WantedBy=frigate.target |
||||
@ -0,0 +1,80 @@ |
|||||
|
[Unit] |
||||
|
Description=Frigate - Network video recorder with object detection |
||||
|
Documentation=https://docs.frigate.video/ |
||||
|
After=network.target var-lib-virtiofs-data.mount frigate-init.service |
||||
|
Requires=var-lib-virtiofs-data.mount frigate-init.service |
||||
|
|
||||
|
# The pull runs in its own unit, and it is Wants=, NOT Requires=. A pull needs |
||||
|
# the registry; starting this container does not, as long as the image is in |
||||
|
# local storage. With Requires=, a boot without network fails the pull, the |
||||
|
# dependency fails, and a failed dependency is never retried: Restart= only |
||||
|
# applies to a unit that started at least once. |
||||
|
# |
||||
|
# After= still orders the pull before this container, so nothing is lost on a |
||||
|
# boot where the network is there. |
||||
|
Wants=frigate-image.service |
||||
|
After=frigate-image.service |
||||
|
|
||||
|
# Frigate starts on the configuration file seeded by frigate-init.service, or on |
||||
|
# the one it already owns. Without it, Frigate would write a default one of its |
||||
|
# own, with none of the settings the README lists. |
||||
|
ConditionPathExists=/var/lib/virtiofs/data/frigate/config/config.yml |
||||
|
|
||||
|
# Start/stop this unit when the target is started/stopped |
||||
|
PartOf=frigate.target |
||||
|
|
||||
|
[Container] |
||||
|
ContainerName=frigate |
||||
|
# The image REFERENCE, and not the name of the frigate.image unit: naming the |
||||
|
# unit makes quadlet generate a Requires= on it, which is the fault described in |
||||
|
# [Unit] above. Podman resolves the reference at start with --pull=missing. |
||||
|
# |
||||
|
# THIS REFERENCE IS ALSO IN frigate.image, and the two must be changed together. |
||||
|
Image=ghcr.io/blakeblackshear/frigate:0.18.2 |
||||
|
|
||||
|
# Root inside the container: the image starts s6-overlay, which runs nginx, |
||||
|
# go2rtc and Frigate as root. Frigate documents no other way to run. |
||||
|
|
||||
|
# Host networking: the cameras push nothing, Frigate pulls their RTSP streams, |
||||
|
# and WebRTC needs the real addresses of the browsers. The listening sockets are |
||||
|
# narrowed by the configuration file instead -- see the README. |
||||
|
Network=host |
||||
|
|
||||
|
# Decoded frames are passed between processes in /dev/shm: about 35 MB per |
||||
|
# camera detecting at 720p, plus up to 50 MB of logs and nginx cache. 512 MB |
||||
|
# covers eight cameras and two more. A shm too small shows as "Bus error" in the |
||||
|
# journal. |
||||
|
ShmSize=512m |
||||
|
|
||||
|
# Recording segments are written here first, then moved to /media/frigate. |
||||
|
Tmpfs=/tmp/cache:rw,size=1g |
||||
|
|
||||
|
# Precious data: configuration, database and model cache on one side, |
||||
|
# recordings on the other. Both on the virtiofs mount, where the host's ZFS |
||||
|
# snapshots and quota apply. |
||||
|
Volume=/var/lib/virtiofs/data/frigate/config:/config:z |
||||
|
Volume=/var/lib/virtiofs/data/frigate/media:/media/frigate:z |
||||
|
|
||||
|
# No /etc/localtime: Fedora CoreOS has none (it runs in UTC), and podman fails |
||||
|
# the start on a missing mount source. Frigate files its recordings by UTC date |
||||
|
# and its web interface shows the time zone of the browser. |
||||
|
|
||||
|
# Health check: the request of the image's own HEALTHCHECK, on the internal |
||||
|
# port, which the configuration file binds to the loopback. The image also skips |
||||
|
# the check during an orderly shutdown; that part needs a shell, and the state |
||||
|
# of a container being stopped does not matter. |
||||
|
HealthCmd=curl --fail --silent --show-error http://127.0.0.1:5000/api/version |
||||
|
HealthInterval=30s |
||||
|
HealthTimeout=10s |
||||
|
# The first start converts the detection model, which takes minutes on a CPU. |
||||
|
HealthStartPeriod=300s |
||||
|
HealthRetries=5 |
||||
|
|
||||
|
[Service] |
||||
|
Restart=always |
||||
|
RestartSec=10 |
||||
|
TimeoutStartSec=900 |
||||
|
TimeoutStopSec=120 |
||||
|
|
||||
|
[Install] |
||||
|
WantedBy=frigate.target |
||||
@ -0,0 +1,27 @@ |
|||||
|
[Unit] |
||||
|
Description=podman pull ghcr.io/blakeblackshear/frigate |
||||
|
Documentation=https://docs.frigate.video/ |
||||
|
|
||||
|
# Keep trying. A machine whose network arrives after it does fails this pull |
||||
|
# once, early in the boot, and a oneshot that failed once never runs again. |
||||
|
# StartLimitIntervalSec=0 makes the retry unlimited: the default start limit |
||||
|
# (5 attempts in 10 s) would turn the unit "failed" for good within a minute. |
||||
|
StartLimitIntervalSec=0 |
||||
|
|
||||
|
# Stopping the service target stops the pull with it, so `systemctl stop |
||||
|
# frigate.target` leaves nothing retrying behind. |
||||
|
PartOf=frigate.target |
||||
|
|
||||
|
[Image] |
||||
|
# Multi-architecture manifest (amd64 + arm64, verified 2026-11-01 on 0.18.2). |
||||
|
# The tag is a full version: Frigate migrates its configuration file in place |
||||
|
# when a new minor version starts on it, and keeps no way back. An upgrade is |
||||
|
# therefore something an operator decides, not something auto-update does. |
||||
|
# |
||||
|
# THIS REFERENCE IS ALSO IN frigate.container, on purpose, and the two must be |
||||
|
# changed together. The reason is in the container file. |
||||
|
Image=ghcr.io/blakeblackshear/frigate:0.18.2 |
||||
|
|
||||
|
[Service] |
||||
|
Restart=on-failure |
||||
|
RestartSec=60 |
||||
@ -0,0 +1,11 @@ |
|||||
|
[Unit] |
||||
|
Description=Frigate Service Target |
||||
|
Documentation=man:systemd.target(5) |
||||
|
Requires=frigate-init.service frigate.service |
||||
|
After=frigate-init.service frigate.service |
||||
|
|
||||
|
# Allow isolation - can stop/start this target independently |
||||
|
AllowIsolate=yes |
||||
|
|
||||
|
[Install] |
||||
|
WantedBy=multi-user.target |
||||
@ -0,0 +1,9 @@ |
|||||
|
# Precious data, on the virtiofs mount so that the host's ZFS snapshots and |
||||
|
# backups pick it up: |
||||
|
# - config: config.yml, the SQLite database (events, users), the model cache; |
||||
|
# - media: the recordings, the snapshots, the clips and the exports. |
||||
|
# The "$" marks them for removal on `systemd-tmpfiles --purge` (i.e. `make |
||||
|
# uninstall`). |
||||
|
d$ /var/lib/virtiofs/data/frigate 0750 root root - |
||||
|
d$ /var/lib/virtiofs/data/frigate/config 0750 root root - |
||||
|
d$ /var/lib/virtiofs/data/frigate/media 0750 root root - |
||||
Loading…
Reference in new issue