Backups and restore
one command, with or without the video files.
Everything Vodmill stores is in DATA_DIR. npm run backup packs the databases and small uploads safely while the server runs; add --with-media and the source videos and rendered clips go into the same archive. npm run restore brings a backup back into an empty folder.
01What is in DATA_DIR
| In DATA_DIR | What it is | In a backup archive |
|---|---|---|
app.sqlite | Accounts, workspaces, sources, clips, renders, credits, payments, campaigns, the payout ledger, publishing | Always |
admin.sqlite | Console: settings, sealed keys, team, audit log | Always |
.admin-secret-key | The generated secret key (only when ADMIN_SECRET_KEY is not in .env) | When it exists |
files/assets | Each workspace's logos and own music | Always |
files/music | The operator's music library | Always |
files/sources | Source videos, their preview copies, audio and transcripts | Only with --with-media |
files/renders | Rendered clips, subtitles, covers | Only with --with-media |
backups | The archives themselves | — |
Without --with-media the archive holds the databases, files/assets and files/music (scripts/lib/ops.mjs). A server restored from it still lists every source and clip, but has no video files: sources must be uploaded again and clips rendered again. Add --with-media when you want a backup that restores everything (below).
02Make a backup
In the product folder:
cd /opt/vodmill
npm run backup
# Docker:
docker exec vodmill npm run backup
It writes vodmill-backup-YYYYMMDD-HHMMSS.tar.gz into DATA_DIR/backups (or BACKUP_DIR when set), and prints what it packed:
Backup written: /opt/vodmill/data/backups/vodmill-backup-20261110-030000.tar.gz (… MB, … s)
databases: app.sqlite, admin.sqlite
folders: files/assets (… files), files/music (… files)
secret key: not included: ADMIN_SECRET_KEY is set in .env, keep a copy of it with your backups
not included: source videos and rendered clips (add --with-media to pack them too)
Copy it off this server (another disk, S3, your laptop): a backup on the same disk does not survive a lost disk.
It is safe while the server runs: the databases are copied with SQLite's online backup, checked with SQLite's integrity check, and only then packed. The archive is readable by its owner only.
Option (after --) | Effect |
|---|---|
--out /mnt/backups | Write somewhere else this time |
--keep 14 | Afterwards delete all but the newest 14 backups in that folder |
--with-media | Also pack files/sources and files/renders. --with-outputs is the older name of the same option. |
npm run backup -- --out /mnt/backups --keep 14
The console's System health page shows when the last backup was made.
03Every night
Open the crontab of the user that runs Vodmill and add one line (change the folder to yours):
sudo crontab -u vodmill -e
0 3 * * * cd /opt/vodmill && /usr/bin/npm run backup -- --keep 14 >> backup.log 2>&1
With Docker, in the host's crontab:
0 3 * * * docker exec vodmill npm run backup -- --keep 14 >> /var/log/vodmill-backup.log 2>&1
04Backups with the video files
npm run backup -- --with-media
# Docker:
docker exec vodmill npm run backup -- --with-media
The archive then also holds the source videos with their transcripts and the rendered clips, and a restore brings back a complete server. The files are written into the archive as a stream, so a large library does not have to fit into the server's memory (scripts/lib/ops.mjs).
- Check the free disk space first. The archive can get as large as
files/sourcesandfiles/renderstogether. Use--outto write it to another disk. - With
--keep, every kept archive has that size. A common arrangement is a plain backup every night and a backup with media once a week into its own folder:npm run backup -- --with-media --out /mnt/backups/full --keep 2. - Renders can be made again from their sources. Sources cannot be recreated, so they matter more.
If your hosting provider offers disk snapshots, a snapshot of the data disk is another way to cover the video files.
05Take backups off the server
A backup on the same disk does not survive a lost disk. Copy the archives to another machine, for example nightly from a machine that can reach the server over SSH:
scp "your-server:/opt/vodmill/data/backups/*.tar.gz" ~/vodmill-backups/
A backup contains your customers' data and, when the key was generated, the key that opens the saved API keys. If ADMIN_SECRET_KEY is in .env, the backup does not contain it: keep a copy of that line in your password manager.
06Restore
A restore goes into an empty DATA_DIR only, so it can never mix two installs. If the folder is not empty, the command stops and says so.
- Stop the server
sudo systemctl stop vodmill(ordocker stop vodmill). - Move the old data away
cd /opt/vodmill && sudo -u vodmill mv data data-old - Restore
sudo -u vodmill npm run restore data-old/backups/vodmill-backup-20261110-030000.tar.gz - Only for a backup made without
--with-media: if the old video files are still on the disk, put them back withsudo -u vodmill cp -a data-old/files/sources data-old/files/renders data/files/. A backup made with--with-mediaalready restored them. - Start the server
sudo systemctl start vodmill
The command answers:
Restored the backup from 2026-11-10T02:00:00.000Z (Vodmill 1.0.0) into /opt/vodmill/data.
Start the server again: npm start, or start the Docker container.
The restore checks the archive and both databases before it moves anything into DATA_DIR, and warns when:
- the backup was made with a newer Vodmill than the one installed (install that version first, or the server refuses to start);
ADMIN_SECRET_KEYin.envis not the key the backup was made with (put the old key back, or enter the API keys again);- the backup was made with a key from
.envand none is set now.
With Docker, restore into a new volume and start the container with it:
docker volume create vodmill-data-restored
docker run --rm --env-file .env -v vodmill-data-restored:/app/data -v "$PWD/backups:/restore:ro" \
vodmill npm run restore /restore/vodmill-backup-20261110-030000.tar.gz
07Moving to another server
- Install Vodmill on the new server (Installation) with the same
ADMIN_SECRET_KEYin.env, and do not start it yet, or stop it and empty itsdatafolder. - Make a backup on the old server with
npm run backup -- --with-mediaand copy the archive across. - Restore on the new server and start it.
- Point the domain at the new server. If the domain changes, update
PUBLIC_URL, the Stripe webhook address and the Google redirect URI.