NovaVMS keeps different data types for different lengths of time. This page lists every default and how to change it.
Default retention per data type
| Data type | Default retention | Configurable? | How to change |
|---|
| Event clips (MP4) | 7 days (per-camera default; hard cap 90 days) | yes, per camera + env var | Server-side default is RETENTION_DEFAULT_DAYS=7; admins set per-camera overrides clamped to RETENTION_HARD_CAP_DAYS=90. See cloud/internal/config/config.go:90-93. |
| Event snapshots (JPEG) | Same as the parent event clip | yes, per camera | Lifecycle is bound to the clip’s retention. |
| Event thumbnails (JPEG, 320 px) | Same as the parent event clip | yes, per camera | Lifecycle is bound to the clip’s retention. |
| Event rows (DB) | Same as the parent event clip | yes, per camera | Starred events exempt — kept indefinitely until unstarred. |
| Audit log entries (org) | 90 days | yes, env var | AUDIT_RETENTION_DAYS on the cloud server. Daily sweeper at cloud/cmd/server/main.go:269-289. |
| Platform audit log entries | 365 days | yes, env var | PLATFORM_AUDIT_RETENTION_DAYS on the cloud server (default 365). Daily sweeper at cloud/cmd/server/main.go:448-468. |
| Webhook deliveries | Query LIMIT 100 per webhook (no active sweeper) | no | The “100 per webhook” cap is enforced as a query LIMIT on the delivery-history endpoint, NOT by an active retention sweeper. Old delivery rows accumulate in the table. See Phase 7 GAP-7-16. |
| In-app notifications | 500 per user | yes, env var | MAX_NOTIFICATIONS_PER_USER. |
| Refresh tokens | 7 days (30 with “Remember me”) | no | Fixed. |
| Share links | 7 days | yes, per link | Link creator picks 1 h, 24 h, 7 d, or 30 d at creation. |
| Reports (PDF) | 90 days | yes, env var | REPORT_EXPIRY_DAYS on the cloud server (default 90). expires_at set per report; retention sweeper deletes both DB row and object-storage file. |
Storage locations
| Data type | Location | Backup cadence |
|---|
| Event clips, snapshots, thumbnails | Object storage (STORAGE_BACKEND — local, gcs, or s3) | Follows the bucket’s own backup/versioning policy. |
| Event rows, audit entries, user data | PostgreSQL | Per operator’s DB backup policy. |
| In-app notifications | PostgreSQL | Same. |
| Gateway pre-event ring buffer | Gateway RAM | Not backed up — transient. |
| Gateway local disk cache | Gateway disk, up to disk_quota_mb (default 10 GB) | Not backed up. Uploaded to cloud on success. |
What happens at expiry
- Event clip or snapshot: the object storage file is deleted first, then the
events row transitions clip_status from ready to expired. Starred events are exempt — they are kept until unstarred.
- Event row: deleted after its clip expires. Retention cleanup runs on a configurable interval (
RETENTION_INTERVAL, default 24h per cloud/internal/config/config.go), batched to RETENTION_BATCH_SIZE rows at a time (default 5000). The sweep aligns to RETENTION_ALIGN_HOUR_UTC (default 03:00 UTC).
- Audit log entry: purged daily in batches. No grace period.
- Webhook delivery: pruned when the per-webhook 100-entry cap is exceeded.
- In-app notification: oldest notifications dropped when the per-user cap is exceeded.
- Share link: rejected with
410 Gone after the expiry timestamp. Row pruned on next cleanup sweep.
- Camera, gateway, site, user: soft-deleted —
deleted_at set, row preserved for audit reference, name freed for reuse.