Backup / Restore Errors
ProxPanel backups are AES-256-GCM encrypted with a key derived from the license server’s per-customer DB password. The file format went through one revision (V1 → V2) in v1.0.208–209 to add an embedded license key for automatic cross-server restore. This page covers the five most common reasons a restore won’t run.
Symptoms
Section titled “Symptoms”- “Invalid backup file format” toast when restoring
- “Decryption failed — wrong password or corrupted file”
- pg_restore fails with
server version: 16.x; pg_dump version: 14.x - Backup created but the scheduler never fires at the configured time
- Restored database loads but with timezone-shifted timestamps
Background — V1 vs V2 format
Section titled “Background — V1 vs V2 format”V1 (pre-2026-02-12): PROXPANEL_ENCRYPTED_BACKUP_V1\n [AES-256-GCM ciphertext]
V2 (current): PROXPANEL_ENCRYPTED_BACKUP_V2\n LICENSE_KEY=PROXP-XXXXX-XXXXX-XXXXX-XXXXX\n [AES-256-GCM ciphertext]The V2 header lets a different server fetch the source’s DB password from license.proxrad.com automatically. V1 backups can still be restored — but only if the operator manually enters the source license key into the Source License Key field on the restore modal.
Diagnostic flow
Section titled “Diagnostic flow”-
Confirm the file isn’t garbage. A valid backup is a binary file starting with one of the two ASCII headers.
Terminal window head -c 35 /opt/proxpanel/backups/proisp_full_20260512_010000.proisp.bakYou want to see
PROXPANEL_ENCRYPTED_BACKUP_V2or_V1. If you see something else (e.g. a SQL dump or random binary), the file is not a ProxPanel backup or is truncated. -
Look at the backup log.
Terminal window docker logs proxpanel-api 2>&1 | grep -iE "backup|restore|decrypt" | tail -40 -
Check the backup_logs table.
Terminal window docker exec proxpanel-db psql -U proxpanel -d proxpanel -c \"SELECT id, schedule_name, filename, last_status, last_error, created_at \FROM backup_logs ORDER BY created_at DESC LIMIT 10;" -
Verify Postgres versions agree between dump and restore (see Cause 4).
Cause 1 — Invalid backup file format
Section titled “Cause 1 — Invalid backup file format”The file doesn’t start with either V1 or V2 magic bytes. Either it’s not a ProxPanel backup, it’s been corrupted in transit, or it’s a partial write from an interrupted backup job.
Log line:
Restore: invalid encrypted backup format (first 30 bytes: '...')Diagnostic:
# What's actually in the header?xxd /opt/proxpanel/backups/SUSPECT.proisp.bak | head -2
# File size sanity checkls -la /opt/proxpanel/backups/SUSPECT.proisp.bakA valid backup for an install with 1000 subscribers is typically 5–50 MB. Anything under 1 KB is almost certainly truncated.
Fix:
- Re-download the file (FTP / SCP / Cloud) — corruption in transit is common over FTP without binary mode.
- Re-take the backup if the original is bad. The local
/opt/proxpanel/backups/directory has the source files; checkbackup_logsfor the most recent successful job.
Cause 2 — Decryption failed
Section titled “Cause 2 — Decryption failed”The header is right but the AES-GCM verification fails. The key is derived from the DB password; if the restoring server has a different password than the source, decryption can’t work.
Log line:
Restore: decryption failed - this backup may be from a different installationDiagnostic:
Same-server restore
The current server’s DB password should decrypt its own backups. If it doesn’t, either the password changed (e.g. you regenerated secrets on the license server) or this backup is from a different server.
# Try the validate endpoint — it attempts decryption without restoringcurl -s -X POST http://localhost:8080/api/backups/SUSPECT.proisp.bak/validate \ -H "Authorization: Bearer $TOKEN" | jqCross-server restore
The backup is from a different installation. If V2, the license key is embedded — the handler will auto-fetch the source’s DB password from license.proxrad.com. If V1, you must enter the source license key manually.
# What format is it?head -c 35 /opt/proxpanel/backups/SUSPECT.proisp.bak
# What license key is embedded? (V2 only)head -c 200 /opt/proxpanel/backups/SUSPECT.proisp.bak | grep -ao "LICENSE_KEY=[A-Z0-9-]*"Fix:
-
V2 backup, restore failed: confirm the embedded license key matches an active license on
license.proxrad.com:Terminal window curl -s -X POST "https://license.proxrad.com/api/v1/license/validate" \-H "Content-Type: application/json" \-d '{"license_key":"PROXP-XXXXX-XXXXX-XXXXX-XXXXX","server_ip":"YOUR_IP"}' | jq '.status'If the license is
revokedor unknown, the password fetch will 404 and the restore can’t proceed. -
V1 backup: open the restore modal in the UI → Source License Key field → paste the source server’s license key (not the current server’s). The handler fetches the DB password matching that key and decrypts with it.
-
Secrets regenerated: if the license-server operator regenerated the customer’s DB password (admin → license → Regenerate Secrets), every backup taken before that point is unrecoverable on this server without the old password. Either restore the old password, or take a fresh backup post-regenerate as the new baseline.
Cause 3 — Wrong source license key for V1
Section titled “Cause 3 — Wrong source license key for V1”You’re restoring a V1 backup from Server A onto Server B and you typed Server B’s license key. Decryption fails because Server B’s key derives a different DB password than Server A’s.
Symptom: “decryption failed” on a V1 backup that you know is good.
Fix:
In the restore modal’s Source License Key field, enter Server A’s license key — the one that created the backup, not the one currently running. The handler calls:
GET https://license.proxrad.com/api/v1/license/backup-passwordHeader: X-License-Key: PROXP-SERVER-A-KEYand uses the returned password for decryption only. Nothing is permanently changed on Server B.
Cause 4 — Postgres version mismatch
Section titled “Cause 4 — Postgres version mismatch”The backup was taken with pg_dump from one Postgres major version (e.g. 16) and you’re restoring with pg_restore from another (e.g. 14). This happens when an upgrade replaced the container’s postgresql-client package but the running database is still on the old version, or vice versa.
Log line:
pg_restore: error: unsupported version (1.16) in file headeror
pg_restore: error: server version: 16.11; pg_dump version: 14.20Diagnostic:
# What server version is running?docker exec proxpanel-db psql -U proxpanel -d proxpanel -c "SELECT version();"
# What client version is in the API container?docker exec proxpanel-api pg_restore --versionBoth should be the same major version (16 today).
Fix:
-
Install matching client in the API container. ProxPanel’s docker-compose since v1.0.95 installs
postgresql-client-16from the PostgreSQL APT repo. For older installs:Terminal window docker exec proxpanel-api bash -c 'apt-get update &&apt-get install -y wget gnupg lsb-release &&install -d /usr/share/postgresql-common/pgdg &&wget -qO /usr/share/postgresql-common/pgdg/apt.postgresql.org.asc https://www.postgresql.org/media/keys/ACCC4CF8.asc &&echo "deb [signed-by=/usr/share/postgresql-common/pgdg/apt.postgresql.org.asc] https://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list &&apt-get update &&apt-get install -y postgresql-client-16'docker restart proxpanel-api -
Verify:
Terminal window docker exec proxpanel-api pg_restore --version -
Retry the restore.
Cause 5 — Backup scheduler never fires (tzdata missing)
Section titled “Cause 5 — Backup scheduler never fires (tzdata missing)”A scheduled backup is configured for 02:00 Asia/Beirut but no backup files appear and backup_logs has no row. The scheduler couldn’t load the timezone and silently fell back to UTC, where 02:00 already passed without firing.
Same root cause as the FUP reset issue — the API container is missing tzdata.
Diagnostic:
docker exec proxpanel-api dpkg -l | grep tzdata
docker exec proxpanel-db psql -U proxpanel -d proxpanel -c \ "SELECT id, name, frequency, time_of_day, last_run_at, next_run_at, last_status FROM backup_schedules;"If tzdata is missing or next_run_at is NULL, the scheduler can’t compute the next firing time.
Fix:
docker exec proxpanel-api apt-get updatedocker exec proxpanel-api apt-get install -y tzdatadocker restart proxpanel-apiThen in the UI: open the schedule, save it again (the save handler recomputes next_run_at correctly). The next firing window is then valid.
Full details: see FUP Not Resetting — the same tzdata fix applies.
Cause 6 — Backup file header was mangled by FTP ASCII mode
Section titled “Cause 6 — Backup file header was mangled by FTP ASCII mode”The backup uploads to FTP with binary content but the FTP client did the upload in ASCII mode, replacing \n with \r\n (or vice versa). The first 30 bytes — the magic header — are now off-by-one, decryption fails.
Diagnostic:
# CR-LF in a backup means corruptionxxd /opt/proxpanel/backups/SUSPECT.proisp.bak | head -3 | grep -c '0d 0a'If you see a 0d 0a (CR-LF) near the start of a backup that should have plain 0a (LF), FTP mangled it.
Fix:
Re-upload with FTP in binary mode:
# Inside an ftp session:binaryput /opt/proxpanel/backups/proisp_full_20260512_010000.proisp.bakOr skip FTP and use SCP / rsync, which never touch byte content.
How to take a clean backup before troubleshooting
Section titled “How to take a clean backup before troubleshooting”Before doing anything destructive, take a manual backup. In the UI: Settings → Backups → Create Backup Now. Or from the host:
# Dump and encrypt via the running APIcurl -s -X POST http://localhost:8080/api/backups/create \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"backup_type":"full"}'
# Files land in:ls -lh /opt/proxpanel/backups/Copy the resulting .proisp.bak to a safe location before continuing.
Still stuck?
Section titled “Still stuck?”# Header + size of the suspect filehead -c 35 /opt/proxpanel/backups/SUSPECT.proisp.bak | xxd > /tmp/header.txtls -la /opt/proxpanel/backups/SUSPECT.proisp.bak >> /tmp/header.txt
# Backup log entriesdocker exec proxpanel-db psql -U proxpanel -d proxpanel -c \ "SELECT * FROM backup_logs ORDER BY created_at DESC LIMIT 10;" > /tmp/backup-log.txt
# Schedulesdocker exec proxpanel-db psql -U proxpanel -d proxpanel -c \ "SELECT * FROM backup_schedules;" > /tmp/schedules.txt
# Postgres versionsdocker exec proxpanel-db psql -U proxpanel -d proxpanel -c "SELECT version();" > /tmp/pgsrv.txtdocker exec proxpanel-api pg_restore --version > /tmp/pgcli.txt
# tzdata statusdocker exec proxpanel-api dpkg -l | grep tzdata > /tmp/tzdata.txt
# Recent API errorsdocker logs --since=1h proxpanel-api 2>&1 | grep -iE "backup|restore|decrypt" > /tmp/backup.logEmail info@proxrad.com with these files and the source/destination license keys (if cross-server).
Related pages
Section titled “Related pages”- License Errors — when the license server can’t return the DB password for restore.
- FUP Not Resetting — the same
tzdatafix applies. - Update Failures — take a backup before every update.