Backups

Maillayer takes nightly VACUUM-INTO snapshots inside the volume. Plus how to ship them off-box.

What's automatic

  • Every night at 03:00 server time, the cron runs VACUUM INTO /app/data/backups/maillayer-YYYY-MM-DD.db.
  • Old backups are pruned after BACKUP_KEEP_N (default 14) snapshots.
  • VACUUM INTO is online — doesn't lock the live DB.

Where they live

/app/data/backups/ inside the container. Since /app/data is a mounted volume, backups survive container rebuilds.

Restoring

# Stop the container
docker compose -f /opt/maillayer/docker-compose.yml down

# Replace the live DB with a backup
cp /var/lib/docker/volumes/maillayer_maillayer-data/_data/backups/maillayer-2026-04-29.db \
   /var/lib/docker/volumes/maillayer_maillayer-data/_data/maillayer.db

# Start back up
docker compose -f /opt/maillayer/docker-compose.yml up -d

Off-volume backup

The included backups are inside the same volume — fine for restart-class recovery, useless if the volume itself dies. Ship them off:

# Cron: rsync backups + .env to S3 every morning
0 4 * * * rsync -av /opt/maillayer/.env \
  /var/lib/docker/volumes/maillayer_maillayer-data/_data/backups/ \
  s3://my-bucket/maillayer-backups/
Don't forget AUTH_SECRET
Your backup is useless without the same AUTH_SECRET that encrypted the credentials. Either: (a) keep AUTH_SECRET in your platform's env vars (recommended), or (b) include /opt/maillayer/.env in your off-box backup.

Manual backup right now

docker exec -it maillayer sh -c \
  'sqlite3 /app/data/maillayer.db ".backup /app/data/backups/manual-$(date +%F-%H%M).db"'