contaiNERD (under construction).
A privacy-focused, self-hosted NVR using Frigate with a Linux container crash course.

Shloobs,
Let me start off by apologizing for dragging you down the self-hosted NVR rabbit hole, but this one won't be too bad. We're going to build on skills we already have with some simple network configurations and new ways to use containers.
containerd is a real thing and we're not using it here. Frigate only normally runs in docker*. We've already used Docker in macOS, and it's even simpler in Linux. So simple, we technically don't even need Docker.
On macOS, we were using Colima, which is super interesting on its own. It is essentially just running containers in a tiny linux virtual machine, complicating access to things like Video ToolBox. In theory an m4 Mac Mini could brute force it just with cpu, but you already have the perfect setup for it. You're already familiar with how launchd and setting up Launch Agents/Launch Daemons works in macOS, and we'll use the same concepts applied a little bit differently.
Docker uses systemd to manage the Docker daemon, which then manages the containers. Because of this, docker is rootful. It's like using a Launch Daemon to bootstrap a Launch Agent to start a VM. Not exactly, but we're stacking layers we don't need.
Instead of Docker, we can use Podman and systemd. Podman downloads the container image and configures mappings just like Docker Compose, but it also creates the service in systemd- meaning systemd is directly managing the container. If we run the service in the user space (like a Launch Agent), we are not relying on a system level daemon with root priveledges. Podman containers can run as the user.
sudo passwd andyIt's good practice to use ssh keys and turn passwords off. I just forgot to do that.
https://www.purdue.edu/science/scienceit/ssh-keys-unix.html
We need two things from the hardware: a video decoder and AI accelerator. Your Quadro has both (NVENC and Tensor Cores). I used that card before it became yours with seven 1080p cameras and it used about 30 watts. My current setup uses a Coral TPU and Intel Quicksync, for comparison.
The cameras need to provide an RTSP stream and have a stable IP. Frigate works with password protected IP cameras, so we can restrict it from others using the same LAN as you. Eventually, we can build you an opnsense box.
UID/GID Mapping
Containers think they have root in their own environment, but since we're accessing system resources, we need to map the internal IDs in the container to the linux host IDs.
sudo usermod --add-subuids 100000-165536 $USER
sudo usermod --add-subgids 100000-165536 $USER
Remember how we mapped paths in docker-compose? It's the same concept. That's one of the ways containers are sandboxed without being a virtual machine- they operate in a separate userspace, but you can easily map devices, paths, or users.
Now you can actually answer correctly when someone asks you the difference between brew, docker and a virtual machine.
Setting Up the Container
Podman should already be installed. Quick sanity check:
podman --version
If it isn't:
sudo apt install -y podman uidmap slirp4netns
podman system migrate
Then, we need to install the required packages for your Quadro:
sudo apt install nvidia-container-toolkit
Make the directory for Frigate. With just one camera and motion detection, you won't be storing that much video. You'll be just fine writing to your home folder, or anywhere else your user can write to. Outdoor cameras tend to need more storage since bushes and trees trigger recording unless properly tuned.
mkdir -p ~/frigate/config
mkdir -p ~/frigate/media
I'm not sure if the old wisdom of continuous writes harming SSDs is still true anymore. My frigate writes almost 10 GiB/hr, so I use a zfs mirror on hard drives. You'll be just fine using your ssd instead of the RAID array. If you did want to store everything on the array, set the permissions and ownership appropriately.
While we're at it, let's create the config. Frigate will crash if it is not present or if the yaml is invalid. We'll have fun with this later.
touch ~/frigate/config/config.yml
Create the service:
mkdir -p ~/.config/systemd/user
nano ~/.config/systemd/user/frigate.service
We're going to paste this into nano. It looks a lot like a docker-compose file, doesn't it?
[Unit]
Description=Frigate NVR (Rootless Podman)
Wants=network-online.target
After=network-online.target
[Service]
Restart=always
TimeoutStartSec=60
# Clean old container
ExecStartPre=/usr/bin/podman rm -f frigate || true
# Pull latest image
ExecStartPre=/usr/bin/podman pull ghcr.io/blakeblackshear/frigate:stable
ExecStart=/usr/bin/podman run \
--name frigate \
--rm \
--sdnotify=conmon \
-p 127.0.0.1:5000:5000 \
-p 8554:8554:8554 \
--shm-size=512m \
--device /dev/nvidia0 \
--device /dev/nvidiactl \
--device /dev/nvidia-uvm \
--device /dev/dri/renderD128 \
-v %h/frigate/config:/config \
-v %h/frigate/media:/media/frigate \
-v /etc/localtime:/etc/localtime:ro \
ghcr.io/blakeblackshear/frigate:stable
ExecStop=/usr/bin/podman stop -t 10 frigate
ExecStopPost=/usr/bin/podman rm -f frigate
[Install]
WantedBy=default.target
There's one really important line here: -p 127.0.0.1:5000:5000
Normally, port 5000 is unauthenticated http. Since you share a lan with other people, they'd be able to access Frigate. Binding it to 127.0.0.1 prevents this, and we'll use Tailscale exclusively. We're also going to block lan access at the firewall level.
Identify your NIC:
ip -br addr
Then
sudo ufw deny in on yourNic to any port 5000 sudo ufw allow in on tailscale0
Subistitute yourNIC for the device with your lan ip.
lastly, we need to let the service run without the user:
sudo loginctl enable-linger $USER
Now let's do this one more time:
systemctl --user daemon-reload
systemctl --user restart frigate
Frigate should be reachable at your.tailscale.IP:5000
Several things have changed: We switched the Ice Cream Machine from Pop OS to Bazzite. I think systemd/podman is the accepted best way to run containers on Fedora/Red Hat, so we're off to a good start.
Shloobs, wanna give me access to the computer again? :)