Backups and restore
Every Artavolo instance can produce one encrypted archive that holds everything needed to rebuild it: the application database, the data database (one table per sheet) and the uploaded files. The Enterprise edition runs this on a schedule and can copy the archive to an S3-compatible bucket.
The encryption key
Set BACKUP_ENCRYPTION_KEY before the first backup:
- Docker:
ARTAVOLO_BACKUP_ENCRYPTION_KEYin.env(openssl rand -hex 32), thendocker compose up -d. - LAMP:
define('BACKUP_ENCRYPTION_KEY', '...');inincludes/config.php.
Store a copy of the key outside the server (password manager, printed in a safe). An archive cannot be opened without it, and there is no recovery path. Rotating the key only affects new archives; keep old keys as long as you keep old archives.
Archives are artavolo-backup-YYYYMMDD-HHMMSS.abk: AES-256-CTR with a random IV, authenticated with HMAC-SHA256 over the whole file. The integrity tag is checked before anything is decrypted, so a wrong key or a damaged file is refused up front.
Running a backup
From the shell (any edition):
# Docker
docker compose exec -u www-data app php utilities/backup.php
# LAMP
php utilities/backup.php
Options: --no-uploads (databases only), --path=/mnt/backups, --keep=30 (retention in days), --no-s3, --plain (unencrypted zip, only for moving data by hand). The archive lands in data/backups/ (Docker: the app_backups volume) unless a path is configured.
From the administration (Enterprise): Settings → Backups → "Run a backup now". The same page configures the daily schedule (hour, server time), the local folder, retention, whether uploads are included, and the S3-compatible target. The cron job that ships with the instance starts the run within five minutes of the chosen hour. A failed run e-mails all administrators; every run appears in the audit log as backup.run or backup.failed.
Copy archives off the host. With Docker: docker compose cp app:/var/www/html/data/backups ./backups. With an S3 target the copy happens automatically after each run; retention applies there too.
Checking an archive
php utilities/restore.php artavolo-backup-20260911-030001.abk --dry-run
This verifies the tag, decrypts to a temporary folder, prints the manifest (version, instance, creation time) and the size of each part, and removes the temporary files. Do this monthly, and once on a scratch machine to prove the full restore below works.
Restoring
Restoring replaces the two databases named in includes/config.php and copies the uploads over the current ones. Stop the web server (or scale the app container down) first.
# Docker: a fresh stack with the same .env (same ARTAVOLO_BACKUP_ENCRYPTION_KEY)
docker compose up -d db
docker compose run --rm -u www-data app php utilities/restore.php /var/www/html/data/backups/artavolo-backup-20260911-030001.abk --yes
docker compose up -d
# LAMP
php utilities/restore.php /path/to/artavolo-backup-20260911-030001.abk --yes
php utilities/clear-cache.php
--no-uploads restores only the databases, --no-database only the uploads, --extract=/tmp/out unpacks without touching the instance, --key=... supplies the key when config.php does not exist yet. The restore records backup.restored in the audit log.
Restoring into an instance that runs a newer application version is supported: run php utilities/migrate.php afterwards so the schema catches up.
What is not in the archive
includes/config.php (contains database credentials and secrets; keep it with the key), the language packs under lang/ if you edited them, and the licence key (also in the database, so it is restored with it).