Migrate Coolify to a New VPS: Database, Keys and App Data

Published on

Move Coolify to a new VPS: dump its database, save APP_KEY and the SSH keys, install the same version, restore, bring back app volumes, then switch DNS.

A VPS control panel listing one Debian server with its RAM, cores, disk and status

You want Coolify and everything it runs on a new VPS, and Coolify has no single export for that. Its instance backup holds Coolify's own database, but not the key that decrypts the credentials in it, and none of your apps' data. Forget the key and the new dashboard shows an encryption error; forget the volumes and your apps come back empty. This guide follows Coolify's restore guide for the usual setup, Coolify and its apps on one server, on Ubuntu 24.04 or Debian 12.

Quick answer: On the old server, note the Coolify version, disable automatic updates, stop your apps and dump the coolify-db database. Save what the dump leaves out: APP_KEY from /data/coolify/source/.env, the files in /data/coolify/ssh/keys/, Coolify's line in /root/.ssh/authorized_keys, your apps' volumes and the resource folders under /data/coolify. On the new VPS, install the same version, stop coolify and coolify-redis, set the old APP_KEY (current Coolify doesn't use APP_PREVIOUS_KEYS), run pg_restore, swap in the old SSH keys and rerun the installer. Then restore each app's data, redeploy, test and switch DNS.

What Has to Move

Coolify's backup guide says an instance backup "does not back up data stored inside your applications, databases, or volumes." This is everything that has to move:

WhatWhere on the old server
Coolify's database: projects, resources, settings, saved credentialsDocker volume coolify-db, used by the coolify-db container
APP_KEY, which encrypts those credentials and private keys/data/coolify/source/.env
SSH keys Coolify uses to reach its servers/data/coolify/ssh/keys/, plus Coolify's public key in /root/.ssh/authorized_keys
Your apps' named volumesDocker's own storage; the names start with the resource's UUID
File Mounts and other per-resource files/data/coolify/applications, /data/coolify/services and /data/coolify/databases
Your own Traefik files and Compose override, if any/data/coolify/proxy/dynamic/ and /data/coolify/source/docker-compose.custom.yml

A copy of /data/coolify alone leaves the database behind: Coolify's Compose file keeps it in the coolify-db volume. Coolify "prefixes the name with the resource UUID" for volume mounts, and File Mounts live in those resource folders. Apps on remote servers stay where they are.

Step 1: Prepare Both Servers

Work as root on both servers (sudo -i): only root can read Coolify's files, and its installer stops with "Please run this script as root or with sudo" otherwise. On the old server:

grep -E '^(APP_KEY|COOLIFY_VERSION)=' /data/coolify/source/.env

Save the APP_KEY=... line in your password manager. COOLIFY_VERSION is the installed version, written by Coolify's upgrade script; if it's missing or says latest, read the version at the top left of the dashboard sidebar. Then set Settings > Updates > Automatic updates to Disabled, as the downgrade guide does, so the version stays put.

Before the day of the move:

  • Lower your DNS records' TTL a day ahead; our website move guide shows how.
  • If your remote servers accept SSH only from the old Coolify server, as Coolify's firewall guide recommends, allow the new IP too.
  • On the new VPS, open ports 22, 80, 443, 8000, 6001 and 6002 in your provider's firewall, or in UFW with ufw-docker. Coolify says not to rely on plain UFW, which Docker-published ports get past (our Docker and UFW guide explains).

Coolify needs 2 CPU cores, 2 GB RAM and 10 GB of free disk plus "additional space for Docker images, volumes, and backups" (install docs); count your archives too. A Space-Node VPS is KVM with full root access, and its order form offers Ubuntu 24.04 LTS. New to Coolify? See our Coolify and Dokploy guide.

Step 2: Stop the Apps and Take the Final Copy

In the old dashboard, stop every application and service. Databases must run for a dump: on each one's Backups page, add a schedule if needed, select Back up now and wait for Success (database backups). Then stop them too. Note any bind mount whose Source Path lies outside /data/coolify (each resource's Persistent Storage page lists them).

In the terminal, save Coolify's public key for localhost:

mkdir -p /data/backups /root/coolify-move/volumes
grep -E '[[:space:]](root@)?coolify[[:space:]]*$' /root/.ssh/authorized_keys > /root/coolify-move/coolify.pub
cat /root/coolify-move/coolify.pub

Expect a line ending in coolify (root@coolify on installs from before September 2024), the key Coolify's installer adds. If it's empty, localhost uses your own key: copy its public key from Keys & Tokens > Private Keys (SSH keys) into that file now.

Then dump Coolify's database as its backup guide does, check the file, and stop the old control plane so no webhook deploys anything afterwards. Coolify's migration guide says to keep the source "unchanged".

docker exec coolify-db \
  pg_dump --format=custom --no-acl --no-owner \
  --username=coolify coolify \
  > /data/backups/coolify-db-manual-backup.dmp
test -s /data/backups/coolify-db-manual-backup.dmp \
  && ls -lh /data/backups/coolify-db-manual-backup.dmp
docker stop coolify coolify-redis

Archive your apps' volumes with Docker's volume backup method: a throwaway container mounts the volume read-only and writes a tar file. Skip coolify-db and coolify-redis, which are Coolify's own.

docker volume ls
V=VOLUME_NAME                        # one name from the list
docker run --rm -v "$V":/data:ro -v /root/coolify-move/volumes:/backup ubuntu:24.04 \
  tar czf "/backup/$V.tar.gz" -C /data .

Repeat the last two lines per volume and keep the list; our Docker volume guide explains the method. Then pack the dump, keys and resource folders, and copy it all over:

cp /data/backups/coolify-db-manual-backup.dmp /root/coolify-move/
cp /data/coolify/source/docker-compose.custom.yml /root/coolify-move/   # only if you have one
tar --numeric-owner -czf /root/coolify-move/ssh-keys.tar.gz -C / data/coolify/ssh/keys
tar --numeric-owner -czf /root/coolify-move/coolify-data.tar.gz -C / \
  data/coolify/applications data/coolify/services data/coolify/databases \
  data/coolify/backups data/coolify/proxy/dynamic
scp -r /root/coolify-move root@NEW_VPS_IP:/root/

Add your noted bind-mount paths to the second tar, without the leading /. Then leave the old server as it is: it's your way back.

Step 3: Install the Same Version on the New VPS

As root on the new VPS, install the version you wrote down. 4.5.3 stands for it here; that was the latest stable release on 31 October 2026, per Coolify's versions file.

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

The restore guide asks for "the version that was installed when the backup was created", and the downgrade guide warns that "an older Coolify release may not work with database changes made by a newer release." Update after the move.

Then open http://NEW_VPS_IP:8000 and create an admin account at once: whoever reaches the registration page first "can become the instance admin and gain root access to your server," the install docs warn. The installer gives you 10 minutes; after that, run docker restart coolify and try again. The restore replaces this account.

Step 4: Restore Coolify's Database and Keys

This is the restore guide's "Replacement server" path. Check the dump, stop the containers that use the database while coolify-db keeps running, and open .env:

mkdir -p /data/backups
cp /root/coolify-move/coolify-db-manual-backup.dmp /data/backups/
export BACKUP_PATH=/data/backups/coolify-db-manual-backup.dmp
test -s "$BACKUP_PATH" && ls -lh "$BACKUP_PATH"
docker stop coolify coolify-redis
nano /data/coolify/source/.env

Releases before 4.4 also run a coolify-realtime container; stop it too. In .env, replace only the value after APP_KEY= with your saved one. Keep the other lines: values such as DB_PASSWORD "belong to the new installation and must continue to match its running containers." Don't add APP_PREVIOUS_KEYS, which older tutorials use: "Current Coolify reads APP_KEY."

Load the dump:

docker exec -i coolify-db pg_restore \
  --clean \
  --if-exists \
  --exit-on-error \
  --no-acl \
  --no-owner \
  --username=coolify \
  --dbname=coolify \
  < "$BACKUP_PATH"

--clean drops each object before restoring it, and --exit-on-error stops at the first error (pg_restore), so a failed restore can't pass for a good one.

Swap in the old SSH keys and authorize the old public key:

rm -f /data/coolify/ssh/keys/*
tar --numeric-owner -xzpf /root/coolify-move/ssh-keys.tar.gz -C /
cat /root/coolify-move/coolify.pub >> /root/.ssh/authorized_keys

Run the installer again with the same version. It starts Coolify and "applies any required database migrations":

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

This run keeps your work: the installer only fills empty or missing .env values, makes a new SSH key only when no coolify-db volume exists, and gives /data/coolify/ssh back to the coolify container's user, UID 9999.

Step 5: Check the Restored Instance

Log in at http://NEW_VPS_IP:8000 with your old account and run the restore guide's checks: no encryption or "Invalid MAC" error, your projects and resources are listed, Servers > localhost validates and your other servers connect.

Three settings came along with the database:

  • Settings > General: URL should be your dashboard's address, and Instance public IPv4 and IPv6 empty or new; empty lets "Coolify use the detected address" (instance settings).
  • Servers > localhost > Docker Cleanup: keep Delete unused volumes off for now; it "deletes every Docker volume that is not currently attached to a container" (Docker cleanup).
  • Servers > localhost > Proxy: select Start Proxy if it isn't running (proxy overview).

Step 6: Restore the Apps' Data

Unpack the resource folders after the installer's last run. Coolify's update guide warns that install.sh on an existing instance "resets ownership and permissions under /data/coolify, including managed service data". The script as of 31 October 2026 only sets them on folders it creates, but unpacking last is safe either way.

tar --numeric-owner -xzpf /root/coolify-move/coolify-data.tar.gz -C /

--numeric-owner restores owners by number, not name, so the files keep the user IDs your containers expect.

Then bring the resources back, databases first so no app starts against an empty one. For each database you dumped, Deploy it, open Import Backup, enter the dump's absolute path and select Check file (restore a database); its backup executions show where the dump sits under /data/coolify/backups. Redis, Dragonfly, KeyDB and ClickHouse have no import, so restore their volumes like the apps', as the migration guide does:

  1. Deploy the resource, then Stop it, so its volumes exist but nothing uses them.

  2. Check with docker volume ls that every name on your list exists. Docker creates a missing volume without asking, so a typo restores into a volume nothing uses.

  3. Extract each archive into its volume:

    V=VOLUME_NAME
    docker run --rm -v "$V":/data -v /root/coolify-move/volumes:/backup ubuntu:24.04 \
      tar xzf "/backup/$V.tar.gz" --numeric-owner -C /data
    
  4. Deploy the resource again and read its logs.

The volume archives of databases you imported are spare copies.

Step 7: Test, Then Switch DNS

From your own computer, test each domain against the new IP:

curl -k -I --resolve app.example.com:443:NEW_VPS_IP https://app.example.com/

--resolve makes curl use that address instead of DNS, and -k accepts the self-signed certificate the Coolify proxy serves until Let's Encrypt can reach the new server (certificate troubleshooting). A 200, or the app's usual redirect, means the proxy found the app.

Then point the A records (and AAAA, if the new server's IPv6 works) of the dashboard domain, every app domain and any wildcard at the new IP, and check with dig +short app.example.com A (Coolify DNS). Do it in the same sitting as the test: certificate requests fail while DNS still points at the old server, and Let's Encrypt allows "up to 5 authorization failures per identifier" per hour (rate limits).

Then fix what still holds the old address:

  • Domains ending in .sslip.io contain the old IP: Coolify generates them "from the server IP address". Give those resources a new domain.
  • If you used the dashboard by IP, update the GitHub App's webhook (/webhooks/source/github/events, see GitHub App), manual Git webhooks and deploy webhooks in your CI. With a domain, the DNS change covers them.
  • Add a copied docker-compose.custom.yml to /data/coolify/source/ only once the dashboard works through its domain: if it unbinds ports 8000, 6001 and 6002, it locks you out before that. Then run curl -fsSL https://cdn.coollabs.io/coolify/upgrade.sh | bash -s 4.5.3, which loads that file.

Keep the old VPS stopped but intact for a while. To roll back, point DNS back, run docker start coolify-redis coolify there and start the apps; the migration guide warns against sending traffic back "to an older source database without accounting for writes accepted by the destination." When all is well, take an instance backup, update, and re-enable automatic updates if you used them. Cron jobs and other things outside Coolify are covered in our VPS migration plan.

If It Still Fails

Encryption or "Invalid MAC" error. The active APP_KEY isn't the one that encrypted the restored data. As the restore guide says: stop coolify, put the saved value in /data/coolify/source/.env and run the installer again.

pg_restore stops with an error. Keep coolify and coolify-redis stopped and read the first error. Check that the file isn't empty and was made with pg_dump --format=custom, then rerun the restore.

localhost fails validation with Server is not reachable. Reason: root@host.docker.internal: Permission denied (publickey). (validation issues). The old public key is missing from /root/.ssh/authorized_keys, or the old key files from /data/coolify/ssh/keys/. Repeat those lines of Step 4.

A remote server is unreachable. On its Private Key page, use Copy public key and check that the key is in the remote user's ~/.ssh/authorized_keys, and that the server's firewall allows SSH from the new IP.

An app comes back empty. Stop it, compare docker volume ls with your list, extract again and redeploy.

HTTPS shows a certificate warning. The proxy still serves its self-signed certificate. Check that DNS returns the new IP and that port 80 is open, since Let's Encrypt's HTTP challenge uses it.

FAQ

Can't I just copy /data/coolify to the new server? Not on its own: Coolify's database and your apps' named volumes are Docker volumes outside that folder. And Coolify warns that copying "live database files can produce an inconsistent or unusable copy."

Do I need APP_PREVIOUS_KEYS? Not on current Coolify. Its restore guide says to put the old value in APP_KEY and to "not add APP_PREVIOUS_KEYS". Older guides on the web still use that variable.

Can I restore from Coolify's S3 instance backup instead of my own dump? Yes. Download the .dmp file from your S3 provider to the new server and point BACKUP_PATH at it. You still need the key: "S3 instance backups include only the database backup file."

How long are my apps offline? From when you stop them until DNS sends visitors to the new server, which takes up to the TTL resolvers cached. Apps on remote servers keep running.

Are backups included with a Space-Node VPS? Not in the plan price of our VPS hosting, but you can arrange them through a support ticket. Coolify can also send its instance and database backups to S3-compatible storage.

Sources

All opened on 31 October 2026: