Skip to main content

Storage

System
Storage

Created: 6/12/2023
Updated: 8/24/2026

The Storage component manages Viserons database and how to store files. It helps you to organize segments, recordings, timelapses and snapshots across multiple storage locations.

Files can be retained based on age and/or space used so that you never run out of storage.

Configuration​

Configuration example
/config/config.yaml
storage:
recorder:
tiers:
- path: /ssd/tier1
events:
max_age:
days: 1
continuous:
max_age:
days: 1
- path: /hdd/tier2
events:
max_age:
days: 7
snapshots:
tiers:
- path: /config/tier1
max_age:
days: 1
tip

The above example will store recordings/events on /ssd/tier1 for 1 day, and then move them to /hdd/tier2 for 7 days.

It will also store continuous recordings on /ssd/tier1 for 1 day, after which they are deleted.

storage​map required
Storage configuration.
caution

Be aware that the age of a file is calculated from the time it was created, not the time it was moved to the current tier. It is NOT additive. In the Configuration example above, a file will be completely deleted after 7 days, not 8 days.

Tiers​

Tiers is list of directories that Viseron can use to store files.

Viseron will always write to the first tier. max_size/max_age then decides when files are moved to the next tier or deleted.

It is of utmost importance that the first tier is a local disk or a RAM disk.
Viseron will not be able to detect new files and will not be able to gather file metadata if the first tier is a network share/NTFS mount.

danger

The first tier cannot be a network share/NTFS mount. Viseron will not be able to detect new files and will not be able to gather file metadata. The first tier should be a local disk or a RAM disk. The other tiers can be network shares/NTFS mounts.

When the max_size is hit the oldest files will be moved to the next tier.
When the age of a file exceeds the max_age, it will be moved to the next tier.
If the current tier is the last one, the file is deleted instead.

caution

If you change a tier path, you need to manually move the files to the new path.

info

For technical reasons it is very likely that there will be a few extra megabytes of files than the max_size allows, so leave some extra space on the disk.

Continuous (24/7) recordings​

To allocate space for continuous recordings for all cameras you use the continuous configuration option for a tier. The below example will store 10gb of continuous recordings per camera in the default location /segments. No events will be stored.

Continuous recordings configuration example
/config/config.yaml
storage:
recorder:
tiers:
- path: / # Will store segments in /segments folder inside the container
continuous:
max_size:
gb: 10
info

If you want to disable continuous recordings for a specific camera, you can set continuous_recording: false in the cameras recorder configuration.

tip

To set camera specific options, you can use continuous or override the entire storage configuration under your camera configuration. See the recorder documentation for the component that provides your cameras.

FFmpeg
GStreamer

Advanced configuration​

For more advanced setups, min_size and min_age can be used. They are best explained in a config.yaml example:

Advanced configuration example
/config/config.yaml
storage:
recorder:
tiers:
- path: /mnt/ramdisk # Store continuous recordings and events here
move_on_shutdown: true
continuous:
max_size: # If this target is hit, segments will be moved to the next tier
gb: 1
max_age: # If this target is hit, segments will be moved to the next tier
days: 1
events:
max_size: # If this target is hit, segments will be moved to the next tier
gb: 1
max_age: # If this target is hit, segments will be moved to the next tier
days: 1
- path: /mnt/ssd # Store only events here
events:
min_size: # If max_age is hit, keep at least 1gb
gb: 1
max_size: # If this target is hit, segments will be moved to the next tier
gb: 10
max_age: # If this target is hit, segments will be moved to the next tier ONLY if size is larger than min_size
days: 7
- path: /mnt/nas # Store only events here
events:
min_age: # If max_size is hit, keep at least 30 days worth of recordings. DOES NOT AFFECT max_age
days: 30
max_size: # If this target is hit, recordings/segments will be DELETED ONLY if they are older than 30 days
gb: 100
tip

The same strategy can be applied for snapshots (images of detected objects, faces etc)

Orphaned cameras​

The automatic cleanup jobs only look at cameras that are present in your configuration. When you remove a camera from config.yaml, its recordings, snapshots, thumbnails and database rows are therefore left behind and no longer count towards any retention rule.

To reclaim that space, go to Settings → Storage Cleanup in the web interface. It lists every camera identifier that still has data stored but is no longer configured, along with how many files it occupies, how much disk space they use and how many database rows reference it. Deleting a camera there removes its files from every tier and its rows from the database.

caution

Deleting an orphaned camera is permanent and cannot be undone.

A camera is only ever listed once it is gone from the configuration, so a camera that merely fails to connect is never listed. The page also refuses to list anything while Viseron is in safe mode, or while any component is still setting up or has failed to set up, since the cameras of such a component are not known yet and would look removed.

Database Migrations​

Viseron will sometimes require database migrations to be run when you upgrade Viseron. This is done automatically on startup, and you should avoid stopping Viseron while migrations are running.

External database​

By default Viseron runs a local PostgreSQL database inside the container. If you want to use an external database, you can set the POSTGRES_DATABASE_URL environment variable to point to your database.

This is an example of a docker-compose.yaml file that runs Viseron alongside a PostgreSQL database container:

docker-compose.yaml
services:
viseron-postgres-container:
image: postgres:latest
restart: always
shm_size: "128mb"
environment:
POSTGRES_USER: viseron
POSTGRES_PASSWORD: example
POSTGRES_DB: viseron

viseron:
# Add the below to your existing Docker Compose configuration
environment:
- POSTGRES_DATABASE_URL=postgresql://viseron:example@viseron-postgres-container/viseron
depends_on:
- viseron-postgres-container

Troubleshooting​

To enable debug logging for storage, add the following to your config.yaml
/config/config.yaml
logger:
logs:
viseron.components.storage: debug
sqlalchemy.engine: debug