---
title: Backing Up and Restoring
description: "A RackHelm install is three things you cannot recreate: the database, the uploaded files under `storage/`, and the application encryption key in `.env`. Back up all three together, and understand why the key is the one that will catch you…"
url: "https://rackhelm.com/documentation/backup-and-restore/"
updated: "2026-09-24"
category: "Installation & Server Setup"
---

# Backing Up and Restoring

A RackHelm install is three things you cannot recreate: the database, the uploaded files under `storage/`, and the application encryption key in `.env`. Back up all three together, and understand why the key is the one that will catch you out.

## Why APP_KEY is the critical item

RackHelm encrypts credentials at rest using `APP_KEY` from `.env`. That covers:

- Payment gateway configuration — API keys and secrets
- Server integration connections — control-panel base URLs and admin API tokens
- Control-panel records and custom API integration credentials
- Notification channel configuration, including webhook URLs
- E-mail route passwords and OAuth client secrets used by e-mail-to-ticket
- Selected rows in the settings table, such as the SMTP password and CAPTCHA secret

Restore a database without its matching `APP_KEY` and every one of those becomes unreadable. The rows are still there, but nothing can decrypt them — you will be re-entering every gateway and integration credential by hand.

> **Warning:** Never rotate `APP_KEY` on a live install, and never let a restore run against a fresh `.env` with a newly generated key.

## What to back up

| Item | Path | Why |
| --- | --- | --- |
| Database | MySQL database named in `DB_DATABASE` | Clients, orders, invoices, services, tickets, settings, schedules — everything. |
| Environment file | `/var/www/rackhelm/.env` | `APP_KEY`, database credentials, licence key. |
| Private storage | `/var/www/rackhelm/storage/app/private` | Generated invoice PDFs and ticket attachments. |
| Public storage | `/var/www/rackhelm/storage/app/public` | Logos, page assets and other uploads served through the `public/storage` symlink. |
| Logs | `/var/www/rackhelm/storage/logs` | Optional, but useful when investigating what happened before an incident. |
| Web-server configuration | `/etc/nginx/sites-available/rackhelm.conf` and the Let's Encrypt files under `/etc/letsencrypt` | Saves reissuing certificates and rewriting the site block. |
| Worker configuration | `/etc/supervisor/conf.d/rackhelm-horizon.conf` (or the RHEL `.ini` equivalent) and the scheduler crontab | Without these the restored install has no queue worker and no scheduler. |
| Installer credentials | `/root/.rackhelm-credentials` | Only exists if you chose a local database, and only until you delete it. |

The application source itself does not need backing up — you can re-extract it from the release archive — but `vendor/` and `public/build` are faster to restore than to rebuild if you have them.

## Taking a backup

A workable nightly routine, run as root:

```bash
#!/bin/bash
set -e
STAMP=$(date +%F)
DEST=/var/backups/rackhelm/$STAMP
mkdir -p "$DEST"

# Read the credentials out of .env rather than duplicating them here.
cd /var/www/rackhelm
DB_NAME=$(grep '^DB_DATABASE=' .env | cut -d= -f2-)
DB_USER=$(grep '^DB_USERNAME=' .env | cut -d= -f2-)
DB_PASS=$(grep '^DB_PASSWORD=' .env | cut -d= -f2-)

mysqldump --single-transaction --routines --triggers
  -u "$DB_USER" -p"$DB_PASS" "$DB_NAME" | gzip > "$DEST/database.sql.gz"

cp .env "$DEST/env"
tar -czf "$DEST/storage.tar.gz" -C /var/www/rackhelm storage/app

chmod -R 600 "$DEST"
```

`--single-transaction` keeps the dump consistent without locking the tables, so the portal stays usable while it runs.

> **Warning:** The dump and the copied `.env` contain database passwords, the encryption key and your licence key. Store them with the same care as the server itself — restricted permissions, encrypted off-site copy, and never in a world-readable directory.

> **Tip:** Copy the backup off the server. A backup that lives only on the machine it protects is not a backup.

## Restoring

Restoring onto a rebuilt server, in order:

1. **Build a matching platform.** Same OS family, same PHP version with the ionCube Loader active, Redis, nginx. The quickest route is to run the installer with the local-database option so all the packages, the nginx site and the Supervisor program are created for you.
2. **Stop the worker** so it cannot process jobs against a half-restored database:
   ```bash
   sudo supervisorctl stop rackhelm-horizon
   ```
3. **Restore the database.** Create an empty database, then load the dump:
   ```bash
   gunzip -c database.sql.gz | mysql -u root -p rackhelm
   ```
4. **Restore `.env`.** Copy the backed-up file into place, then correct `DB_PASSWORD` if the rebuilt server generated a new one, and `APP_URL` if the hostname changed. *Do not* change `APP_KEY`.
   ```bash
   chown root:www-data /var/www/rackhelm/.env
   chmod 640 /var/www/rackhelm/.env
   ```
5. **Restore storage.** Unpack `storage.tar.gz` over `/var/www/rackhelm`, then fix ownership:
   ```bash
   chown -R www-data:www-data /var/www/rackhelm/storage
   chmod -R u=rwX,g=rX,o= /var/www/rackhelm/storage
   sudo -u www-data php artisan storage:link
   ```
6. **Bring the schema up to date** if the backup came from an older release:
   ```bash
   sudo -u www-data php artisan migrate --force
   ```
7. **Re-add the scheduler cron entry** and restart the worker:
   ```bash
   sudo supervisorctl start rackhelm-horizon
   ```

## After a restore

1. Sign in to the admin area and open a payment gateway and a server integration. If the credentials display correctly, `APP_KEY` is right.
2. Check **Schedules** — the last-run times will be stale until cron fires; confirm they start moving.
3. Check the licence. A new server means a new hardware fingerprint, so arrange rebinding with support if the check fails. See [Licensing and activation](/docs/licensing-and-activation/).
4. If the portal's hostname changed, update `APP_URL`, the **Company URL** setting, the nginx `server_name` and the certificate.

## Troubleshooting

| Symptom | Cause and fix |
| --- | --- |
| Gateway or integration credentials look like random text, or fail to decrypt | `APP_KEY` does not match the one in force when they were saved. Restore the original key from the backed-up `.env`. |
| Invoices and ticket attachments 404 after a restore | `storage/app/private` was not restored, or ownership was not reset to the web-server user. |
| Logos and page images 404 | The `public/storage` symlink was lost. Re-run `php artisan storage:link`. |
| Nothing recurring runs on the restored server | The crontab was not restored — it is not part of the application tree. Re-add the `schedule:run` entry. |
| Jobs from before the outage never resume | Queued jobs live in Redis, not the database, and are not covered by this backup. Anything in flight when the server died is gone; re-drive it from the admin retry actions. |
| Licence rejected on the restored box | New IP address or hostname. The fingerprint changed — contact support. |
