Personally, I
use Frigate with the Frigate+ option,
with a system architecture similar to the diagram below.
Frigate
runs on a dedicated
mini PC accessible from the internet with an
encrypted connection via a Let's Encrypt
certificate. Everything is managed by Traefik, which also
handles authentication. Both run on the Docker
containerization tool. Locally, this
mini PC is called Cerbere. It's supported by a
secondary machine called Ultra, which manages a
low-cost camera whose RTSP address fluctuates
regularly. Ultra also runs an Apache httpd server.
Cerbere
uses a
Google Coral TPU key to accelerate the training
models. A total of six
IP cameras are connected, distributed outdoors. Four are
connected via PoE (Power over Ethernet), and three use
PTZ (Pitch-to-Zoom) functionality and are mobile.
The cameras
include a video intercom controlled from a monitor
connected indoors via PoE Ethernet. There's also a
low-cost camera connected via a Wi-Fi repeater located
in an outdoor electrically powered area.
Physically, my system is distributed across several
zones:
There are a
plethora of IP cameras on the market; the cheapest are
often Chinese models linked to an Android app with
highly questionable security. The selection
criteria are:

Frigate relies on Docker, which I installed simply by typing on my Mageia.
urpmi docker docker-compose
Next, we launch Docker by typing
systemctl start docker
that is its status
systemctl status docker
and the result
● docker.service - Docker Application Container Engine
Loaded: loaded
(/usr/lib/systemd/system/docker.service; disabled; preset:
disabled)
Active: active (running) since Fri
2025-09-26 17:02:55 CEST; 6s ago
Docs:
http://docs.docker.com
Process: 210883
ExecStartPre=/usr/sbin/docker-network-cleanup (code=exited,
status=0/SUCCESS)
Main PID: 210886 (dockerd)
Tasks: 17
Memory: 41.1M
CPU: 369ms
CGroup:
/system.slice/docker.service
├─210886 /usr/sbin/dockerd --data-root /var/cache/docker -H
unix:///var/run/docker.sock -H tcp://127.0.0.1:2375
└─210899 containerd --config
/var/run/docker/containerd/containerd.toml
sept. 26 17:02:52 ultra.kervao.fr dockerd[210899]:
time="2025-09-26T17:02:52.687489959+02:00" level=info
msg=serving...
address=/var/run/docker/containerd/containerd.sock
sept. 26 17:02:52 ultra.kervao.fr dockerd[210899]:
time="2025-09-26T17:02:52.687576078+02:00" level=info
msg="containerd successfully booted in 0.201550s"
sept. 26 17:02:54 ultra.kervao.fr dockerd[210886]:
time="2025-09-26T17:02:54.283134739+02:00" level=info
msg="Loading containers: start."
sept. 26 17:02:55 ultra.kervao.fr dockerd[210886]:
time="2025-09-26T17:02:55.007460149+02:00" level=info
msg="Loading containers: done."
sept. 26 17:02:55 ultra.kervao.fr dockerd[210886]:
time="2025-09-26T17:02:55.281188368+02:00" level=warning
msg="WARNING: API is accessible on http://127.0.0.1:2375 without
encryption>
sept. 26 17:02:55 ultra.kervao.fr dockerd[210886]:
time="2025-09-26T17:02:55.281244407+02:00" level=info
msg="Docker daemon" commit=library-import
containerd-snapshotter=false storage>
sept. 26 17:02:55 ultra.kervao.fr dockerd[210886]:
time="2025-09-26T17:02:55.281378785+02:00" level=info
msg="Daemon has completed initialization"
sept. 26 17:02:55 ultra.kervao.fr dockerd[210886]:
time="2025-09-26T17:02:55.804448712+02:00" level=info msg="API
listen on /var/run/docker.sock"
sept. 26 17:02:55 ultra.kervao.fr systemd[1]: Started
docker.service.
sept. 26 17:02:55 ultra.kervao.fr dockerd[210886]:
time="2025-09-26T17:02:55.804558097+02:00" level=info msg="API
listen on 127.0.0.1:2375"
to see if Docker is working correctly, we will type
Docker Run Hello World
Here is the result
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
17eec7bbc9d7: Pull complete
Digest:
sha256:54e66cc1dd1fcb1c3c58bd8017914dbed8701e2d8c74d9262e26bd9cc1642d31
Status: Downloaded newer image for hello-world:latest
Hello from Docker!
This message shows that your installation appears to be
working correctly.
To generate this message, Docker took the following
steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image
from the Docker Hub.
(amd64)
3. The Docker daemon created a new container from
that image which runs the
executable that produces the output
you are currently reading.
4. The Docker daemon streamed that output to the
Docker client, which sent it
to your terminal.
To try something more ambitious, you can run an Ubuntu
container with:
$ docker run -it ubuntu bash
Share images, automate workflows, and more with a free
Docker ID:
https://hub.docker.com/
For more examples and ideas, visit:
https://docs.docker.com/get-started/
To have Docker launch on every reboot, type
systemctl enable docker
We will create the /etc/frigate and /etc/frigate/config directories ; the records will be stored in the /media/frigate directory. We return to /etc/frigate/config where I created the docker-compose.yml file which will contain
services:
frigate:
container_name: frigate
privileged: true # this may not be
necessary for all setups
network_mode: host # j'ai dû rajouter cette
ligne pour accéder à ma caméra restreamée avec mediamtx
restart: unless-stopped
stop_grace_period: 30s # allow enough
time to shut down the various services
image:
ghcr.io/blakeblackshear/frigate:stable
shm_size: "512mb" # update for your
cameras based on calculation above
devices:
- /dev/bus/usb:/dev/bus/usb # ligne
pour l'accélérateur USB Google Coral, à commenter sinon
-
/dev/dri/renderD128:/dev/dri/renderD128 # For intel hwaccel,
needs to be updated for your hardware
volumes:
-
/etc/localtime:/etc/localtime:ro
-
/etc/frigate/config:/config
-
/media/frigate:/media/frigate
- type: tmpfs # Optional:
1GB of memory, reduces SSD/SD Card wear
target:
/tmp/cache
tmpfs:
size: 1000000000
ports: #section inutile avec le
mode réseau host
- "8971:8971" # port
de l'interface navigateur
#- "8554:8554" # RTSP feeds,
j'ai mis en commentaire car conflit avec mediamtx, utile pour
renvoyer le flux vidéo vers homeassistant
- "8555:8555/tcp" # WebRTC
over tcp
- "8555:8555/udp" # WebRTC
over udp
and config.yaml in which we will define the cameras and objects to detect (list here https://docs.frigate.video/configuration/objects )
mqtt:
enabled: false
cameras:
Camera-sud:
ffmpeg:
inputs:
- path:
rtsp://frigate:passwd@192.168.2.110
roles:
- detect
- record
hwaccel_args: preset-vaapi
objects:
track:
- person
- dog
- cat
- bird
- bicycle
- car
- motorcycle
detect:
width: 1280
height: 720
record:
enabled: true
retain:
days: 7
mode: all
Camera-entree:
ffmpeg:
inputs:
- path:
rtsp://frigate:passwd@192.168.2.112
roles:
- detect
- record
hwaccel_args: preset-vaapi
objects:
track:
- person
- dog
- cat
- bird
- bicycle
- car
- motorcycle
detect:
width: 1280
height: 720
record:
enabled: true
retain:
days: 7
mode: all
Camera-piscine:
ffmpeg:
inputs:
- path:
rtsp://frigate:passwd@192.168.2.200:554/ch0_1.264
roles:
- detect
- record
objects:
track:
- person
- dog
- cat
- bird
detect:
width: 800
height: 448
record:
enabled: true
retain:
days: 7
mode: all
Camera-ouest:
ffmpeg:
inputs:
- path:
rtsp://ultra.kervao.fr:8554/cam1
roles:
- detect
- record
objects:
track:
- person
- dog
- cat
- bird
detect:
width: 640
height: 360
record:
enabled: true
retain:
days: 7
mode: all
detectors:
cpu1:
type: cpu
num_threads: 3
detect:
enabled: true
version: 0.16-0
If you have a Google Coral USB accelerator, we will replace it with the detectors.
coral:
type: edgetpu
device: usb
Then type in this same directory
docker compose -f docker-compose.yml up -d
Here is the result: 9.6s
+] Running 10/10
✔ frigate 9 layers
[⣿⣿⣿⣿⣿⣿⣿⣿⣿]
0B/0B
Pulled
79.5s
✔ b1badc6e5066 Pull
complete
1.2s
✔ 4f4fb700ef54 Pull
complete
0.3s
✔ 14d4601b76c8 Pull
complete
14.6s
✔ cab46c26a5aa Pull
complete
1.1s
✔ 6891a7512d41 Pull
complete
14.3s
✔ b44b002328e4 Pull
complete
8.3s
✔ 8074b201d282 Pull
complete
8.6s
✔ d823104cefbd Pull
complete
9.0s
✔ f86078113133 Pull
complete
9.6s
[+] Running 2/2
✔ Network config_default
Created
0.1s
✔ Container
frigate Started
docker restart frigate
Frigate is now installed, launched, and accessible locally from the browser by typing
https://ultra.kervao.fr:8971/
To find out the password you will need to type
docker logs frigate
and we see
2025-09-26 17:46:21.528704726 [2025-09-26 17:46:21]
frigate.app
INFO :
********************************************************
2025-09-26 17:46:21.529867665 [2025-09-26 17:46:21]
frigate.app
INFO :
********************************************************
2025-09-26 17:46:21.530590965 [2025-09-26 17:46:21]
frigate.app
INFO : *** Auth is enabled,
but no users
exist. ***
2025-09-26 17:46:21.531237207 [2025-09-26 17:46:21]
frigate.app
INFO : *** Created a default
user:
***
2025-09-26 17:46:21.542212889 [2025-09-26 17:46:21]
frigate.app
INFO : *** User:
admin
***
2025-09-26 17:46:21.566794613 [2025-09-26 17:46:21]
frigate.app
INFO : *** Password:
2bf8aeb98g16b66f54e70a8e8f121076 ***
2025-09-26 17:46:21.577589942 [2025-09-26 17:46:21]
frigate.app
INFO :
********************************************************
2025-09-26 17:46:21.598394530 [2025-09-26 17:46:21]
frigate.app
INFO :
********************************************************
With a TPU, we should get a message that looks like this.
2025-10-30 07:55:40.442764361 [2025-10-30 07:55:40]
frigate.detectors.plugins.edgetpu_tfl INFO :
TPU found
2025-10-30 07:55:40.443899970 INFO: Created
TensorFlow Lite XNNPACK delegate for CPU.
This allows you to
log in, and here's the general interface with the latest
events at the top and the live views just below.
With only the TPU,
it's practically unusable on a daily basis; the program
constantly uses almost 100% of the resources.
And with a TPU, it's like night and day!!
Before
going any further, it is important to understand some basic
concepts of how Frigate works. We talk
about alerts and detections. Alerts are based on detections
but are considered priority events to be reported, while
detections are less significant events that do not warrant
notification but are still recorded and visible in the event
review (see the documentation in English here https://docs.frigate.video/configuration/review/).
Let's move on to
the settings, accessible via the gear icon in the bottom left
corner of the main screen. This is the interface you get.

In the first
tab, User Interface, there are
general settings that I have not modified.
You can then
access the camera settings via Camera
Settings ; the Management menu allows you
to activate or deactivate the cameras, which is useful when
a camera is causing problems and to avoid error messages.
Before going to
Activities, we'll start by
defining the masks and detection zones via Masks/Zones. For each
camera selectable from the blue button in the upper right
corner, we declare detection zones; other areas of the image
will be ignored for detection.





We
now return to the Activities menu ; in the
configuration below, only Person and Car objects will
be displayed as alerts in the predefined area, and other
detected objects will be classified as detections in the
same area.
And
so on for all cameras. Note that you will need to restart
Frigate for the
changes to take effect; this can be done from the Settings
menu.
In
the Enhancements menu, you can
activate (they are disabled by default)
Regarding
recordings, by default we have
record:
enabled: true
retain:
days: 7
mode: all
This
means that both alerts and detections are recorded and
stored for 7 days. For more detailed recording management,
see this page: https://docs.frigate.video/configuration/record/


Frigate calculates a
score for each object detected in every image, and the
object is considered positive when both the score exceeds
the min_score and the threshold, which is the
average of the scores over several consecutive images.
This last parameter confirms the detection.
Here's what
can be added to the config.yaml file as a
common section for all cameras; these are values
commonly shared by the Frigate user community.
objects:
filters:
dog:
min_score: .7
threshold: .9
cat:
min_score: .65
threshold: .8
person:
min_score: .7
threshold: .9
car:
min_score: .65
threshold: .85
objects:
track:
- person
- dog
- cat
- face
filters:
person:
min_score: .7
threshold: .8
To further reduce false positives, a minimum and maximum detection zone can be defined; that is, if the object is too large or too small, it will be automatically rejected. For a stream with a resolution of 3072x1728, an image can be extracted at that size and opened in GIMP.
Using the selection tool, draw the min and
max boxes to detect a car (for example). The width and
height appear at the bottom center Rectangle:
245x252
The minimum detection box parameter is min_area and has a value of width*height, so for the example above, min_area=245x252=61470. The same applies to the max_area parameter. In practice, I set for a car
min_area 22000
max_area 230000
for a person
min_area 6000
max_area 30000
which gives us
the following in
config.yaml
filters:
car:
min_score: .7
threshold: .9
min_area: 22000
max_area: 230000
person:
min_score: .7
threshold: .8
min_area: 6000
max_area: 3000
To
enable face detection, we will first add the corresponding
section to the config.yaml file.
face_recognition:
enabled: true
And
for the cameras in question, we will add the mention face in the track subsection.
objects:
track:
- person
- dog
- cat
- bicycle
- car
- motorcycle
- face
Frigate is restarted and the logs return the following messages
2025-11-02 11:20:57.493001613
[2025-11-02 11:20:57]
frigate.config.config
WARNING : Camera-entree is configured to track ['face']
objects, which are not supported by the current model.
2025-11-02 11:20:57.495025758 [2025-11-02
11:20:57]
frigate.config.config
WARNING : Camera-piscine is configured to track ['face']
objects, which are not supported by the current model.
2025-11-02 11:20:57.519957917 [2025-11-02
11:20:57]
frigate.app
INFO : Starting Frigate (0.16.1-e664cb2)
2025-11-02 11:20:57.520656637 [2025-11-02
11:20:57]
frigate.app
INFO : Creating directory:
/media/frigate/clips/faces
(...)
2025-11-02 11:20:58.822822712
[2025-11-02 11:20:58]
frigate.api.fastapi_app
INFO : FastAPI started
2025-11-02 11:21:00.117012864 [2025-11-02
11:21:00]
frigate.util.downloader
INFO : Downloading complete:
https://github.com/NickM-27/facenet-onnx/releases/download/v1.0/facenet.tflite
2025-11-02 11:21:00.269584095 [2025-11-02
11:21:00] frigate.detectors.plugins.edgetpu_tfl
INFO : TPU found
2025-11-02 11:21:00.270930420 INFO: Created
TensorFlow Lite XNNPACK delegate for CPU.
2025-11-02 11:21:03.456767865 [2025-11-02
11:21:03]
frigate.util.downloader
INFO : Downloading complete:
https://github.com/NickM-27/facenet-onnx/releases/download/v1.0/landmarkdet.yaml
A
Face Library icon appears








This
feature, introduced in version 0.17.0, allows you
to create subcategories of objects. In the example
below, I'll differentiate between the cats my
cameras usually see. In the main window, click the
Classification icon.

We will create a subcategory of an
existing object, here resident cats,
distinguishing between cats

Next, you will need to select the
images that correspond to Plumette the cat and the
same for the other cat.


This
feature, introduced in version 0.17.0,
allows you to distinguish the state of a
particular object, such as whether a
door or gate is open. In this example, I
will consider the gate's open or closed
state. In the main window, select the Classification icon.






By default, Frigate
generates a self-signed certificate to use port
8971, which triggers a security warning in the
browser. In Firefox ,
you can accept the risk to access it. But this
isn't ideal, especially if you're accessing Frigate
from the internet. In the configuration
presented here, we'll use Apache,
not Nginx as
is often suggested. For Let's
Encrypt
configuration with
Apache,
see this page.
The
principle is as follows:
Internet
-> HTTPS port 443 -> reverse proxy ->
internal HTTP -> Frigate port 8971
Apache acts as a reverse proxy, meaning it allows access to an internal URL by automatically modifying the URL and pointing to the correct port. My internet-accessible address is https://adresseperso.ddns.net, which already points to an index.html page . This configuration therefore allows access to Frigate at https://adresseperso.ddns.net/frigate
In the Apache configuration file httpd.conf ,
the following modules will be enabled:
LoadModule proxy_module
modules/mod_proxy.so
LoadModule proxy_http_module
modules/mod_proxy_http.so
and we will add the virtual host
<VirtualHost
192.168.2.13:443>
ServerName
adresseperso.ddns.net
SSLEngine on
SSLCertificateFile
/etc/letsencrypt/live/adresseperso.ddns.net/fullchain.pem
SSLCertificateKeyFile
/etc/letsencrypt/live/adresseperso.ddns.net/privkey.pem
ProxyPreserveHost
On
ProxyRequests
Off
# Redirection vers Frigate
sur port 8971
ProxyPass
/frigate http://127.0.0.1:8971/frigate
ProxyPassReverse
/frigate http://127.0.0.1:8971/frigate
# Websocket support
RewriteEngine
On
RewriteCond
%{HTTP:Upgrade} =websocket [NC]
RewriteRule
^/frigate/(.*) ws://127.0.0.1:8971/frigate/$1
[P,L]
</VirtualHost>
We restart Apache by
typing `systemctl
restart httpd`
In the docker-compose.ym
file ,
we will modify the default path in
the environment section by adding
We'll use Traefik as a reverse
proxy ; it will manage the Let's
Encrypt certificate and redirect
secure traffic to Frigate .
Another configuration detail: in my setup, I have
two servers.
- Apache server,
local address 192.168.2.13, accessible via myapacheserver.ddns.net
- Frigate server,
local address 192.168.2.11, accessible via myfrigateserver.ddns.net
Traefik will be
hosted on the Frigate
server and will also manage the Let's
Encrypt
certificate and the redirection of secure
traffic to the Apache server.
On the modem/router, you need to open a route to the
Traefik server
with the local address 192.168.2.11, using ports 80
(for automatic certificate renewal) and 443. You
will also need to open the same ports using Shorewall on the
Traefik machine
.
It is
not necessary to open a route on the router to port
8971 (for login/password connections to Frigate) or
port 5000 (for login/password connections to
Frigate).
Centralization:
Traefik becomes
the sole entry point. Ports 80 and 443 are only opened
on the PC running Docker/Traefik.
SSL
for all: The Apache
httpd server
will also automatically benefit from the
secure HTTPS (SSL) connection, even if it doesn't
manage it itself. Traefik handles
decryption and redirects to the insecure HTTP
connection to the Apache server
on the local network.
Automatic renewal: Since
port 80 points to Traefik, Let's
Encrypt can
renew the Frigate
and Apache
certificates without any intervention.
In summary, the principle for relaying to
Apache is as follows.
Internet -> HTTPS port 443 -> reverse proxy -> HTTP interne -> Apache port 80
For Frigate, it's a
little different because, since we connect to its
port 8971 with an encrypted connection, Traefik
will need to accept Frigate's
self-signed certificate. This
requires some additional configuration, particularly
with the Shorewall
firewall . The connection principle is as follows:
Internet -> HTTPS port 443 ->
reverse proxy -> HTTPS interne -> Frigate port
8971
Regarding authentication, traefik will manage the login/password and transmit them to frigate without the need to re-enter them via the frigate login screen .
Let's start by creating a
/etc/traefik
directory, then navigate to it and create an
empty acme.json file
with 600 permissions; it will contain the
certificates.


Now let's
return to the main interface. We saw earlier that the main
view links to the camera views; it is accessible by clicking
on the icon representing a camera on the left.
Clicking on the Event Review on the left
displays all recent events (alerts, detections, movement). The
detected object appears as an icon in the top left corner of
the image; you can access the replay by clicking on the image.
You can save the review by clicking on Mark these items
as reviewed at the bottom.





When you click on a detection image you can
view the kinematics of the object in more detail with
the detection parameters which can be used to refine
the frigate
configuration .
You can
delete them one by one using the context menu, or in
batches with CTRL-A and then the Delete button in
the top right corner.
As we saw earlier , we
could access the system resource status via the Settings->System->System
Metrics menu ;
there is also a Storage tab that
provides a status of recordings


For the rest, I refer you to the Frigate
documentation for usage instructions, which includes
some useful URLs.
https://docs.frigate.video/guides/getting_started
https://community.jeedom.com/t/tuto-integrer-frigate-et-faire-de-la-reconnaissance-video-dobjet-par-ia-en-local-dans-son-jeedom/117108
https://www.simplehomelab.com/frigate-docker-guide/
Regarding remote viewing on a mobile device, there is indeed an unofficial Android application, but I don't really see the point because simply accessing the address directly (once it's made accessible on the internet) from a mobile browser is quite user-friendly, as you can see below.

Now we can add it at the
beginning of the script along with the other
variables.
then further on, instead of sending by email
### --- ENVOI SIGNAL
---
if [[ -n "$ATTACH" && -f
"$ATTACH" ]]; then
signal-cli \
send
-u "$SIGNAL_USER"\
-g
"Wti9r4I7n+76f(...)O3yLkUIgh3TAt9Y="\
-a
"$ATTACH"\
-m
"🚨 Alerte Frigate
Caméra
: ${camera}
Objet
: ${label}
ID
: ${EVENT_ID}
Date
: $(date)"
# si tmp
file, on peut le supprimer
if [[
"$ATTACH" == /tmp/frigate_snapshot_* ]]; then
rm
-f "$ATTACH"
fi
log
"Alerte envoyée sur Signal au groupe Alarme avec pj :
$ATTACH"
else
signal-cli \
send
-u "$SIGNAL_USER"\
-g
"Wti9r4I7n+76f(...)O3yLkUIgh3TAt9Y="\
-m
"🚨 Alerte Frigate
Caméra
: ${camera}
Objet
: ${label}
ID
: ${EVENT_ID}
Date
: $(date)"
log
"Alerte envoyée sur Signal au groupe Alarme sans pj"
fi














From then on, it picked up many more objects, like this cat, that I couldn't detect before.

When the
model is well trained, we can review the object detection
threshold settings to lower
them in order to capture more objects.
Frigate+ allows
you to generate up to 12 models per year. The
advantage of generating multiple models is that, model
by model, you can create refined models that precisely
adapt to the cameras' environment. Successive models
improve false positive detection, adapt to varying
conditions (season, wind, lighting, etc.), and better
detect certain objects (animals, stationary objects,
etc.). Generating an additional model does not mean
using multiple models until the system's resources are
exhausted; the new model simply replaces the previous
one.
Generating one model per month is
a good pace; during the life of a model, it will be
necessary to continue annotating the images; ideally,
at least 200 to 300 annotated images are needed for it
to be worthwhile to change models and for a real
improvement to be observed.
A word of caution regarding image
annotation: when submitting images, don't limit
yourself to false positives, otherwise the model will
become paranoid and stop detecting anything, or it
will make mistakes in detection, for example,
mistaking a person for a cat. Therefore, it's
essential to also submit positive detections. The
ideal ratio is 80% true positives and 20% false
positives.
If it regularly detects a gatepost as a person, it will also be necessary to submit images where a real person walks past the gatepost so that it can see the difference; this is called contrastive learning .
To create
a new model in the Frigate+
interface, click on Request
Model




Next, you need to modify the config.yaml file to indicate the identifier of the new model and restart frigate .
model:
path: plus://new-identifier
Note that if the base model evolves to a new version, it will be necessary to request the generation of a new customized model for it to be taken into account.
If the subscription is not renewed, access to the personalized template will still be available.
Here are
some links to learn more