Skip to content

Retention policy

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 typeDefault retentionConfigurable?How to change
Event clips (MP4)7 days (per-camera default; hard cap 90 days)yes, per camera + env varServer-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 clipyes, per cameraLifecycle is bound to the clip’s retention.
Event thumbnails (JPEG, 320 px)Same as the parent event clipyes, per cameraLifecycle is bound to the clip’s retention.
Event rows (DB)Same as the parent event clipyes, per cameraStarred events exempt — kept indefinitely until unstarred.
Audit log entries (org)90 daysyes, env varAUDIT_RETENTION_DAYS on the cloud server. Daily sweeper at cloud/cmd/server/main.go:269-289.
Platform audit log entries365 daysyes, env varPLATFORM_AUDIT_RETENTION_DAYS on the cloud server (default 365). Daily sweeper at cloud/cmd/server/main.go:448-468.
Webhook deliveriesQuery LIMIT 100 per webhook (no active sweeper)noThe “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 notifications500 per useryes, env varMAX_NOTIFICATIONS_PER_USER.
Refresh tokens7 days (30 with “Remember me”)noFixed.
Share links7 daysyes, per linkLink creator picks 1 h, 24 h, 7 d, or 30 d at creation.
Reports (PDF)90 daysyes, env varREPORT_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 typeLocationBackup cadence
Event clips, snapshots, thumbnailsObject storage (STORAGE_BACKEND — local, gcs, or s3)Follows the bucket’s own backup/versioning policy.
Event rows, audit entries, user dataPostgreSQLPer operator’s DB backup policy.
In-app notificationsPostgreSQLSame.
Gateway pre-event ring bufferGateway RAMNot backed up — transient.
Gateway local disk cacheGateway 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.
  • Chain of custody — why exported clips remain valid after source retention expires
  • Audit actions — what retention cleanup writes to the audit log