Maker.io main logo

Build a Fully Local, Privacy-First RTSP IP Camera with any Raspberry Pi

223

2026-07-29 | By Nate_Larson

License: Attribution 3D Printing Camera Wifi Wireless Raspberry Pi SBC

Introduction

If you already have Home Assistant and Frigate NVR set up, you're closer than you think to creating a fully local, open-source, wide-angle night-vision camera. This setup can be achieved for a fraction of the cost of a commercial IP camera, and it ensures that no video frames are sent to someone else's cloud. With this system, there are no subscriptions, no cloud accounts, no vendor lock-in, and no need to worry about what happens to your footage. Everything stays on your own hardware and within your own network.

Subscription-based "smart" cameras are widespread today, but their convenience comes with hidden costs beyond just the monthly fee. When your camera relies on a cloud backend, your video is leaving your property. This project serves as a direct alternative to that model, seamlessly integrating with the privacy-first, locally controlled framework that Home Assistant and Frigate are designed around.

The project uses a Raspberry Pi Zero W and a Pi NoIR Camera Module 3 Wide in a weatherproof enclosure. MediaMTX handles streaming H.264 video from the camera module as a standard RTSP stream using its native rpiCamera source; talking directly to the rpicamera stack without spawning external processes or piping stdout. Frigate picks up the stream just like any other IP camera. From Frigate's perspective, this is completely indistinguishable from a commercial camera: live view, recording, motion detection, and full Home Assistant integration all work out of the box.

Raspberry Pi with camera in an enclosure

 

For context, I built this to keep an eye on my chickens when they are in the run and to keep watch on the coop door. The same setup works just as well for a garage, workshop, front door, greenhouse, or anywhere you need a locally controlled camera with solid low-light performance, though for better night performance, you may want to add some IR LEDs for better visibility.

Prerequisites

This guide assumes you've already got Home Assistant and/or Frigate NVR up and running, though they are not required if you have another source you plan on piping your RTSP video stream into.

If you still need to get those set up, check out the following guides:

 Parts and Materials

·         Raspberry Pi Zero W or any other Raspberry Pi SBC

·         Raspberry Pi NoIR Camera Module 3 Wide or any other Raspberry Pi compatible camera

·         Raspberry Pi Zero Camera Cable (150mm)

·         Weatherproof Enclosure

·         Cable Gland (USB Power Entry)

·         #2 Screws (Pi/Camera Mount)

·         PETG Filament (3D Printed Mount)

You'll also need:

Why the NoIR Camera Module?

The NoIR (No Infrared) variant is what enables night vision functionality. Standard Pi camera modules come with an IR-cut filter that blocks infrared light, which is beneficial for accurate color representation during the day. However, this filter also obstructs the infrared spectrum that night-vision illumination relies on. By omitting this filter, the sensor can capture both visible and near-infrared light. In complete darkness, when an IR source is present, you can achieve a clear black-and-white image. Even without a dedicated IR illuminator, the NoIR sensor performs significantly better in low-light conditions compared to a standard camera module.

The Wide variant provides approximately a 120° field of view from a single mounting point, making it useful for monitoring a compact area without the need for multiple cameras or a pan/tilt arrangement.

Raspberry Pi Camera Module 3 NoIR Wide

Pi Zero W vs. Pi Zero 2 W — Which One?

For this project, I recommend the newer Raspberry Pi Zero 2 W, which features a quad-core Cortex-A53 processor and efficiently handles video encoding tasks. However, the original Raspberry Pi Zero W is also a viable option and is what I used since I had one lying around; any other Raspberry Pi with WiFi functionality will work, but you would need a different enclosure and camera cable.

Raspberry Pi Zero 2 W board

 

MediaMTX talks directly to the VideoCore hardware encoder rather than burdening the CPU with software encoding. In testing with my original Raspberry Pi Zero W, MediaMTX with the native rpiCamera source used only around 11% CPU at steady state, leaving plenty of headroom.

Step 1: Flash Raspberry Pi OS Lite

Download the Raspberry Pi Imager and flash Raspberry Pi OS Lite to your microSD card. The Lite version is essential for working smoothly with the VideoCore hardware encoder on the Pi Zero family, making H.264 encoding easy, even on lighter hardware.

Raspberry Pi imager

To learn how to flash an OS for Raspberry Pi, check out this guide: How to Flash an Operating System to SD Card for a Raspberry Pi

Before you write the image, click the gear icon in the imager to pre-configure a few things:

  • Hostname: something useful, like outdoorcam

  • Enable SSH: password or public key, your call

  • WiFi credentials: SSID and password for your network

  • Username: worth a moment's thought — the default is pi, but you can set whatever you like. Just remember it, because you'll need to put it in the systemd service file in Step 5.

  • Locale and timezone: set these for your region

Write the image, put the card in the Pi, power it up, and SSH in to make sure everything's working before you go any further:

Copy Code
ssh yourusername@outdoorcam.local

If you need assistance with this, I recommend this tutorial: How to Boot to Command Line and SSH on Raspberry Pi

This is also a great time to ensure everything is up-to-date:

Copy Code
sudo apt update && sudo apt upgrade -y

Then do a quick reboot before reconnecting over SSH to continue.

Copy Code
sudo reboot

Step 2: Verify rpicam-apps

The Raspberry Pi camera software suite was recently renamed to rpicam-apps. The old libcamera-* commands and their compatibility symlinks are gone in current Raspberry Pi OS releases, so libcamera-hello and similar commands no longer work. Recent versions of Raspberry Pi OS Lite ship with rpicam-apps pre-installed, but let's confirm:

Copy Code
rpicam-hello --version

If that spits out a version string, you're good. If it comes back as command not found:

Copy Code
sudo apt install -y rpicam-apps

Once that's resolved, verify the camera module is actually detected:

Copy Code
rpicam-hello --list-cameras

You should see the camera sensor listed with its available modes. If nothing shows up, the most likely culprit is the CSI ribbon cable since it's easy to not quite seat it fully, and the contacts need to face away from the PCB on both ends. The locking tab on the Pi Zero connector is particularly easy to miss.

 How to Connect a Camera to a Raspberry Pi

Step 3: Configure GPU Memory

Note: This step is only necessary if you are using a Pi Zero board.

The Pi's VideoCore GPU handles H.264 encoding, but it needs enough memory headroom to do that reliably. Add this to /boot/firmware/config.txt:

Copy Code
echo 'gpu_mem=128' | sudo tee -a /boot/firmware/config.txt

That reserves 128MB for the GPU. Without it, the hardware encoder can fail or produce garbage output under sustained load. This takes effect following a reboot, which we'll do at the end once everything's configured.

Additionally, if you are using a Raspberry Pi Zero W or 2 W, I recommend you also review this guide: How to Keep a Raspberry Pi Zero W or Zero 2 W Reliably Connected to WiFi

Step 4: Install and Configure MediaMTX

MediaMTX (formerly rtsp-simple-server) is a lightweight and zero-dependency live media server and media proxy that allows publishing, reading, proxying, recording, and playback of real-time video and audio streams. Its native rpiCamera source talks directly to the camera stack, handling H.264 encoding via the VideoCore hardware encoder without spawning subprocesses or piping video over stdout. The result is a stable, low-overhead RTSP stream that Frigate connects to like any other IP camera and works directly in VLC as well.

MediaMTX website screen capture

MediaMTX's native rpiCamera source requires a couple of libcamera runtime libraries:

Copy Code
sudo apt install -y libcamera0.4 libfreetype6

Download the correct binary for the Pi Zero family's ARMv6 architecture. Check the MediaMTX releases page (https://github.com/bluenviron/mediamtx/releases) for the latest version and the correct architecture for your Raspberry Pi, and substitute accordingly:

Copy Code
wget https://github.com/bluenviron/mediamtx/releases/download/v1.19.0/mediamtx_v1.19.0_linux_armv6.tar.gz
tar -xzf mediamtx_v1.19.0_linux_armv6.tar.gz
sudo mkdir -p /opt/mediamtx
sudo mv mediamtx mediamtx.yml /opt/mediamtx/

Now edit the configuration file:

Copy Code
sudo nano /opt/mediamtx/mediamtx.yml

The default config is heavily commented and quite long. Change the "rtspTransports: [udp, multicast, tcp]" line near the top of the file with the other top-level settings to:

Copy Code
rtspTransports: [tcp]

Also, find the "paths:" section near the bottom and replace everything under it with the following:

Copy Code
paths:
  cam:
    source: rpiCamera
    sourceOnDemand: no
    rpiCameraWidth: 1280
    rpiCameraHeight: 720
    rpiCameraFPS: 7
    rpiCameraBitrate: 1000000
    rpiCameraCodec: hardwareH264
    rpiCameraIDRPeriod: 10
    rpiCameraMode: 2304:1296:10:P

A few things worth understanding here:

  • rtspTransports: [tcp] - forces RTSP over TCP rather than UDP. On WiFi, UDP packet loss causes H.264 decode errors in Frigate. TCP eliminates that.

  • rpiCameraBitrate: 1000000 - sets a conservative 1Mbps bitrate. The default of 5Mbps causes instability on the Pi Zero family — don't skip this if you are using a Pi Zero W or Zero 2 W.

  • rpiCameraMode: 2304:1296:10:P - tells the sensor to read a large portion of the IMX708 sensor area and downscale to the output resolution. Without this, MediaMTX defaults to reading only the center 1536x864 pixels, producing a significantly cropped image. That defeats the entire purpose of using the Wide module, so this setting is not optional if you are using a wide-angle camera.

  • rpiCameraIDRPeriod: 10 - controls how often the Raspberry Pi camera encoder inserts an IDR (Instantaneous Decoder Refresh) frame, also known as a keyframe, into an H.264 video stream. A lower value produces more frequent keyframes, improving stream recovery and reducing the time it takes new viewers to begin playback, while a higher value reduces bandwidth overhead at the cost of slower recovery from packet loss or when clients join the stream. The default value is 60, but I set my config to 10 because I was having issues with frame tearing and stream corruption.

  • rpiCameraFPS: 7 - sets the target frame rate, in frames per second (FPS), for the Raspberry Pi camera stream. Higher frame rates provide smoother motion but require more processing power and bandwidth, while lower frame rates reduce resource usage. The default is 30, but I lowered this due to the limited bandwidth of the Pi Zero W.

  • sourceOnDemand - controls whether the camera runs continuously or only when a client is connected. In my testing with a Pi Zero W, I found that when clients disconnect from the video server and the stream stops, it can sometimes cause the MediaMTX service to hang, so this is an important tradeoff to understand:

    • yes - the camera spins up when a client connects and shuts down when the last client disconnects. Lower power draw and less wear on the camera hardware, but if you are integrating the stream directly with Home Assistant and not using the Frigate restream, and Home Assistant tries to grab a snapshot via an automation while no one is actively watching the stream, it will get a black frame or nothing at all while the stream cold-starts. Fine for a camera if you're primarily watching it live.

    • no - the camera runs 24/7 regardless of whether anyone is watching. Snapshot automations in HA will always get a valid frame. The right choice for any camera where you need reliable automation-triggered snapshots, such as a 3D printer monitor, a front door camera, etc.

    Set this based on your use case. The rest of this guide uses yes as the default, but change it to no if you need reliable snapshot support.

Camera Module Variants

The config above is written for the Camera Module 3 Wide (standard or NoIR, 120° FOV). If you're using a different module, here's what changes:

Camera Module 3 (standard or NoIR, 75° FOV) — IMX708

The standard Camera Module 3 uses the same IMX708 sensor and the same sensor modes as the Wide. The config is identical, except for the lens, which gives you 75° instead of 120° FOV. So no changes to the configuration should be needed.

Camera Module 2 (standard or NoIR) — IMX219

The Camera Module 2 uses Sony's IMX219 sensor with different native modes. Use "1640:1232" as the sensor mode, which reads the full sensor area and downscales to your output resolution, the same principle as the Wide module config above:

Copy Code
paths:
  cam:
    source: rpiCamera
    sourceOnDemand: no
    rpiCameraWidth: 1280
    rpiCameraHeight: 720
    rpiCameraFPS: 7
    rpiCameraBitrate: 1000000
    rpiCameraCodec: hardwareH264
    rpiCameraIDRPeriod: 30
    rpiCameraMode: 1640:1232:10:P

The IMX219 has a narrower native FOV than the IMX708 Wide (roughly 62° diagonal), so the cover area per camera is smaller. If you're using the NoIR variant, you may notice a color cast in daylight due to the missing IR-cut filter. This is normal and can be corrected by passing a tuning file to the camera stack, though for a pure monitoring application in low light, it generally doesn't matter.

You can find more information on the official Raspberry Pi camera modules here: https://www.raspberrypi.com/documentation/accessories/camera.html#about-the-camera-modules

As always, run "rpicam-hello --list-cameras" on your Pi first to confirm what sensor modes your specific module reports. The output tells you exactly what to put in rpiCameraMode.

Step 5: Create a systemd Service

Setting MediaMTX up as a systemd service means it starts automatically on boot and restarts itself if it ever crashes, which is exactly what you want for an unattended camera.

Copy Code
sudo nano /etc/systemd/system/mediamtx.service

Paste in:

Copy Code
[Unit]
Description=MediaMTX
Wants=network-online.target
After=network-online.target

[Service]
User=yourusername
ExecStart=/opt/mediamtx/mediamtx /opt/mediamtx/mediamtx.yml
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target

Replace "yourusername" with the username you set in Step 1. Enable and start it:

Copy Code
sudo systemctl daemon-reload
sudo systemctl enable mediamtx
sudo systemctl start mediamtx
sudo systemctl status mediamtx

Step 6: Enable the Hardware Watchdog

A hardware watchdog is worth setting up for any Pi that's going to live somewhere you can't easily reach. Raspberry Pi SBCs have a built-in watchdog timer that'll force a hard reboot if the OS stops checking in due to kernel panics, complete hangs, or whatever. It's a safety net that pure software solutions can't provide.

Enable the watchdog overlay and kernel module:

Copy Code
echo 'dtparam=watchdog=on' | sudo tee -a /boot/firmware/config.txt
echo 'bcm2835_wdt' | sudo tee -a /etc/modules

Then configure systemd to actually feed the watchdog:

Copy Code
sudo nano /etc/systemd/system.conf

Find and uncomment (or add) these two lines:

Copy Code
RuntimeWatchdogSec=15
RebootWatchdogSec=2min

With RuntimeWatchdogSec=15, the hardware watchdog triggers a forced reboot if the system goes 15 seconds without checking in. Long enough to ride out brief load spikes, but short enough to recover quickly from a real hang.

Step 7: Reboot and Verify

Apply everything with a reboot:

Copy Code
sudo reboot

Once it's back up, verify if the mediamtx service is running:

Copy Code
sudo systemctl status mediamtx

You should see MediaMTX running, with the rpiCamera component active. You can also open the network stream directly in VLC on any machine on your network:

Copy Code
rtsp://[Pi IP address]:8554/cam

If you get a clean stream in VLC, you're ready to add the new camera stream to Frigate.

Step 8: Add the Camera to Frigate

On your Frigate host, add this to config.yml under cameras:

Copy Code
cameras:
  outdoor_camera:
    ffmpeg:
      inputs:
        - path: rtsp://[Pi IP address]:8554/cam
          roles:
            - detect
            - record
    detect:
      width: 1280
      height: 720
      fps: 7

Restart Frigate to pick up the change. If you have Frigate tied into your Home Assistant server, the camera will also appear as a new entity in Home Assistant through the existing Frigate integration and can be easily added to any dashboard as a camera card. However, you will have lower latency in Home Assistant if you add the camera's direct stream to your server rather than using the restream provided by Frigate.

Step 9: Add the Camera Directly to Home Assistant

If you're not using Frigate, or want the stream available in HA independently of Frigate, you can add the MediaMTX RTSP stream directly to Home Assistant using the built-in Generic Camera integration. This gives you a live view card and snapshot capability without the additional latency of an NVR in the middle.

In Home Assistant, go to Settings → Devices & Services → Add Integration and search for Generic Camera. When prompted, enter:

  • Stream source URL: rtsp://[Pi IP address]:8554/cam

  • Still image URL: leave blank — HA will pull stills directly from the stream

  • Username / Password: leave blank

  • Verify SSL: off

Give the camera a name and save. It shows up as a camera entity in HA and can be added to any dashboard as a camera card, used in automations for snapshots, or referenced in notifications.

Note that if you have "sourceOnDemand: yes" in your MediaMTX config, snapshot automations may return a black frame when the stream isn't already active. See the note in Step 4 about this tradeoff.

Step 10: Physical Assembly

3D Printed Mount

I designed and printed a custom mount using PETG to securely hold the Raspberry Pi Zero W and the camera module inside the enclosure I selected for my camera. This setup ensures that the camera lens is correctly aligned with the dome window. PETG is an ideal material for this project because it performs better than PLA in handling the temperature fluctuations typical of an outdoor enclosure. The Raspberry Pi and camera board are attached to the mount using #2 screws. There is also a camera cover to mitigate reflections of the camera or the Pi's power LED on the camera dome.

image

3D printed camera mount in enclosure

3D printed camera cover

Assembled camera enclosure

Download 3D model files

Cable Entry

Power comes in through a cable gland, which makes a weatherproof seal around the USB cable. Drill a hole sized to your gland's thread, feed the cable through, tighten the gland from outside, and connect to the Pi's micro-USB power input inside. The enclosure has a built-in gasket at the dome window and enclosure cover, so nothing extra is needed there. 

Mounting

Mount the enclosure where the 120° wide-angle lens gets a clean view of the area you want to cover. Secure it with fasteners appropriate for your application.

Conclusion

Once it is up and running, the camera appears in Home Assistant as a fully integrated Frigate entity, featuring live view, recording, motion detection, and event clips, all operating entirely on your own hardware without any external dependencies. The Pi Zero W pulls around 300–400mA at 5V under load, so it's efficient enough to run continuously without any concerns. In testing, MediaMTX used around 11% CPU at steady state on the Pi Zero W, with a 46°C operating temperature and comfortable memory headroom, which is well within the sustained operating envelope of this hardware.

The setup scales well, too. Anywhere you have power and WiFi, you can spin up another camera with the same process: add a stream in Frigate, get a new entity in Home Assistant, done. No new subscriptions, no new accounts, no new cloud services.

I've used this configuration to add several new cameras to my Home Assistant server and Frigate, and they have all been running autonomously for several weeks without any issues once I stabilized my Pi Zero W' board's WiFi connection following the instructions here: How to Keep a Raspberry Pi Zero W or Zero 2 W Reliably Connected to WiFi

Mfr Part # WP-CR-80-27
WP CAMERA CASE 80 DOME 27MM
Entaniya
$116.37
View More Details
Mfr Part # 1551ATS100
SHT MTL SCREW PAN PHIL #2 100/PK
Hammond Manufacturing
Mfr Part # SC1176
SBC 1.0GHZ 4 CORE 512MB RAM
Raspberry Pi
Mfr Part # SC1226
RASPBERRY PI CAMERA 3 WIDE NOIR
Raspberry Pi
Mfr Part # 114992065
RPI ZERO CAMERA CABLE 150MM
Seeed Technology Co., Ltd
Mfr Part # HYPER-PETG BLACK 1KG
HYPER-PETG BLACK 1KG
Creality 3D
Mfr Part # 5308 711/SET
CABLE GLAND 3.5-10MM PG11
Altech Corporation
Add all DigiKey Parts to Cart
Have questions or comments? Continue the conversation on TechForum, DigiKey's online community and technical resource.