Skip to main content

Migration

Backing up the old server's Coolify install and data, then restoring it onto the new one — the actual old→new cutover, not just standing up Coolify fresh. See the baseline install for installing Coolify itself; this page assumes that's already done on the new server before restoring anything onto it.

What the backup covers

A full backup of this setup covers three distinct layers, and all three are needed to fully recover:

  • Coolify database (services, env vars, domains) — via the pg dump in /data/coolify/backups/
  • Coolify config (SSH keys, proxy config, SSL certs, .env) — via /data/coolify/
  • Service data (admin accounts, files, database contents) — via Docker volumes in /mnt/data/docker/volumes/

The config layer is the one most often lost. Coolify encrypts all service environment variables using APP_KEY from /data/coolify/source/.env. Without that key, the encrypted values in the database are unreadable — you cannot recover your service credentials even if the database itself is intact. Most self-hosted setups lose these keys in a migration and have to reconfigure everything from scratch. Backing up the full /data/coolify/ directory prevents that.

Coolify's built-in backup only covers its own database, not service data. Volumes must be backed up separately.

Backing up

Update the old server's Coolify to the latest version first. The new server's installer (the baseline install step 6, and step 6 below on restore) always pulls whatever's current — backing up an outdated version risks restoring an old DB schema into a newer Coolify that doesn't expect it. Update via Coolify's own UI ("Update available" prompt) or by re-running the install script on the old server:

curl -fsSL https://cdn.coollabs.io/coolify/install.sh | sudo bash

Confirmed clean on 2026-08-14 — no issues updating in place.

Then, run from your local Mac:

rsync -aP --rsync-path="sudo rsync" -e ssh personal-services:/data/coolify/backups/ ~/Desktop/coolify-backup/
rsync -aP --rsync-path="sudo rsync" -e ssh personal-services:/mnt/data/docker/volumes/ ~/Desktop/docker-volumes-backup/
rsync -aP --rsync-path="sudo rsync" -e ssh personal-services:/data/coolify/ ~/Desktop/coolify-config-backup/

Stop Docker before backing up to avoid writes mid-transfer:

sudo docker stop $(sudo docker ps -q)
sudo systemctl stop docker docker.socket containerd

Restoring

1. Set up the new server

Follow the baseline install completely before restoring.

2. Upload backups to the new server

The new server has no SSH config alias — personal-services is the old server's, a namespace collision worth knowing about before pasting these blind. Use the real address, through the pass-cli agent (see Basics):

export SSH_AUTH_SOCK=~/.ssh/proton-pass-agent.sock
rsync -aP -e ssh ~/Desktop/coolify-backup/ debian@<new-server-ipv4>:/tmp/coolify-restore/
rsync -aP -e ssh ~/Desktop/docker-volumes-backup/ debian@<new-server-ipv4>:/tmp/volumes-restore/
rsync -aP -e ssh ~/Desktop/coolify-config-backup/ debian@<new-server-ipv4>:/tmp/coolify-config-restore/

If rsync: command not found comes back from the remote side, it's not installed on a fresh Debian image — sudo apt-get install -y rsync on the new server first, then retry.

3. Stop Coolify

sudo docker stop $(sudo docker ps -q)

4. Restore config and volumes

sudo rsync -aP /tmp/coolify-config-restore/ /data/coolify/
sudo rsync -aP /tmp/volumes-restore/ /mnt/data/docker/volumes/

5. Add the old APP_KEY(s) as APP_PREVIOUS_KEYS

Check what's actually there first — grep APP_KEY matches APP_PREVIOUS_KEYS too, since it's a substring:

grep "^APP_" /tmp/coolify-config-restore/source/.env

If the old server had rotated its key before, it'll already have its own APP_PREVIOUS_KEYS — carry all of those forward, comma-separated, plus its final APP_KEY, not just the one value. Otherwise data encrypted under whichever older key got dropped becomes permanently unreadable:

echo "APP_PREVIOUS_KEYS=<old APP_KEY>,<old APP_PREVIOUS_KEYS, if any>" | sudo tee -a /data/coolify/source/.env

Worth checking whether APP_KEY actually changed from the old server's — if the installer didn't overwrite an existing .env, it may just be the same key carried straight across, not a new one.

6. Restart via the install script

curl -fsSL https://cdn.coollabs.io/coolify/install.sh | sudo bash

This does not bring the proxy back up. If step 3's docker stop $(docker ps -q) stopped coolify-proxy (Traefik) and coolify-sentinel along with everything else, the install script only restarts the core coolify/coolify-db/coolify-redis/coolify-realtime containers — the proxy and sentinel are left however they were. Check:

sudo docker ps -a | grep -E "coolify-proxy|coolify-sentinel"

If either shows Exited, start them manually:

sudo docker start coolify-proxy coolify-sentinel

Without this, DNS can resolve correctly, SSH/validation can pass, and the core Coolify app can be reachable directly on its own port — and the actual domain (https://dashboard.<domain>) still won't load, because nothing is listening on 80/443 to route it. This is the single most likely thing to look broken after a restore that isn't.

7. Fix SSH access

Coolify connects to the server via SSH as itself, to manage containers on "localhost." Check what key(s) exist first — a fresh install followed by a config restore can leave more than one:

sudo ls -la /data/coolify/ssh/keys/

If there's more than one and it's unclear which the restored database actually references, don't try to figure out which is "the" real one — just trust both. authorized_keys holds multiple entries fine, and it's harmless either way:

sudo ssh-keygen -y -f /data/coolify/ssh/keys/<key_file_1> | sudo tee -a /root/.ssh/authorized_keys
sudo ssh-keygen -y -f /data/coolify/ssh/keys/<key_file_2> | sudo tee -a /root/.ssh/authorized_keys

Then in Coolify: Servers → localhost → Validate & configure.

8. Redeploy services

In the Coolify UI, redeploy each service. The volumes with all data are already in place — they will reconnect automatically. The dashboard itself being reachable only confirms Coolify's core is running, not that any individual service behind another subdomain has actually been redeployed yet.

Service-specific issues hit during redeploy are documented on that service's own page — see Authentik for the permission error hit there.