Now on GitHub · v0.2.0 alpha

Follow the trail.

CrumbVMS is a self-hosted video management system built around the seat you review footage from: operator-grade timeline scrubbing, a fast multi-camera wall, polished native clients. The kind of software that runs control rooms, on your own hardware. Yours.

If you have spent time in a commercial control-room VMS, the investigate workflow will feel familiar.

Free and open source, AGPL-3.0-or-later. It's early (v0.2.0 alpha); bugs and feedback go through GitHub Issues and Discussions.

11cameras under test
Monthsrecording nonstop
H.265scrubs smoothly
Freeopen source, AGPL
CrumbVMS desktop client showing the Clips view for the Backyard camera. A grid of nine motion clips from the last 24 hours, each thumbnail with a recording icon and timestamp.
Why this exists

I built this because the operator layer was missing.

I spent about thirty years in IT and worked with most of the enterprise NVRs along the way. One commercial VMS, the kind that runs control rooms, got the client experience right: grab the timeline and scrub a dozen cameras frame by frame, hunting for a gray blob of pixels in grainy 3 a.m. footage, and the software just keeps up. Then it revoked my test license and removed its free camera tier, and I found there was nothing self-hosted that felt like that. The open-source world had solved detection brilliantly, Frigate does it better than anything I could build. Nobody had built the seat you review it from. So I built it: a multi-camera wall, a timeline you can actually scrub (H.265 included), native clients on every device. Three surfaces, one backend.

Full transparency

The whole picture, no spin.

I'm an IT engineer with thirty years in the field, building CrumbVMS on my own time. It runs my own house right now: eleven cameras, multiple storage volumes, recording day in and day out for months. About 90% of where I want v1 to be.

Most polished: the recording server, the Windows desktop client, and the Android app. Newer and rougher: the macOS and iOS apps are a shared native SwiftUI codebase, the Mac app runs today; the iOS app works on my own iPhone but isn't something I can hand you yet (see the clients below). The remaining 10% is the boring, important part: hardware variety, different cameras, CPUs, drives, network topologies. That's exactly what running it on hardware that isn't mine, and GitHub Issues, are for. I've run 30+ days without losing footage on my own setup; I can't promise that for yours yet.

Full transparency: I'm using AI to build CrumbVMS itself, and to build this page. The architecture, the decisions, and the testing are mine, AI is the power tool that lets a side project move at this pace.

My long-term goal is to make CrumbVMS installable by anyone, click an installer, point it at your cameras, done. We're nowhere near that today. Right now CrumbVMS is for technical users comfortable bringing up a Docker container on a Linux host; a browser-driven first-run wizard handles the rest.

On licensing: CrumbVMS is free and open source, under AGPL-3.0-or-later, every client, no camera limits. Voluntary sponsorship is welcome if it keeps the lights on, but it's not why I'm here.

Timeline

Scrub the day. Click an event. Find the moment.

Recording segments, motion events, and per-object detection icons all live on one bar. Hour ticks at top, transport at the right, jump-by-time chips for fast triage. From a 24-hour overview down to a 60-second slice. Same interaction.

CrumbVMS timeline strip showing recording bars, hour ticks, transport controls, jump and zoom controls, and a vertical playhead with a detection icon cluster (person plus shield badges) at 16:28:14.
How it works

The operator layer your cameras have been waiting for.

CrumbVMS is the recorder, the storage policy engine, the timeline, the live wall, and the native clients you actually use day-to-day. One backend, three ways in.

Read plates, keep them local

Two OCR engines you pick per camera: Frigate's native LPR, or Crumb's own CPU-only fast-alpr worker for the wide entry angles Frigate misses. Both run on your box, no cloud, and Crumb's own worker needs no GPU. Set a camera to Both and the desktop Benchmark screen puts them head to head so you can see which one actually wins on your cameras. Watchlist alerts, an ignore list, and fuzzy matching that treats confusable characters (O/0, I/1, B/8) as free. Name a plate once and the name follows it everywhere, and any sighting exports as a one-page PDF report.

Your Home Assistant, on the video

Link a camera to your HA entities and drag their live state onto the frame as badges. The front-door sensor reads "Open" right where the door is; a temperature badge shows its real value and units. As of v0.2.0 those badges are interactive: tap the porch light to switch it on, or open its card and drag a slider for brightness, cover position, or fan speed, without leaving the camera. Self-hosted, REST-polled, authored in the console. State honesty is built in: an unknown or stale entity goes grey and dimmed, never a false "closed."

Record everything, reliably

Rust-based recorder with per-policy size caps and a Postgres-backed segment index as the single source of truth. In motion mode, footage buffers in RAM and only persists when motion fires, idle time is never written to disk. Everything lands as plain MP4 files on your disk, in an open schema.

Scrub the day in one stroke

Hour-by-hour timeline with crumb-trail recording segments, motion event dots, per-object detection icons, and a playhead that snaps to the next event with one keypress.

Native clients everywhere

Desktop: Flutter + libmpv, the full investigation surface, built to live full-screen 24/7. Android: Kotlin/Compose, pull footage from anywhere, adaptive Auto / Full / Data saver quality plus a low-bandwidth snapshot mode for cellular. macOS: native SwiftUI, a download you run today (rougher edges). iOS: same codebase, built and working on my own phone, but Apple only lets you distribute an iOS build through TestFlight, which needs a paid developer account I haven't set up yet. So iOS works; I just can't hand it to you yet. Being straight about that. Web: admin console at /admin. H.264 and H.265 both play smoothly, and for the rare camera whose H.265 main a phone's decoder rejects, an opt-in on-demand server repair serves an HD stream it can actually play.

In practice

What you'll actually use. No mockups.

Captured from the desktop build I run on my own hardware.

View setup screen. A saved-views list on the left (All Cam's, Inside, Outside) with quick-layout presets, column and row steppers, an Edit layout button, and draggable special tiles (Carousel, Hotspot, Image, Clock, Text, Detections, Web). The canvas on the right shows a hero layout: a large Front Yard pane beside LPR and Driveway, with Front Door, Backdoor, Backyard and an auto-follow Hotspot along the bottom.

Build the wall you want

Start from a preset or set your own rows and columns, then merge and split boxes until the layout matches how you actually watch. Drag cameras into panes, or drop in a special tile: carousel, motion-following hotspot, clock, text, detections, a web page. Name each view, give it an icon, switch with one click. Saved per device.

Export tab. Build a list of clips (one camera + one time range each), choose MP4 H.264 output, optional timestamp burn-in, optional AES-256 password-protected ZIP, then export the whole batch at once.

Export a list. Encrypt it if you want.

Add multiple clips from multiple cameras, then export the whole list at once. Output as plain MP4 files or as a single AES-256 ZIP with a password. Burn the timestamp in. Lands in your Downloads folder.

Motion tuning panel. Live camera preview with drawable exclusion zones over trees and edges, grid overlay, detector picker (Census, Frame diff, MOG2, Optical flow, Ensemble), grid size and threshold sliders.

Tune motion in the actual scene

Draw exclusion zones right on the live image. Swap detectors (Census, Frame diff, MOG2, Optical flow, Ensemble) per camera. Watch the sensitivity and floor live as you tune.

Server management. Left sidebar shows Cameras with online status, a Motion Cameras group, Recording Profiles (Default, Motion), Storage volumes (16TB Spinner, 2TB NVMe), Roles (Administrator, Viewer), Users, Integrations, Notifications, and Server & Streaming. The main panel shows a custom Viewer role with capability checkboxes and per-camera access.

Roles, groups, and per-camera access

Custom roles with explicit capabilities: Playback, Clips, Export, PTZ control, Manage views. Camera access by individual camera or by group. Bookmarks scoped per-user.

Timeline detail. Selection highlighted in amber between 16:22 and 16:24 with two detection icons (shield badges and a person glyph), right-click context menu with Set export start here, Set export end here, Export selection, Clear selection.

Right-click the timeline. Done.

Drop a start and an end on the timeline, send the selection straight to the export list. Per-object detection icons from Frigate stay on the bar so you can see exactly what you're picking before you pick it.

PTZ builder edit mode on a live camera tile. A cluster of placed control buttons (zoom in and out, focus near and far, autofocus, iris open and close) sits over the top-left of the frame, each with a small remove handle. A toolbar along the bottom offers D-pad, arrows, Home, Zoom, Focus, AF, Iris and Preset buttons to add, plus Snap, Clear all and Done.

Put the PTZ controls where you want them

Edit mode turns the live tile into a canvas. Drag ONVIF d-pad, zoom, focus, iris, home, and preset buttons anywhere on the frame, resize them, snap them to each other, rename them. Undo and redo while you fiddle. Saved per camera on the desktop client.

Engine Benchmark screen comparing Frigate and Crumb ALPR over 24 hours on one camera. Summary cards show reads, passes seen, hit rate and average confidence for each engine, with 156 vehicle passes and an agreement percentage in the middle. Below, one row per pass shows each engine's read side by side with a verdict chip. Plate numbers are redacted in this screenshot.

See which plate engine suits which camera

Set a camera to run both engines and the Benchmark screen scores them side by side over a window you choose: reads, hit rate, average confidence, and every pass where they agreed or differed, with the crops to check it yourself. The two engines are built for different jobs, so the answer moves with the camera, the angle, and the light. The camera above is a wide entry-angle cam, which is exactly what Crumb's own engine was added for; on a dedicated head-on plate camera the numbers can land the other way. The point is that you get to measure it on your hardware instead of guessing.

Smooth live and smooth playback, including H.265

Most web viewers struggle with H.265, which is now the default on serious cameras. CrumbVMS hands frames straight to libmpv on desktop and Media3 on Android, so 4K H.265 plays without judder, without server-side transcoding, in both live and recorded playback. A pre-generated preview proxy makes revisiting a spot on the timeline a cached read instead of a fresh 4K decode, which is what keeps scrubbing instant.

Operator-grade timeline

Hour ticks, recording segments as crumbs, motion events as dots, per-object detection icons (person / car / animal / package), pinch-to-zoom, and a snap-to-event playhead.

Adaptive motion detection

Illumination-invariant census-transform foreground with a percentile-histogram floor, so headlights and moving shadows stop tripping recording at 3 AM. Draw exclusion zones for the wind-blown trees. Trial it in shadow mode on real footage before flipping a camera to motion-only.

Recording policies, no guesswork

Named recording policies with live / archive storage tiers; every camera is on exactly one policy, so there's no inheritance chain to reason about. Per-policy size caps and free-space headroom, and an optional maximum-retention cap that deletes footage past N days, protected bookmarks survive it.

Customizable views

Dedicated Setup mode. Drag any camera into any pane, save the layout per device, switch with one click. Tab persistence so the wall comes back the way you left it.

On-video PTZ control

Place ONVIF d-pad, zoom, home, focus, iris, and preset buttons anywhere on the live tile. Resizable, snap-aligning, renamable. Per-camera, persisted on the desktop client.

Alerts where you already are

Motion and detection notifications to Discord, Slack, Telegram, Pushover, ntfy, or a generic webhook, snapshot attached where the service takes one. Named channels with alert text you write yourself, per-camera rules, quiet hours, snoozes, and presence gating so it can stay quiet while you are home.

On-demand everything, sips power

Nothing is transcoded ahead of time. Footage records straight to disk, and smaller streams and clips are generated the moment something asks for them, cached, then evicted. The local plate reader is CPU-only and motion-gated. A recorder runs 24/7, so it should sip power, not cook a GPU transcoding footage nobody is watching.

Wanted

Storage tiering: transcode old footage down

After a configurable window, older recordings transcode down to 1080p or 720p, freeing bytes for the next stretch of high-res. Same scene, smaller bucket, same operator surface.

Wanted

Privacy masks, per camera and per role

Draw black-out zones over neighbors' windows or license plates. Apply globally or scoped per role, so a Viewer sees the redacted footage and an Admin sees the raw.

Wanted

Federated multi-server

When one box isn't enough: federate two or more CrumbVMS hosts, see all their cameras in one wall, query timelines across all of them at once.

Wanted

Overlay mode: sit on top of your existing Frigate

Use CrumbVMS as the operator layer next to a Frigate setup you already run: ingest the same cameras through Frigate's go2rtc, keep Frigate doing detection, and let Crumb record its own copy with the multi-camera wall, H.265 scrubbing, export list, and RBAC on top. One honest constraint: the frame-level scrub is a record-time property, so Crumb records its own footage rather than reading Frigate's existing archive. Your Frigate keeps its recordings; Crumb builds its trail alongside.

Tell me which of these matters most to you in GitHub Discussions.

Reference setup

My hardware. My real numbers.

Use this as a sanity check, not a requirement. CrumbVMS is designed to scale down to weaker boxes too. My own testing is on Intel + NVIDIA; AMD (Ryzen / Radeon) should work, CPU decode is vendor-neutral and VAAPI covers AMD iGPUs, but it isn't tested yet.

Recorder host

The box doing the work

CPU
Intel Core Ultra 7 265K · 20 cores / 20 threads
Memory
64 GB
GPU
NVIDIA Quadro RTX 4000 (8 GB) · Intel UHD 770 iGPU · Coral USB TPU
GPU usage
Not in use for motion decode. CPU wins on power at this scale (see benchmark below).
Cameras
11 mixed ONVIF / RTSP, indoor and outdoor, mostly H.265, includes 4K mains
Storage
Multiple disks. H.265 footage (most cameras) at roughly half the bitrate of H.264.
OS
Ubuntu, Docker Compose stack
Windows desktop client

What I sit in front of

CPU
Intel Core i5-14500 · 14 cores (6 P + 8 E) / 20 threads
Memory
64 GB
GPU
NVIDIA GeForce RTX 3070 (8 GB) · Intel UHD 770 iGPU
Wall load
11 sub-streams (the everyday view): ~1% CPU, ~22% NVDEC, ~1 GB RAM
Notes
libmpv hands H.264 and H.265 straight to NVDEC. The wall defaults to sub-streams; main only loads when a tile is maximized.
Power benchmark · 2026-06-23

CPU motion decode beat NVDEC by 30%, at the wall meter.

I ran a controlled A/B on the recorder host: motion-detection decode on CPU versus NVDEC, 11 low-resolution sub-streams. NVDEC's fixed activation cost dwarfs the actual decode work at this scale, so CPU wins on power until camera count or resolution scales up.

MetricNVDEC (cuda)CPUDelta
Wall meter105 W73 W−32 W (−30%)
GPU power36.6 W8.8 W (idle)−27.8 W
Recorder CPU~26%~33%+7% (about 0.08 of one core)
GPU temperature51 °C35 °C−16 °C

Decode mode is admin-selectable per server: auto / cpu / cuda / vaapi (VAAPI drives an Intel iGPU instead of a discrete card), and the admin console shows the requested-vs-active decode per camera. Changes hot-reload without a full restart.

Testers wanted

Help me find what works, and what doesn't.

No application, no invite, no waiting list: the code is public, so anyone can grab it and run it. The smoothest path today is a Linux host running the Docker stack, with Windows on the desktop and Android on the phone, the most-tested clients. The macOS app is ready to try too, just rougher, an unsigned download you open past Gatekeeper. iOS is the honest exception: it's built and runs on my own iPhone, but Apple requires a paid developer account before I can distribute it (even through TestFlight), so I can't hand it to you yet.

I'm looking for two things in a tester: you're comfortable with the possibility of losing footage (I've run 30+ days without an incident on my own setup, but I can't promise that for yours), and you're happy to share feedback about what worked, what didn't, and what surprised you.

What you'd need

  • Linux x86-64 host for the Docker stack. A NAS, a mini PC, an old desktop, a Proxmox VM, all fine.
  • Docker + Compose v2, with your user able to run docker ps without sudo
  • Storage. Cameras eat terabytes; ~2 TB is a reasonable starting point.
  • A few ONVIF / RTSP cameras. A mix of brands is even more useful than one consistent set.
  • LAN only to start. Never open ports to the public internet.
  • A Windows desktop to run the Smart Client, and/or an Android phone for the mobile app

NVIDIA GPU is optional (CPU motion detection is the default and benchmarks fine for typical residential loads). x86-64 + Docker is the supported floor today. Tested on Intel + NVIDIA, AMD (Ryzen / Radeon) testers especially wanted: it should work, but I haven't verified it.

What I'd ask in return

  • Run it for a few weeks. Notice when something feels weird. Tell me.
  • Be patient with bugs. There will be some. There may be migrations between releases.
  • Send a quick note when something works really well too. That's useful signal.
  • Let the app contribute your cameras. Adding one can file a one-click "contribute this camera" report prefilled with what it detected (make, model, firmware, codecs), and every entry makes the compatibility list better for the next person.
  • Be honest if it's not for you. I'd rather hear that than have you ghost.

Get it on GitHub, stand it up (the README walks you through it, or hand the AI install guide to your coding assistant), and run it for a while. One honest ask first: read the Alpha Tester Terms and the responsible-use guide. CrumbVMS is pre-release software provided as-is with no warranty, you shouldn't rely on it as your only security system, and lawful use is your responsibility. Recording people, especially audio, is regulated in many places.

Get it on GitHub

Then tell me what broke, and what didn't. Bugs, rough edges, and confusing install steps go to GitHub Issues; questions, ideas, and "here's how it ran on my hardware" notes go to GitHub Discussions. Prefer email? [email protected] reaches me directly. I read every one.

No NDA, no account with me, nothing to sign up for. CrumbVMS is free and open source (AGPL-3.0-or-later).

Integrations

Bring your own Frigate. Bring your own Home Assistant.

CrumbVMS does not bundle Frigate, and object and face detection stay Frigate's job. Motion detection is built in, and plates are the one thing Crumb will read itself, with its own local engine. If you already run Frigate, point CrumbVMS at your MQTT broker and map each camera to its Frigate name. That's the entire integration.

  • FRIGATE_MQTT_URL in .env or the admin UI, mapped per-camera to its Frigate source_camera_name
  • Events flow in over MQTT; CrumbVMS tolerates schema drift (we live-fixed a sub_label regression in production)
  • No broker? The bundled mosquitto is optional and right there

If FRIGATE_MQTT_URL is empty, the entire detection subsystem stays disabled. Zero magic, zero coupling.

And your Home Assistant.

Same principle: HA stays your automation brain, Crumb just gives it a seat on the glass. Connect it once and link a camera's entities from the admin console or the desktop app, no YAML.

  • HA sensors act as an extra recording trigger, so a real door or motion sensor arms recording alongside pixel motion and Frigate's detections
  • Drag entity badges onto the live frame where the thing physically is: the contact sensor on the door, the light on the porch, the motion sensor over the driveway, each showing live state
  • Badges are interactive on desktop, Android, macOS, and iOS: tap to act, or open the card for a brightness, position, or speed slider
  • Acting on a device is off until you allow it: a separate role capability gates control, and each link can require a confirmation tap or expose only the actions you choose

Leave Home Assistant unconfigured and none of it runs. Same zero-coupling rule as Frigate.

New in v0.2.0

Read the house from the camera. Now act on it too.

Crumb could already show a linked Home Assistant entity's state on the live wall, but that was as far as it went, and setting the links up was a thin, awkward surface. This release rebuilt the authoring layer and made the badges interactive. You still keep Home Assistant as the automation brain; Crumb is just one more place to see it and touch it.

Live front-yard camera view. A lightbulb badge labeled Front Island Light sits on the lawn where the light is. Its detail card is open beside it showing the state On, the entity id light.front_island_light, On and Off buttons, and a brightness slider at 100 percent.

Tap the light. Drag the slider.

A single tap actuates a simple device such as a light or a switch. Covers and locks open a card first, so you don't unlatch a door by brushing the glass. Dimmable lights, covers, and fans get a slider for brightness, position, and speed, and it commits once when you let go rather than spamming your instance while you drag.

Editing the Home Assistant overlay on a backdoor camera. Badges for Yard, Floodlight, Traeger and Patio door are placed around the live frame. The style panel for the Traeger switch is open with an icon dropdown set to Fire/smoke, a Dot or Pill shape toggle, an accent color swatch grid, separate off and on background colors, size and opacity controls, and checkboxes for pinning state text, pinning last-changed time, and outline plus shadow.

Make the badge look like the thing

Place a badge where the device physically sits, then pick its icon from one canonical set, its shape, its accent color, and separate backgrounds for off and on. Size, opacity, pinned state text, pinned last-changed time. The icon list is shared and enforced server-side, so what you pick on the desktop shows up the same on your phone instead of quietly falling back.

Home Assistant section of a camera's settings in the admin console. Five linked entities are listed, each with a role dropdown (Control, Sensor status only, Motion triggers recording), an editable label, an optional device class, the Home Assistant entity id, and per-row Icon and style, Controls, and Remove buttons. Buttons at the bottom add a motion sensor, a sensor reading, or a control.

Link the entities in the console, once

Home Assistant now has its own area in the admin console instead of a scattering of per-camera fields, with a global view of every link and a flag on any link whose entity has disappeared from your instance. The picker covers every controllable domain plus numeric sensors, and each link carries its own role, label, device class, icon, overlay style, and control rules. Same screen on the desktop app if you'd rather stay there.

All of it is opt-in. No Home Assistant URL and token means none of this subsystem runs at all, and even with it configured, acting on a device needs a role capability that is off by default.

Self-hosted by design

It all runs on your own hardware.

CrumbVMS is a Docker stack you bring up on a Linux box, a NAS, a mini PC. Anywhere you can run a container. Recordings are plain MP4 files on a disk you own, in an open schema. The configuration plane is a web UI on your LAN, and the native clients talk to your server. And yes: no cloud, no account, no telemetry, and nothing leaves your network unless you make it. That is how self-hosting works, not the reason to run it.

Secrets are generated for you on first boot. The admin console walks you through creating your administrator account. Every key is documented and the Postgres schema is open, so nothing about how it works is hidden from you.

Under the hood

An honest stack, no surprises.

Recorder
Rust
API
Rust · axum
Database
PostgreSQL
Restreaming
go2rtc
Detection (BYO)
Frigate · MQTT
Desktop
Flutter · libmpv
Android
Kotlin · Compose · Media3
Admin
Web console · served by the API
Deploy
docker compose up -d
Latest

What we shipped recently.

A running log of releases, fixes, and design notes. RSS · all updates

See all →
Shipped

v0.2.0: the Home Assistant half-build, finished

Crumb could put a Home Assistant entity's state on the live wall, but you could not do anything with it, and setting the links up was a thin, awkward surface. v0.2.0 rebuilds the authoring layer and makes the badges interactive: tap a light, drag a slider, read a sensor's real value off the frame. Plus adaptive live-wall quality on every client and human-readable plate names.

BackendDesktopAndroidiOSWeb
releasehome-assistantlprlive-wall
Shipped

v0.1.1: a hardening pass, run by an adversarial audit program

v0.1.1 is a hardening release. An intensive multi-model audit swept the recorder, the API, every client, and the seams between them, finding each issue, trying to disprove it, then fixing it. Sixty-plus changes, almost none of which change what Crumb does, all of which change how much you can rely on it.

BackendDesktopAndroidiOSWeb
releasesecurityreliabilityaudit
Shipped

One-page plate report, exported to PDF from the desktop

Any single plate sighting exports as a clean one-page PDF from the desktop client, with the plate, the time, the camera, the full frame plus crop, and a recent-sightings dossier. For handing a sighting to someone without screenshotting the app.

Desktop
lprexportreports
Shipped

Home Assistant entity badges, painted on the live video

Link a camera to your Home Assistant entities and drag their live state onto the video as badges. Self-hosted, REST-polled, configured in the console. State honesty is built in.

BackendDesktop
home-assistantoverlayintegrations

The trail starts on GitHub.

The code, the install guide, and the conversation all live in one place now. Grab it, run it on your hardware, and tell me what you find in Issues and Discussions.

Get it on GitHub

crumbvms.com · self-hosted video management