Backup & restore
There are exactly two things you need to back up: the database and the storage volume.
What to back up
| What | Default location | Restorability |
|---|---|---|
| SQLite DB | /data/app.db (Docker)/var/lib/speedyfiles/app.db (bare-metal) | Required — contains users, packages, tokens, settings, audit |
| Storage volume | /srv/files | Required for local backend — contains bulk file contents |
.env file | ./ or /opt/speedyfiles/ | Needed because SESSION_SECRET decrypts stored credentials |
NOT needed
- App source (re-pulled from Docker / git)
- nginx vhost (re-generated from
deploy/nginx/template) - systemd unit (same)
- TLS cert (re-issued by certbot — but you'll lose ~minutes during re-issue)
Hot backup (no downtime)
SQLite's .backup command is safe to run while the app is using the DB:
BACKUP_DIR=/srv/backups/speedyfiles
mkdir -p "$BACKUP_DIR"
DATE=$(date +%Y%m%d-%H%M%S)
# 1. SQLite snapshot
sqlite3 /data/app.db ".backup $BACKUP_DIR/app-$DATE.db"
# 2. Files volume (rsync incremental)
rsync -a --delete /srv/files/ "$BACKUP_DIR/files/"
# 3. Bundle for offsite
tar czf "$BACKUP_DIR/sf-$DATE.tar.gz" \
"$BACKUP_DIR/app-$DATE.db" "$BACKUP_DIR/files/" \
/opt/speedyfiles/.env
# ship to S3 / B2 / wherever:
aws s3 cp "$BACKUP_DIR/sf-$DATE.tar.gz" s3://your-backup-bucket/
Run from cron daily:
# /etc/cron.d/speedyfiles-backup
30 2 * * * root /usr/local/sbin/sf-backup.sh
Cold backup (with downtime, simpler)
systemctl stop speedyfiles # or: docker compose stop
tar czf sf-cold-$(date +%Y%m%d).tar.gz \
/data /srv/files /opt/speedyfiles/.env
systemctl start speedyfiles # or: docker compose start
S3 storage backend
If STORAGE_BACKEND=s3, the files are in your S3 bucket — back up the bucket separately using S3 versioning + replication. Only the SQLite DB and .env need to be backed up server-side.
Restore
systemctl stop speedyfiles
# Restore DB
cp /path/to/backup/app-20260101.db /data/app.db
chown speedyfiles:speedyfiles /data/app.db
# Restore files
rsync -a /path/to/backup/files/ /srv/files/
chown -R speedyfiles:speedyfiles /srv/files
# Restore .env (only if SESSION_SECRET was rotated since the backup)
cp /path/to/backup/.env /opt/speedyfiles/.env
systemctl start speedyfiles
Verifying a backup
Restore to a temp instance periodically. The cheapest test:
# Mount the backup on a test container
docker run --rm -it \
-v /path/to/backup/app-20260101.db:/data/app.db \
-v /path/to/backup/files:/srv/files \
-e SESSION_SECRET=$(cat /path/to/backup/session_secret.txt) \
-e PUBLIC_BASE_URL=http://localhost:5300 \
-p 5300:5300 \
ghcr.io/speedyfiles/speedyfiles:latest
# Visit http://localhost:5300, log in, browse packages