This is the canonical recovery procedure when the stack's database or
user-uploaded content is lost, corrupted, or needs to be rolled back to
a known-good snapshot. Backups are produced nightly by the backup service
(see config/phpbu/backup.json).
Located in the snipeit-backups volume (bind-mount it onto an off-host
share for disaster-survival), organised into three subdirectories:
| Path | What | Cadence | Retention |
|---|---|---|---|
/backups/db/snipeit-db-*.sql.gz |
mariadb-dump of the application DB (single-transaction, routines + triggers) |
nightly 03:00 | rolling 5 GB |
/backups/uploads/snipeit-uploads-*.tar.gz |
tar of app-data volume (Snipe-IT uploads / EULAs / barcodes) |
nightly 03:00 | 30 days |
/backups/storage/snipeit-storage-*.tar.gz |
tar of app-storage volume (Laravel storage, including the cached config) |
nightly 03:00 | 30 days |
/backups/phpbu.log |
structured JSON log of phpbu runs (success and failure) | per-run append | rotated by phpbu |
make backup-list shows what's currently on disk.
docker compose stop app web scheduler
# Leave `db`, `valkey`, and `backup` up — we need db to receive the restore.make backup-list
# Pick the SQL file you want — typically the most recent. Note the timestamp.
TIMESTAMP=20260521-030001 # adjust to your chosen fileThis is destructive. The mariadb-dump artefact contains the schema and
data — we're swapping in the backed-up state wholesale.
docker compose exec -T db sh -c '
mariadb -uroot -p"$MARIADB_ROOT_PASSWORD" -e "
DROP DATABASE IF EXISTS snipeit;
CREATE DATABASE snipeit CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
GRANT ALL ON snipeit.* TO snipeit@'%';
FLUSH PRIVILEGES;"'docker compose exec -T backup sh -c "
zcat /backups/db/snipeit-db-${TIMESTAMP}.sql.gz" \
| docker compose exec -T db sh -c '
mariadb -uroot -p"$MARIADB_ROOT_PASSWORD" snipeit'For a large dump this can take minutes — let it finish.
docker compose run --rm -v snipeit-app-data:/restore \
backup sh -c "
cd /restore && \
rm -rf ./* ./.[!.]* 2>/dev/null || true && \
tar xzf /backups/uploads/snipeit-uploads-${TIMESTAMP}.tar.gz --strip-components=2"--strip-components=2 peels the /snapshot/data/ prefix that phpbu added
when it captured the tree.
docker compose run --rm -v snipeit-app-storage:/restore \
backup sh -c "
cd /restore && \
rm -rf ./* ./.[!.]* 2>/dev/null || true && \
tar xzf /backups/storage/snipeit-storage-${TIMESTAMP}.tar.gz --strip-components=2"The restored storage volume contains the old cached config. Clear it before the app re-mounts the cache:
docker compose run --rm app sh -c "
rm -f bootstrap/cache/config.php bootstrap/cache/routes-*.php bootstrap/cache/events.php"
docker compose start app web scheduler
docker compose logs -f --tail=50 app# 1. HTTP responds:
curl -sI http://localhost:8000/login | head -1
# 2. Log in via browser and confirm assets visible.
# 3. Compare a known asset count:
docker compose exec -T db sh -c '
mariadb -usnipeit -p"$DB_PASSWORD" -e "SELECT COUNT(*) FROM assets;" snipeit'Skip steps 5 and 6. App will use its current uploads + storage with the restored DB. Useful for "undo an incorrect bulk update on assets" type incidents.
Caveat: if the DB references uploaded files (avatars, EULAs) that were added after the snapshot, those files exist on disk but Snipe-IT won't reference them (they're orphans). And uploads referenced by the restored DB that were deleted later are missing 404s. For point-in-time fidelity, restore all three.
The DB service has --log-bin=mariadb-bin --binlog-format=ROW --expire-logs-days=14 enabled, so PITR within the last 14 days is possible:
# 1. Restore the latest dump BEFORE the bad event
# 2. Apply binlogs up to a specific timestamp:
docker compose exec -T db sh -c '
mariadb-binlog --start-datetime="2026-05-21 09:00:00" \
--stop-datetime="2026-05-21 14:30:00" \
/var/lib/mysql/mariadb-bin.* \
| mariadb -uroot -p"$MARIADB_ROOT_PASSWORD" snipeit'If the issue is a broken Snipe-IT release, not lost data:
# .env: set SNIPE_IT_IMAGE_TAG=8.5.0-20260520 (or any dated tag)
docker compose pull app app-assets
docker compose up -d
# Migrations roll forward; the image carries its own migration state.Note: Snipe-IT migrations are forward-only. Restoring an older image against a DB migrated by a newer image will produce schema errors. In that case do a full DB+image restore from the corresponding day's snapshot.
make backup-verify runs phpbu in --simulate mode, plus checks that
last night's dump is on disk and not zero-byte. It does NOT restore — use
it to confirm the backup pipeline is working without disrupting
production.
snipeit-backups is a local Docker volume. Survives container restart
but not host loss. For real disaster recovery, configure your existing
backup tool (restic, rclone, BorgBackup, etc.) to read from the volume
mount point on the host. Example bind-mount snippet for compose.yml:
backup:
volumes:
- /mnt/nas/snipeit-backups:/backups # NAS-mounted destinationReplace /mnt/nas/snipeit-backups with your real destination. The
phpbu config is unchanged — it still writes to /backups inside the
container; that path is now on your NAS.