How NovaVMS records a clip
NovaVMS does not record continuously. Instead, the gateway keeps the last few seconds of every camera in memory and only writes a file when something triggers a clip. Triggers come from motion, AI tags, manual operator action, and alert rules. The file then leaves the gateway and lands in your cloud storage bucket (per R3 and D49).
Why this works this way
The naive approach is to record every frame of every camera to disk, forever. Verkada does this on their cloud-connected cameras because they own the hardware and the storage tier; it is less cheap if you are self-hosting and already paying for cloud egress. For a 30-camera site, continuous recording at 4 Mbps per camera is around 30 GB per day before any event tagging. Most of those bytes are of an empty corridor.
Decision R3 records the alternative we chose. The gateway keeps a Go in-memory ring buffer of RTP packets, GOP-aligned, sized to the camera’s pre_event_buffer_sec setting (default 5 seconds, max 10). Nothing touches disk until a trigger fires. When a trigger fires, the ring buffer flushes — so the resulting clip includes the N seconds before the trigger — and the recorder keeps writing packets until the post-event duration is satisfied. The muxer assembles those packets into an MP4 in the gateway’s local spool directory.
Triggers arrive from four sources.
- ONVIF motion events from the camera itself, delivered over PullPoint (per D7).
- AI tags produced by the cloud pipeline after a snapshot or clip is analysed. These close the loop: the camera fires an event, the cloud tags it, an alert rule keyed on the tag records a follow-up clip (rare in v1, but supported).
- Manual operator trigger via the Record Clip button on the live view (per D49). Rate limited to one trigger per camera per five seconds so a panicked operator cannot flood the spool.
- Alert rules that specify a
record_on_triggeraction.
The spool is local so the gateway can continue working when the cloud is unreachable. Per D16, the gateway buffers up to one hour of completed clips on disk if the cloud connection is down, and drains them in order once connectivity returns. Clips older than an hour are logged and dropped — we preferred that ceiling over unbounded disk growth on an unattended edge device.
Upload to cloud storage (GCS, S3, or MinIO) uses presigned URLs the cloud backend issues on demand. The gateway uploads directly to the bucket without the clip passing through the cloud backend, which keeps egress off the backend’s NIC. Once uploaded, the gateway deletes its spool copy.
We explicitly refused to depend on FFmpeg anywhere in this path. The ring buffer, the muxer, and the upload are all pure-Go code paths inside the gateway binary. That keeps the gateway a single static binary, survivable on a headless Pi, and avoids the long tail of FFmpeg version mismatches.
What this means for you
- A clip you trigger with the Record Clip button includes roughly the five seconds before you clicked, not only the seconds after.
- If the gateway is online, a clip appears in the Events feed within seconds of the trigger — usually under ten for short clips.
- If the gateway is offline (red dot on the Gateways page), clips are still being recorded locally. They appear in the feed when the gateway reconnects, in trigger order.
- If the gateway is offline for more than an hour, the oldest clips are dropped rather than blocking new ones. Check the gateway log for dropped-clip warnings.
- Clip length is set by the trigger type and the camera’s
post_event_buffer_secsetting. The manual Record Clip button uses a fixed duration, not a start-stop toggle — you cannot “stop” it once it has started. - Clips never touch the cloud backend’s compute path. If the cloud is unreachable during upload, the clip waits in spool and retries.
Where to go next
- Record a clip manually — the operator how-to
- Retention policy — how long clips stay in storage after upload
- Alert rule schema — how an alert rule specifies a recording as an action