Skip to content

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.

bash
mysqldump -u phlix -p phlix_db > phlix-db-$(date +%Y%m%d).sql

For MariaDB the command is identical. Verify the dump is valid:

bash
# 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.

bash
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.

bash
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.

bash
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 โ€‹

WhatWhy
data/transcode/*Regenerated on next transcode
Active transcode job stateLost on crash, not recoverable
Scan cacheRegenerated on library rescan
*.log filesRegenerated by the logging system

2. Backup Methods โ€‹

2.1 Manual Backup (CLI) โ€‹

bash
# 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:

bash
# /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 -delete

This 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:

bash
# /etc/systemd/system/phlix-backup.timer
[Unit]
Description=Daily Phlix backup

[Timer]
OnCalendar=daily
Persistent=true

[Install]
WantedBy=timers.target

2.4 Third-Party Tools (Restic / Borg / rsync) โ€‹

Wrap the tarball or point rsync/restic directly at the above paths:

bash
# 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:

bash
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 โ€‹

  1. Stop Phlix
bash
systemctl stop phlix
# or, if running in the foreground, stop the `php public/index.php` process (Ctrl+C)
  1. Restore database
bash
mysql -u phlix -p phlix_db < phlix-db-20250601.sql
  1. Restore config and data
bash
tar -xzf phlix-backup-20250601.tar.gz -C /
# Verify paths match your installation
ls /path/to/phlix/config/
ls /path/to/phlix/data/metadata/
  1. Start Phlix
bash
systemctl start phlix
# or, for development, run in the foreground:
php public/index.php
  1. Verify

Log in, check your library items are visible, confirm watch history is present. If metadata images are missing, trigger a full library rescan from the admin UI, or via the API:

bash
curl -X POST http://localhost:32400/api/v1/libraries/{id}/rescan \
  -H "Authorization: Bearer $TOKEN"

4. Docker / docker-compose Backup โ€‹

Using docker compose exec โ€‹

bash
# 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.gz

Backing up named volumes directly โ€‹

bash
# 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 โ€‹

bash
# 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-server

5. 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:

bash
sha256sum phlix-backup-20250601.tar.gz
# Store the checksum alongside the backup or in a separate log

Test 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).

bash
# Verify JWT_SECRET is consistent between backup and running config
grep JWT_SECRET /path/to/phlix/config/server.php

Failure 3 โ€” Media Not Re-Scanning After Restore (Stale mtime) โ€‹

Symptom: Library shows items but poster/fanart missing; rescan doesn't re-download metadata.

Cause: FolderWatcher uses mtime as its checksum. If the backup was restored to the same filesystem paths, mtimes are preserved and the watcher sees "no change."

Fix: Force a full rescan after restore from the admin UI or the API:

bash
curl -X POST http://localhost:32400/api/v1/libraries/{id}/rescan \
  -H "Authorization: Bearer $TOKEN"

# OR touch all media files to update mtime, then rescan
find /path/to/media -type f -exec touch {} \;

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:

bash
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 backups table for recent successful entries)

BSD-3-Clause