Since: 0.18.0
TL;DR โ
Phlix needs three things to survive a disaster: the database (users, watch history, library metadata), config files (JWT secret, DB credentials, server settings), and metadata images (posters, fanart). You do NOT need to back up the transcode cache, active job state, or scan cache โ these are regenerated automatically. Back up regularly (daily is recommended), store off-site, and test restores quarterly.
1. What to Back Up โ
1. Database โ
What: All Phlix data โ users, watch history, media library metadata, playback state, sessions.
mysqldump -u phlix -p phlix_db > phlix-db-$(date +%Y%m%d).sqlFor MariaDB the command is identical. Verify the dump is valid:
# Check the dump starts correctly
head -5 phlix-db-20250601.sql
# Should show: -- MySQL dump 10.x ... or -- MariaDB dump ...2. Config Files โ
What: config/ directory โ server.php, database.php, ffmpeg.php, backups.php (if present). These contain your JWT_SECRET, DB credentials, and all server settings.
tar czf phlix-config-$(date +%Y%m%d).tar.gz -C /path/to/phlix config/3. Metadata Images โ
What: data/metadata/ โ poster.jpg, fanart.jpg, and all artwork downloaded from TMDB/TVDB. These are not stored in the DB; they live on disk.
tar czf phlix-metadata-images-$(date +%Y%m%d).tar.gz -C /path/to/phlix data/metadata/These can be re-downloaded by re-scanning your library, but re-downloading from TMDB/TVDB takes significant time and may hit rate limits. A local backup is much faster to restore.
4. Plugin Configs โ
What: data/plugins/ if you have custom plugins installed that store configuration on disk.
tar czf phlix-plugins-$(date +%Y%m%d).tar.gz -C /path/to/phlix data/plugins/5. TLS Certificates โ
What: config/ssl/ for self-hosted deployments using custom HTTPS certificates.
NOT Needed โ
| What | Why |
|---|---|
data/transcode/* | Regenerated on next transcode |
| Active transcode job state | Lost on crash, not recoverable |
| Scan cache | Regenerated on library rescan |
*.log files | Regenerated by the logging system |
2. Backup Methods โ
2.1 Manual Backup (CLI) โ
# Database
mysqldump -u phlix -p phlix_db > phlix-db-$(date +%Y%m%d).sql
# Full tarball โ everything that matters (excluding transcode and logs)
tar czf phlix-backup-$(date +%Y%m%d).tar.gz \
-C /path/to/phlix \
config/ data/metadata/ data/plugins/ \
--exclude='data/transcode/*' \
--exclude='*.log'2.2 Built-in Backup Manager โ
Phlix ships a BackupManager (src/Admin/BackupManager.php) that packages the MySQL dump, config/*.php, the data/ directory, and SSL certificates into a single tar.gz, with optional S3 upload and retention cleanup. There is no admin CLI command for this yet โ backups are driven by configuration: set auto_backup_interval_days and retention_count in config/backup.php and the server creates and prunes backups automatically. See Backup for the full configuration reference (local path, S3 credentials, retention).
For an on-demand backup without the manager, use the manual tarball shown in ยง2.1 above.
2.3 Automated (Cron / systemd timer) โ
If you prefer OS-level scheduling over the built-in interval, run the manual backup from cron:
# /etc/cron.d/phlix-backup โ daily at 03:00
0 3 * * * root /usr/bin/mysqldump -u phlix -pYOURPASS phlix_db \
> /backups/phlix-db-$(date +\%Y\%m\%d).sql && \
/usr/bin/tar czf /backups/phlix-$(date +\%Y\%m\%d).tar.gz \
-C /path/to/phlix config/ data/metadata/ data/plugins/ \
--exclude='data/transcode/*' --exclude='*.log' && \
/usr/bin/find /backups -name 'phlix-*' -mtime +7 -deleteThis creates a daily backup and removes backups older than 7 days. Adjust -mtime +7 to retain more or fewer days.
For systemd instead of cron:
# /etc/systemd/system/phlix-backup.timer
[Unit]
Description=Daily Phlix backup
[Timer]
OnCalendar=daily
Persistent=true
[Install]
WantedBy=timers.target2.4 Third-Party Tools (Restic / Borg / rsync) โ
Wrap the tarball or point rsync/restic directly at the above paths:
# Restic โ backup to S3/B2/NAS
restic -r s3:s3.amazonaws.com/bucket/phlix-backups \
backup /path/to/phlix/config /path/to/phlix/data/metadata
# rsync to NAS
rsync -avz --exclude='data/transcode' --exclude='*.log' \
/path/to/phlix/ backup-server:/backups/phlix/For off-site copies:
rclone copy phlix-backup-$(date +%Y%m%d).tar.gz b2:my-bucket/phlix/3. Restore Process โ
Critical: Always restore the database and config from the same backup point. Mixing configs from different points can cause JWT_SECRET mismatches (see Failure 2 below).
Step-by-step โ
- Stop Phlix
systemctl stop phlix
# or, if running in the foreground, stop the `php public/index.php` process (Ctrl+C)- Restore database
mysql -u phlix -p phlix_db < phlix-db-20250601.sql- Restore config and data
tar -xzf phlix-backup-20250601.tar.gz -C /
# Verify paths match your installation
ls /path/to/phlix/config/
ls /path/to/phlix/data/metadata/- Start Phlix
systemctl start phlix
# or, for development, run in the foreground:
php public/index.php- Verify
Log in, check your library items are visible, confirm watch history is present.
If items are missing, trigger a library rescan from the admin UI or via the API โ it re-walks the tree and indexes every file at a path that is not yet in the catalogue:
curl -X POST http://localhost:8096/api/v1/libraries/{id}/rescan \
-H "Authorization: Bearer $TOKEN"If the items are there but metadata images are missing, a rescan will not help โ see Failure 3 below.
Restoring media to a different path is handled by a rescan (S158)
A parent-less item โ movie, video, photo, book, audiobook โ whose file now sits at a different path under the same filename is matched by a path-independent canonical key, and the prune in the same rescan re-points the existing row at the new location. The UUID, watch history and resume position survive the relocation, so the restored backup keeps its value. Episodes were never affected.
Run a Rescan, not a standalone Prune โ a prune does not scan and will delete the rows instead. If the restore also changed filenames, those items will come back as new rows with new UUIDs; restore to the original names where you can. See Moving a file.
4. Docker / docker-compose Backup โ
Using docker compose exec โ
# Inside the container โ dump the DB and tar the data, landing in a mounted volume
docker compose exec phlix-server sh -c \
'mysqldump -u phlix -pYOURPASS phlix_db > /backups/phlix-db-$(date +%Y%m%d).sql && \
tar czf /backups/phlix-$(date +%Y%m%d).tar.gz -C /app config/ data/metadata/ data/plugins/ \
--exclude="data/transcode/*" --exclude="*.log"'
# Copy the backup out of the container
docker compose cp phlix-server:/backups/phlix-20250601.tar.gz ./phlix-backup-20250601.tar.gzBacking up named volumes directly โ
# Backup the named volumes directly (does not need Phlix running)
docker run --rm \
-v phlix_data:/data \
-v $(pwd):/backup \
alpine \
tar czf /backup/phlix_data.tar.gz -C /data .Restoring Docker volumes โ
# Restore
docker run --rm \
-v phlix_data:/data \
-v $(pwd):/backup \
alpine \
tar xzf /backup/phlix_data.tar.gz -C /data
# Restart Phlix
docker compose up -d phlix-server5. What Can Go Wrong โ
Failure 1 โ Corrupt or Incomplete Backup โ
Symptom: Restore succeeds but some tables are empty or SQL errors on import.
Fix: Always verify checksums after backup:
sha256sum phlix-backup-20250601.tar.gz
# Store the checksum alongside the backup or in a separate logTest restores periodically (e.g., monthly) in a staging environment. A backup that was never tested is not a reliable backup.
Failure 2 โ JWT Secret Mismatch (Users Can't Log In) โ
Symptom: Database restored, but all login attempts fail with auth errors.
Cause: JWT_SECRET in config/server.php was not restored, or the backup config used a different secret than the one the server is currently using.
Fix: Restore config/server.php from the same backup as the database. Never rotate JWT_SECRET without also backing up the old value and all existing refresh tokens (stored hashed in the DB).
# Verify JWT_SECRET is consistent between backup and running config
grep JWT_SECRET /path/to/phlix/config/server.phpFailure 3 - Metadata Not Re-Fetched After Restore โ
Symptom: Library shows items but poster/fanart missing, and a rescan does not re-download them.
Cause: A rescan is a filesystem operation, not a metadata one. Only the music scanner acts on its "read every file" flag; on a movie, TV, photo, book or audiobook library an already-indexed path is skipped over, so nothing re-resolves the item's metadata or re-downloads its artwork. (Nothing else picks it up in the background either โ there is no scheduled or filesystem-triggered scan.)
Fix (movie / TV libraries): Queue a forced metadata re-match, which re-resolves items that already carry metadata and re-downloads the artwork for them:
curl -X POST http://localhost:8096/api/v1/libraries/{id}/refresh-metadata \
-H "Authorization: Bearer $TOKEN"If only the local artwork cache was lost, POST /api/v1/libraries/{id}/clear-artwork purges the cached files (metadata text and user data untouched) so the next match re-downloads them. See Force a metadata re-match.
There is no need to touch your media files first โ neither operation looks at mtime, and neither deletes anything.
Photo, book and audiobook libraries have no fix here. Those types have no metadata provider, and both metadata jobs skip their rows, so a metadata_refresh job on such a library completes having processed zero items. Restore the item rows from the database backup โ re-running a job will not rebuild them. See what Match metadata skips.
Failure 4 โ Backup Too Large (Transcode Cache Included) โ
Symptom: Backups are 50 GB+, slow to transfer, fill up backup storage.
Cause: data/transcode/ directory included in tarball.
Fix: Always exclude data/transcode/* from tar commands:
tar czf phlix-backup-$(date +%Y%m%d).tar.gz \
-C /path/to/phlix \
config/ data/metadata/ data/plugins/ \
--exclude='data/transcode/*' \
--exclude='*.log'Active transcode job state is not recoverable anyway โ excluding it saves space with zero data loss.
6. Next Steps โ
- Troubleshooting โ general recovery help if restore has unexpected issues, including reading server logs in
.logs/ - Reverse proxy โ verify your server is correctly exposed after restore
- Consider setting up monitoring/alerting on backup job success/failure (e.g., check your cron/timer exit status, or the
backupstable for recent successful entries)