XAXA Server control panel

Admin

Backups

XAXA backs up every site with Automatic backups enabled. The backup time (default 03:00, panel time zone) is set under Admin → Backups; each site picks its frequency under Settings → General: every hour, every 6 hours, daily (the default) or weekly on Sunday. Hourly and 6-hourly backups run at the minute of the backup time. Each backup is one .tar.zst archive (tar compressed with Zstandard, several times faster than gzip at the same size, so the backup takes less CPU from the sites) containing:

  • the site user's home directory (/home/<user>), including htdocs — except node_modules, .cache and .npm, which a deploy recreates,
  • a mysqldump of every database that belongs to the site.

Sites that have databases also get a separate database backup with every scheduled backup: an archive with only the mysqldump files (db-<date>.tar.zst), made from the same dump as the full backup, so the databases are dumped once. It is kept on its own schedule, so you always have the databases of the last days even when you keep few full backups.

Archives are stored in /var/backups/xaxa/<user>/, readable only by root. When a site is deleted its archives are moved to /var/backups/xaxa/_deleted/<date>-<user>/ and kept for 30 days. Each archive is read back once after it is written (zstd -t checks its checksums), so a damaged archive fails the backup instead of the restore. A file that changes while it is being archived (logs, cache, sessions of a live site) is saved as it was at that moment and does not fail the backup.

Settings

Under Admin → Backups:

  • Backup time — when daily and weekly backups run; 6-hourly backups start from it.
  • Keep full backups — how many file + database archives to keep per site (1–90). Older ones are deleted after each backup. The count is per archive, so with hourly backups 7 means the last 7 hours.
  • Keep database backups — how many database-only archives to keep per site (default 7, the last week).
  • Also one per day / per week — besides the newest archives above, keep the newest archive of each of the last N days (0–90) and N weeks (0–52) that have one, for both kinds. With hourly backups, Keep full backups 24 + 7 days + 4 weeks keeps the last day hour by hour, then one a day for a week and one a week for a month. 0 (the default) turns it off.
  • Remote storage — copy every new archive off the server: to S3-compatible storage (AWS S3, Cloudflare R2, Backblaze B2, Wasabi, Hetzner Object Storage, DigitalOcean Spaces, MinIO and others) or over SFTP (Hetzner Storage Box, a NAS, another server).

With alerts on (Instance → Settings), admins are told when a backup fails and, once a day, when a site has had no successful backup for twice its interval (at least 6 hours more than it): a stopped worker fails no job, so only this alert would notice.

If the copy to remote storage fails, the local archive is still saved and can be restored; the backup shows the error in red and admins get an e-mail when notifications are on. Each backup copies every local archive that is not off-site yet (compared by name), so an archive a failed night left out goes with the next backup; turning remote storage on uploads the archives already kept on the server.

By default every copy stays in the bucket; limit it there with a lifecycle rule, or turn on Apply the retention off-site too: after each backup the off-site copies of that site and kind are pruned with the same settings as the local ones (the panel backup keeps 14), and deleting a backup deletes its off-site copy. The access key then also needs permission to list and delete objects. If pruning fails, the backup is still saved and shows the error like a failed copy. With versioning or object lock on the bucket, deleted copies stay recoverable for as long as the bucket keeps them.

For S3-compatible storage enter the endpoint URL, region, bucket (optionally with a path, like backups/server-1) and an access key pair. The secret key is written to a root-only rclone config at /etc/xaxa/rclone.conf and is never stored in the panel database.

For SFTP enter the host, port (22; a Hetzner Storage Box uses 23), user and a folder (relative to the account's home, like backups/server-1). The server logs in with its own SSH key: the form shows its public key, add it to .ssh/authorized_keys of the account before saving. Or enter the account's password instead; like the S3 secret it goes only to the rclone config. The storage's host key is read on save and pinned (/etc/xaxa/backup-known_hosts): if another machine answers later, copies fail instead of going to it. Save again after the storage really changes its host key.

On save the server writes and deletes a small test object (.xaxa-write-test) with the new settings; if the storage refuses it (wrong key, address, bucket or folder), the error is shown and nothing changes.

Copies are encrypted on the server before upload (rclone crypt): the panel archive holds the app key, the S3 key and TLS private keys, and site archives hold files and database dumps. The encryption password is created when remote storage is first saved and shown once — store it outside the server, in a password manager. It stays the same when you change the settings or turn remote storage off and on again. File names stay readable (<user>/2026-10-30_030000.tar.zst.bin); contents are not. Storage set up before encryption shows a warning until you enter the secret key and save again; earlier plain copies stay as they were.

Use a key that can only write to that bucket, and enable versioning or object lock on the bucket for protection against ransomware.

Manual backup

Open a site's Backups tab. Full backups and Database backups are separate tabs; each has its own button (Back up now, Back up databases). The list refreshes until the backup is done.

Download

Click the download icon on a finished backup to save the .tar.zst archive. It contains databases/<name>.sql, and for full backups the home directory as well. Extract it with tar -xf <file> (GNU tar 1.31 or later with zstd installed) or any archiver that supports Zstandard. Backups taken before 0.4.7 are .tar.gz; they still download and restore as before, and retention counts both kinds together.

Restore

Click Restore on a finished backup. Before anything is replaced, the current state is backed up (a backup of the same type, marked Taken automatically before a restore; it deletes no older archive, the next scheduled backup applies the retention), so a wrong pick can be undone by restoring that one; if this backup fails (for example the disk is full), nothing is restored. Restoring a database backup only re-imports the site's databases and leaves the files alone. A full restore runs in the background (progress on the site's Overview): the home directory is replaced with the archive contents (files that are not in the backup are removed; node_modules, .cache and .npm, which backups skip, are kept), the site's databases are re-imported and a running Node.js or Python app restarts on the restored code. Each database is emptied before the import, so tables created after the backup do not stay behind. The files are written by the site user, never by root. They are unpacked next to the home directory first, so a full restore needs free disk space about the size of the site plus 1 GB; without it the restore stops before anything changes.

If copying files or importing a database fails after the restore starts, the site can be left partially restored. The safety backup is not applied automatically; restore it from the Backups tab to recover the previous state.

Only one backup, restore or deployment runs per site at a time. A backup is Queued until the site is free and the backup worker gets to it; a queued backup does not block the site, so deploys and restores still start (and the backup runs after them). A restore or deploy is refused while another one, or a backup, is running. Each site has at most one queued or running backup of each kind: the scheduler skips a site whose last backup has not finished yet. Deleting a queued backup cancels it.

While a site job holds its lock, database creation, deletion and password changes wait briefly and then ask you to retry; deleting a database is also refused while the site reports a running task.

To restore from remote storage, download the archive into /var/backups/xaxa/<user>/ first. On the same server the config already holds the encryption password:

rclone --config /etc/xaxa/rclone.conf copy xaxa:<user>/2026-10-30_030000.tar.zst /var/backups/xaxa/<user>/

On a new server, create the same two remotes with the saved encryption password, then copy as above:

rclone config create xaxa-s3 s3 provider=Other access_key_id=… secret_access_key=… endpoint=https://… region=…
rclone config create xaxa crypt remote=xaxa-s3:BUCKET filename_encryption=off directory_name_encryption=false password=ENCRYPTION_PASSWORD
# or, for SFTP storage (a key the storage accepts, or pass=… instead of key_file):
rclone config create xaxa-sftp sftp host=… port=23 user=… key_file=/root/.ssh/id_ed25519 shell_type=none
rclone config create xaxa crypt remote=xaxa-sftp:FOLDER filename_encryption=off directory_name_encryption=false password=ENCRYPTION_PASSWORD

Copies made before encryption sit at BUCKET/<user>/ in plain form and are copied with xaxa-s3:BUCKET/<user>/….

Then let the panel find the archive, and restore it from the site's Backups tab:

xaxa backup-scan example.com

The same brings back a site deleted by mistake within 30 days: add the site again with the same domain and user name, move its archives back and scan:

mv /var/backups/xaxa/_deleted/2026-11-05_120000-example/* /var/backups/xaxa/example/
xaxa backup-scan example.com

Panel backup

Every night at 02:45 (and on xaxa panel-backup) the panel backs up itself to /var/backups/xaxa/_panel/ — 14 archives are kept and, with remote storage on, each is copied to BUCKET/_panel/. An archive holds:

  • a consistent snapshot of the panel database,
  • the panel's .env (its APP_KEY decrypts stored secrets such as deploy keys and environment variables),
  • /etc/xaxa, the nginx site configs, /etc/nginx/xaxa and the page cache zones, the Redis site users, firewall rules and /etc/letsencrypt.

Site files and databases are in the site backups above. To move the panel to a new server:

  1. Install XAXA on the new server, and the PHP versions your sites use (xaxa php-install 8.2).
  2. Copy the latest panel archive to the new server, and each site's backup folder to the same path (/var/backups/xaxa/<user>/). Stop the panel: systemctl stop xaxa-scheduler.timer xaxa-queue xaxa-queue-long php8.4-fpm.
  3. Extract the panel archive: tar -xf 2026-10-30_024500.tar.zst -C /. Put the database snapshot in place; the old -wal/-shm files must go, or SQLite applies them to the new file and corrupts it: rm -f /var/lib/xaxa/panel/database.sqlite-wal /var/lib/xaxa/panel/database.sqlite-shm && mv /var/lib/xaxa/panel/backup-snapshot.sqlite /var/lib/xaxa/panel/database.sqlite && chown xaxa:xaxa /var/lib/xaxa/panel/database.sqlite.
  4. Load the Redis users: systemctl restart redis-server.
  5. Run xaxa resync-sites --recreate. Each site's Linux account and databases are created again with new passwords, printed once, and its pool, service, vhost and cron jobs are written.
  6. Start the panel: systemctl start php8.4-fpm xaxa-queue xaxa-queue-long xaxa-scheduler.timer && systemctl reload nginx. Restore each site from its latest backup in Site → Backups, then put the new database passwords into the apps' configuration (.env, wp-config.php) or set the old ones in Site → Databases.

Found a mistake or need help? support@xaxa.com.tr