contaiNERD (under construction).

A privacy-focused, self-hosted NVR using Frigate with a Linux container crash course.

As of May 2026, this project is on hold. We're going to get to it! Promise.

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.

🤷
I was really proud of the pun in the post title, but be aware that 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.

💡
Hey shloobs, I forgot my password to the Ice Cream Machine again. sudo passwd andy

It'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


⚠️
This is a work in progress with no expected completion date.
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? :)