VPS Snaps

How to back up and restore Coolify

Coolify has three separate backups, and none of them runs until you set it up: scheduled dumps of each database, scheduled archives of application and service volumes (Coolify 4.3 and later), and a backup of the Coolify instance's own database under Settings > Backup. Send all three to S3-compatible storage, and keep the APP_KEY from /data/coolify/source/.env off the server, because no Coolify backup includes it.

10 min readUpdated Checked against official documentation

What Coolify backs up, and what it doesn't

Coolify keeps its own records in a PostgreSQL container called coolify-db and runs your apps and databases as Docker containers on the servers you connect, including the Coolify host itself (localhost). Each layer has its own backup:

DataCoolify's backupDashboard restore
PostgreSQL, MySQL, MariaDB, MongoDBScheduled database backupsYes, Import Backup
ClickHouseScheduled database backupsNo
Redis, Dragonfly, KeyDBNoneNo
Volume and directory mounts of apps and servicesStorage backups, 4.3.0 and laterNo
File mounts and host file mountsNoneNo
Projects, settings, saved credentialsInstance backup of coolify-dbNo, a terminal procedure
APP_KEY and the SSH keys in /data/coolify/ssh/keysNoneCopy them yourself

On Coolify Cloud the Coolify team backs up the instance, but your apps and databases are still yours. For Redis, see backing up Redis.

Add S3-compatible storage

By default every Coolify backup stays on the server that made it; the S3 copy is the one that survives losing that server. Create the bucket before you start, because Coolify validates the connection by listing it. Then open S3 Storages in the sidebar, select Add, enter the endpoint, bucket, region and keys, and select Validate Connection & Continue. Enter the endpoint without the bucket name, for example https://s3.eu-central-1.amazonaws.com.

Uploads go from the server that made the backup. Coolify's AWS guide grants s3:ListBucket, s3:GetObject, s3:PutObject, s3:DeleteObject, s3:GetObjectAcl and s3:PutObjectAcl; delete is needed because S3 retention removes old copies.

That key can delete every backup in the bucket. Use a bucket only for backups and turn on versioning or Object Lock, as in protecting backups from ransomware and the S3 bucket guide.

Schedule database backups

  1. Open the database and select Backups.
  2. Select + Add next to Scheduled Backups, enter a frequency and select Save.
  3. Open the schedule. Leave Databases To Backup empty for the resource's own database, or list names separated by commas.
  4. In the S3 section, enable S3, pick the storage and select Save.
  5. Select Backup Now. The execution should show Success, a size above zero and, with S3 on, a successful upload.

Frequency takes a cron expression or hourly, daily (0 0 * * *), weekly, monthly and similar names, in the server's timezone (or the instance's, if the server has none). Timeout defaults to 3600 seconds. A schedule that fires while the database is stopped is skipped.

For PostgreSQL, Coolify runs pg_dump --format=custom --no-acl --no-owner inside the database container with docker exec, so the client always matches the server. The file lands under /data/coolify/backups/databases/ on the database's server as pg-dump-<database>-<timestamp>.dmp; Backup All Databases uses pg_dumpall and gzip instead. In the bucket, the object key repeats the local path without the leading slash.

Retention has three limits for local copies and three for S3: Number of backups to keep, Days to keep backups and Maximum storage (GB). 0 means no limit, and reaching any one deletes the oldest backups. Keeping 7 local and 30 S3 copies of a daily backup gives a week on the server and a month in the bucket.

Disable Local Backup deletes the server copy after a successful upload, but the dump is still written to disk first, so leave room for one. If the upload fails, the file stays and the execution shows an S3 warning.

For alerts, select Backup Failure for a channel under Notifications. It covers failed dumps and failed uploads, but a dead instance sends nothing, so check the bucket now and then.

Back up volumes and directory mounts

Coolify 4.3.0 (August 2026) added storage backups: scheduled .tar.gz archives of the volume and directory mounts of applications and services. Open the application, select Backups, then Add under Scheduled Backups, choose the mount as Backup Target and set a frequency.

  • Stop containers while creating the archive stops the containers that use the mount, archives it and starts them again. Without it, files can change mid-read. Turn it on for anything that rewrites files in place.
  • Retention has the same local and S3 limits; the number to keep defaults to 7.
  • With S3 on and Disable Local Backup set, the archive streams straight to the bucket. Otherwise it goes under /data/coolify/backups/volumes/ first.

Coolify has no restore button for storage archives, so test one as shown below. For a database's own volume, use the database backups: a file copy of a running database may not start.

On older versions, or for volumes Coolify does not manage, use a helper container as in backing up Docker volumes. Coolify puts the resource's UUID in each volume name, so this lists one resource's volumes:

Terminal
docker volume ls --filter name=<resource-uuid>

Keep Delete Unused Volumes off in the server's Docker cleanup settings. It deletes every volume no container is attached to, including a stopped app's data.

Back up the Coolify instance

Open Settings > Backup and select Configure Backup. Coolify creates a daily schedule for coolify-db. Set retention, turn on S3 Enabled, and select Backup Now. If Coolify says instance backups are disabled, validate the Coolify server first.

That backup is only the database. Coolify encrypts environment variable values, SSH private keys and S3 credentials with the APP_KEY in /data/coolify/source/.env, and no Coolify backup includes that file. Restore without the matching key and the dashboard fails with encryption or Invalid MAC errors. Print it and store it in a password manager:

Terminal
grep '^APP_KEY=' /data/coolify/source/.env

A move also needs /data/coolify/ssh/keys/, the keys Coolify uses to reach your servers. This script uses the dump command from Coolify's docs, so it works without the dashboard, reads the dump back (pg_restore -f /dev/null) so a truncated file fails, and archives .env and the keys:

/usr/local/bin/coolify-instance-backup.sh
#!/usr/bin/env bash
set -euo pipefail

BACKUP_DIR="/var/backups/coolify"
KEEP_DAYS=14
STAMP="$(date +%Y-%m-%d_%H%M)"
DUMP="$BACKUP_DIR/coolify-db-$STAMP.dmp"

mkdir -p "$BACKUP_DIR"
chmod 700 "$BACKUP_DIR"
trap 'rm -f "$DUMP.partial"' EXIT

docker exec coolify-db pg_dump --format=custom --no-acl --no-owner --username=coolify coolify > "$DUMP.partial"
docker exec -i coolify-db pg_restore -f /dev/null < "$DUMP.partial"
mv "$DUMP.partial" "$DUMP"

tar czf "$BACKUP_DIR/coolify-files-$STAMP.tar.gz" -C / data/coolify/source/.env data/coolify/ssh/keys

find "$BACKUP_DIR" -type f -mtime +"$KEEP_DAYS" -delete
Terminal
sudo chmod 700 /usr/local/bin/coolify-instance-backup.sh
/etc/cron.d/coolify-instance-backup
15 3 * * * root /usr/local/bin/coolify-instance-backup.sh >> /var/log/coolify-instance-backup.log 2>&1

The files archive holds the APP_KEY, the database password and private keys for every server Coolify manages. Encrypt it before copying /var/backups/coolify off the machine, for example with rclone. Note the Coolify version shown in the dashboard too; a restore should use the same one.

Restore a database backup

Stop whatever writes to the database, open it (it must be running) and select Configuration > Import Backup. Choose Restore from File for a server path or an upload, or Restore from S3. Once Coolify shows the file information, run the restore and confirm. For S3 File Path (within bucket), the location shown on the execution works.

Read the Custom Import Command first. For PostgreSQL it is pg_restore with a user and database name but no --clean, so over tables that still exist you get already exists errors and a mix of old and restored data. Restore into a new, empty database resource, or add --clean --if-exists.

Backup includes all databases drops every non-template database in the container, recreates the default one and loads a pg_dumpall file with psql. Use it only for an all-databases backup.

From the server's shell, find the container with docker ps. For a standalone database its name is the resource's UUID, also the host in its Internal URL.

Terminal
docker exec -i <container> sh -c 'pg_restore --clean --if-exists --exit-on-error -U "$POSTGRES_USER" -d "$POSTGRES_DB"' < pg-dump-<database>-<timestamp>.dmp
  • --clean --if-exists drops each object before recreating it, without an error when it does not exist yet.
  • --exit-on-error stops at the first error; by default pg_restore carries on and counts errors.
  • The single quotes leave $POSTGRES_USER and $POSTGRES_DB for the shell inside the container, which has them in its environment.

Coolify dumps without owners or privileges, so restored objects belong to the restoring user. If your app connects as another role, grant it access again; see PostgreSQL roles and the pg_dump guide.

Restore a volume archive

Download the archive from the execution or the bucket into /root/restore. Coolify archives the volume's root, so extract it into the root of a volume. Try a scratch volume first and count the files:

Terminal
docker volume create restore-test
Terminal
docker run --rm -v restore-test:/data -v /root/restore:/backup:ro alpine tar xzf /backup/<archive>.tar.gz --numeric-owner -C /data
Terminal
docker run --rm -v restore-test:/data:ro alpine sh -c 'find /data -type f | wc -l'

To restore in place, stop the application in Coolify, empty the real volume and extract into it, then start the application. For a directory mount, extract on the host into its source path instead.

Terminal
docker run --rm -v <volume>:/data -v /root/restore:/backup:ro alpine sh -c 'find /data -mindepth 1 -delete && tar xzf /backup/<archive>.tar.gz --numeric-owner -C /data'

Move Coolify to a new server

You need the .dmp, the saved APP_KEY, the old /data/coolify/ssh/keys/ and the old version number. Install that version on the new machine and copy the dump there:

Terminal
curl -fsSL https://cdn.coollabs.io/coolify/install.sh | sudo bash -s <version>

Stop the containers that use the instance database, leaving coolify-db running:

Terminal
docker stop coolify coolify-redis coolify-realtime

In /data/coolify/source/.env, replace only the APP_KEY value with the saved one; keep the new DB_PASSWORD. Then restore:

Terminal
docker exec -i coolify-db pg_restore --clean --if-exists --exit-on-error --no-acl --no-owner --username=coolify --dbname=coolify < /root/coolify-db-<date>.dmp

Delete the keys the fresh install made, copy the old ones into /data/coolify/ssh/keys/, and put the old public key in ~/.ssh/authorized_keys:

Terminal
rm -f /data/coolify/ssh/keys/*

Run the install command again with the same version; it starts Coolify and applies migrations. Check that Servers > localhost validates and your other servers connect. Apps that ran on the old host come back as configuration only: redeploy them and restore their data. See also migrating to a new provider.

Test the restores

A successful execution only proves Coolify wrote a file. Once a month:

  • Database: create a PostgreSQL resource of the same major version in a test project, run Import Backup > Restore from S3 with the latest file, count rows in key tables, then delete it.
  • Volumes: extract the latest archive into a scratch volume and compare file counts with the live one.
  • Instance: restore onto a throwaway VM, skip the authorized_keys step and block the VM's outgoing connections to your servers. The restored database holds working keys to them. Check that the dashboard opens and lists your projects, then delete the VM.
Terminal
docker exec <test-container> sh -c 'psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" -c "SELECT count(*) FROM users"'

Write down how long the import took: that is your real recovery time. More in testing a restore.

Common errors

ErrorFix
Database must be running to restore a backup.Start the database, then import.
Execution shows an S3 warningThe dump is on the server but the upload failed. Fix the endpoint, keys or bucket and run Backup Now.
Backup Now does nothing for a MySQL or MariaDB serviceMap MYSQL_DATABASE or MARIADB_DATABASE in the Compose file and redeploy.
already exists errors during Import BackupAdd --clean --if-exists, or restore into an empty database.
Invalid MAC after an instance restorePut the saved APP_KEY in /data/coolify/source/.env and run the installer again.
New server fails validation after a moveCopy the old SSH keys and put the matching public key in ~/.ssh/authorized_keys.

Frequently asked questions

Does Coolify back up Docker volumes?
Since 4.3.0, yes: storage backups archive app and service volumes on a schedule, locally and to S3. There is no restore button; you extract the archive yourself.
Where does Coolify save database backups?
Under /data/coolify/backups/databases/ on the database's server and, with S3 on, under the same path in your bucket.
Does a Coolify instance backup include my apps?
No. It holds Coolify's own database: projects, settings, credentials and deployment history. App and database data need their own backups.
What happens if I lose the APP_KEY?
A restored instance cannot decrypt the environment variables, private keys and S3 credentials in its database. Save the key off the server now.
Can Coolify back up Redis?
Not with scheduled backups. Redis, Dragonfly and KeyDB are not supported; back up their persistence files instead.

How this was checked

Commands, limits and prices were checked against these official pages, on October 3, 2026: