How to back up a Dokku server
Dokku has no single backup command. Export each database with its plugin (dokku postgres:export, or postgres:backup straight to S3), save each app's variables with dokku config:export --format envfile, and archive the directories Dokku keeps its state in: /home/dokku, /var/lib/dokku/config, /var/lib/dokku/data, /var/lib/dokku/services and /var/lib/dokku/plugins. App code comes back with git push.
What a Dokku server holds
Dokku is an open-source platform that builds and runs apps on one host, and it keeps all of its state on that host. The commands below were checked against Dokku 0.38.31 and the Postgres plugin 2.2.0, the current releases in October 2026.
| What | Where | How to back it up |
|---|---|---|
| App source | A git repository per app under /home/dokku, plus your own clone | Keep your remote repository; git push restores the app |
| Config variables and plugin settings | /var/lib/dokku/config | dokku config:export, and the directory archive |
| Domains, nginx config and certificates | /home/dokku/<app> and /var/lib/dokku/config; certificates in /home/dokku/<app>/tls | The directory archive, with domains:report as a readable list |
| Uploads and other files apps keep | Storage mounts, by convention /var/lib/dokku/data/storage/<app> | Copy the directory |
| Databases | Plugin services in /var/lib/dokku/services | The plugin's export or backup command |
| Plugins | /var/lib/dokku/plugins | The directory archive |
Storage only lands under /var/lib/dokku/data/storage if you mount it from there, as Dokku's backup guide recommends, so one archive covers every app. dokku storage:list <app> shows an app's mounts.
Export the databases
Each official datastore plugin dumps its service to standard output. For a Postgres service named lollipop (see dokku postgres:list):
dokku postgres:export lollipop > lollipop.dumpThe plugin runs pg_dump -Fc --no-acl --no-owner inside the service container, so the dump is PostgreSQL's compressed custom format, made by the server's own version, without owners or grants. postgres:import loads it with pg_restore --clean --if-exists, which replaces existing objects:
dokku postgres:import lollipop < lollipop.dumpThe MySQL, MariaDB, MongoDB and Redis plugins have the same export and import commands. pg_dump reads a consistent snapshot while the database runs; a copy of the files under /var/lib/dokku/services may contain partially written data, as Dokku's backup guide warns. More in the pg_dump guide and restoring a dump.
Send database backups to S3 on a schedule
The plugin can also upload backups itself, to AWS S3 or any S3-compatible storage. Give it a key for the bucket first:
dokku postgres:backup-auth lollipop <access-key-id> <secret-access-key> <region> s3v4 <endpoint-url>On AWS itself you can stop after the region. Each call replaces every stored setting, so repeat them all when you change one, and use a key limited to the backup bucket, as in the S3 bucket guide. Then set a passphrase, run a backup and schedule it:
dokku postgres:backup-set-encryption lollipop <passphrase>dokku postgres:backup lollipop my-dokku-backupsdokku postgres:backup-schedule lollipop "0 3 * * *" my-dokku-backups- Each backup dumps the database to a temporary file on the host, so leave room for one dump, then uploads
postgres-lollipop-<timestamp>.tgzwith the dump inside asbackup/export. The timestamp is UTC, such as2026-10-04-03-00-01. - With a passphrase set, the upload is encrypted with GPG (AES256) and ends in
.tgz.gpg. Keep the passphrase off the server: without it the backups cannot be read. - The bucket name may end in a path, such as
my-dokku-backups/postgres, to upload under a prefix. - The schedule is a cron expression or a word such as
@daily. It goes into the dokku user's crontab, output is appended to/var/log/dokku/postgres.log, anddokku postgres:backup-schedule-cat lollipopprints the line.
The plugin does not delete old backups. Add a lifecycle rule to the bucket so they expire, as in choosing a retention policy.
dokku cron:list --global lists scheduled backups. Dokku writes that crontab only when the global scheduler or at least one app uses the docker-local scheduler, so a k3s-only host runs no scheduled backups.
Save app config, domains and mounts
config:export prints an app's variables. The envfile format writes one dotenv line per variable, which config:import (Dokku 0.37 and later) reads back; on older versions, set them again with config:set. --global exports the variables every app inherits:
dokku config:export --format envfile shop > shop.envdokku config:export --format envfile --global > global.envThese files hold every secret the apps use: keep them readable by root only and encrypt them before they leave the server. Record each app's domains and mounts too:
dokku domains:report shop --domains-app-vhostsdokku storage:list shop --format jsondokku certs:show shop crt and dokku certs:show shop key print a certificate you added yourself. Certificates from the Let's Encrypt plugin need no backup: it issues new ones on the new server.
Archive Dokku's own directories
Dokku's backup guide archives five directories, at a time when no Dokku command or deploy is running:
sudo tar -czf dokku-files.tar.gz -C / home/dokku var/lib/dokku/config var/lib/dokku/data var/lib/dokku/services var/lib/dokku/pluginsThat covers the app repositories, every plugin's settings, the storage under /var/lib/dokku/data, each service's configuration and password, and the installed plugins. It also copies database files while they run, which can be caught mid-write, so restore databases from the dumps. Because those files change during the copy, GNU tar may exit with status 1, which means some files changed while being archived.
A nightly backup script
#!/usr/bin/env bash
set -euo pipefail
umask 077
BACKUP_DIR="/var/backups/dokku"
KEEP_DAYS=14
SERVICES="lollipop"
OUT="$BACKUP_DIR/$(date +%Y-%m-%d_%H%M)"
mkdir -p "$OUT"
dokku config:export --format envfile --global > "$OUT/global.env"
for APP in $(dokku --quiet apps:list); do
dokku config:export --format envfile "$APP" > "$OUT/$APP.env"
dokku domains:report "$APP" --domains-app-vhosts > "$OUT/$APP.domains"
dokku storage:list "$APP" --format json > "$OUT/$APP.storage.json"
done
for SVC in $SERVICES; do
dokku postgres:export "$SVC" > "$OUT/$SVC.dump"
head -c 5 "$OUT/$SVC.dump" | grep -q PGDMP
done
tar -czf "$OUT/dokku-files.tar.gz" -C / home/dokku var/lib/dokku/config \
var/lib/dokku/data var/lib/dokku/services var/lib/dokku/plugins || [ "$?" -eq 1 ]
find "$BACKUP_DIR" -mindepth 1 -maxdepth 1 -type d -mtime +"$KEEP_DAYS" -exec rm -rf {} +sudo chmod 700 /usr/local/bin/dokku-backup.sh30 2 * * * root /usr/local/bin/dokku-backup.sh >> /var/log/dokku-backup.log 2>&1- List your Postgres services in
SERVICES. A custom-format dump starts with the bytesPGDMP, so theheadcheck stops the script on an empty or failed export. umask 077makes every file readable by root only.dokku --quiet apps:listprints app names without the header.|| [ "$?" -eq 1 ]accepts tar's status 1 for files that changed, and still stops on real errors (status 2).- The
findline deletes dated folders older than 14 days.
Schedule it away from deploys and the plugin's own backups, and copy /var/backups/dokku off the server, for example with rclone. More in the cron guide.
Restore onto a new server
Install the same Dokku version as the old server (dokku version prints it) on Ubuntu or Debian, with that version in the bootstrap URL and DOKKU_TAG:
wget -NP . https://dokku.com/install/v0.38.31/bootstrap.shsudo DOKKU_TAG=v0.38.31 bash bootstrap.shRoute 1, the whole archive. Extract it over the fresh install, rerun the plugins' install steps, which a restore does not trigger, start each service, load its dump and rebuild the apps:
sudo tar -xzf dokku-files.tar.gz -C /sudo dokku plugin:installsudo dokku plugin:install-dependenciesdokku postgres:start lollipopdokku postgres:import lollipop < lollipop.dumpdokku ps:rebuild --allOn a server with a different CPU architecture, run rm -rf /home/dokku/.basher straight after extracting. Recreate the Docker networks that dokku network:report lists with dokku network:create, and redeploy apps that came from an image (git:from-image) with the command you first used, since ps:rebuild may fail for them. Then check dokku cron:list --global for the scheduled backups.
Route 2, rebuild from the exports. Use this to move to a newer Dokku or start clean. Add your SSH key, create the app and the service, load the dump and link them:
cat ~/.ssh/authorized_keys | sudo dokku ssh-keys:add admindokku apps:create shopdokku postgres:create lollipopdokku postgres:import lollipop < lollipop.dumpdokku postgres:link lollipop shop --no-restartCreate the service at the old server's Postgres version or newer: dokku postgres:info lollipop --version shows it, and postgres:create takes --image-version. The new service has a new password and the link sets a new DATABASE_URL, so leave the old one out of the config you import:
grep -v '^DATABASE_URL=' shop.env > shop-nodb.envdokku config:import --no-restart shop shop-nodb.envconfig:import prints each variable it sets, values included, so run it where the output is not logged. Then restore the domain and the storage mount, taking just that app's files from the archive:
dokku domains:set shop shop.example.comsudo tar -xzf dokku-files.tar.gz -C / --numeric-owner var/lib/dokku/data/storage/shopdokku storage:mount shop /var/lib/dokku/data/storage/shop:/app/storage--numeric-owner keeps the owners' numeric IDs, such as the 32767 Dokku uses for Herokuish apps. From your machine, point the remote at the new server and push to the branch Dokku deploys, master unless you set deploy-branch:
git remote set-url dokku [email protected]:shopgit push dokku main:masterOnce DNS points at the new server, install the Let's Encrypt plugin, set an address with dokku letsencrypt:set --global email [email protected], run dokku letsencrypt:enable shop and schedule renewals with dokku letsencrypt:cron-job --add. For a certificate you added yourself, use dokku certs:add shop server.crt server.key. Moving providers too? See migrating to a new provider.
Restore a backup from S3
Download the object with any S3 client or your provider's console. Decrypt it if you set a passphrase, extract it, and import the dump inside:
gpg --output backup.tgz --decrypt postgres-lollipop-2026-10-04-03-00-01.tgz.gpgtar -xzf backup.tgzdokku postgres:import lollipop < backup/exportgpg asks for the passphrase; in a script, add --batch --pinentry-mode loopback --passphrase-file <file>. Do not pipe the .tgz itself into postgres:import: pg_restore needs the backup/export file inside it.
Test the restore
Load the newest dump into a scratch service beside the real one, count rows in a table you know, and remove it:
dokku postgres:create restoretestdokku postgres:import restoretest < /var/backups/dokku/2026-10-04_0230/lollipop.dumpecho 'SELECT count(*) FROM users;' | dokku postgres:connect restoretestdokku postgres:destroy restoretest --forceWithout a terminal, postgres:connect reads SQL from standard input. Time the import: that is the database part of your recovery time. See testing a restore.
Common errors
| Error | Fix |
|---|---|
Missing AWS_ACCESS_KEY_ID file | Run postgres:backup-auth for the service, or pass --use-iam on EC2 with an instance role. |
Service container is not running | Start it with dokku postgres:start lollipop; backups need a running service. |
input file does not appear to be a valid tar archive | The .tgz from S3 went straight into postgres:import. Extract it and import backup/export. |
input file appears to be a text format dump. Please use psql. | The file is plain SQL. Load it with dokku postgres:connect lollipop < dump.sql. |
| pg_restore reports an unsupported version in the file header | The dump came from a newer Postgres than the service. Create the service at that version or newer. |
password authentication failed for user "postgres" after a rebuild | The imported config kept the old DATABASE_URL. Run dokku postgres:unlink lollipop shop, then dokku config:unset shop DATABASE_URL, then link again. |
WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! on git push | The host name now points at the new server. Remove the old key with ssh-keygen -R <host> and push again. |
Permission denied (publickey) on git push | Your key is not on the new server. Add it with dokku ssh-keys:add. |
Frequently asked questions
- Where does Dokku store app data?
- Config and plugin settings in /var/lib/dokku/config, app repositories and certificates under /home/dokku, files in storage mounts wherever you mounted them (by convention one folder per app under /var/lib/dokku/data/storage), and datastore services in /var/lib/dokku/services.
- How do I back up a Dokku Postgres database?
- Run dokku postgres:export with the service name and redirect it to a file for a local dump, or set up postgres:backup-auth and postgres:backup-schedule to upload backups to S3 on a schedule, encrypted if you set a passphrase.
- Does the Dokku Postgres plugin delete old S3 backups?
- No. Each backup gets a timestamped name and stays until you delete it, so add a lifecycle rule to the bucket.
- How do I move Dokku apps to a new server?
- Install the same Dokku version, then either extract an archive of Dokku's directories and rebuild the apps, or recreate each app from its config export and database dump and git push the code.
- Can I restore a Dokku backup on a server with a different CPU architecture?
- Yes. Remove /home/dokku/.basher straight after extracting, as Dokku's backup guide says, because it does not carry across architectures, then rebuild the apps.
How this was checked
Commands, limits and prices were checked against these official pages, on October 4, 2026:
- Dokku documentation: Backup and Recovery
- Dokku documentation: Persistent Storage
- Dokku documentation: Environment Variables
- Dokku documentation: SSL Configuration
- Dokku documentation: Domain Configuration
- Dokku documentation: Deploying an Application
- Dokku documentation: Git Deployment (deploy branch)
- Dokku documentation: Docker Image Deployment
- Dokku documentation: Application Management (apps:list)
- Dokku documentation: Process Management (ps:rebuild)
- Dokku documentation: Scheduled Cron Tasks
- Dokku documentation: Plugin Management
- Dokku documentation: Installation (v0.38.31 bootstrap, ssh-keys:add)
- Dokku v0.38.31 source: config plugin commands (config:import)
- Dokku v0.38.31 changelog (config:import added in 0.37.0)
- dokku-postgres 2.2.0 README
- dokku-postgres 2.2.0: Postgres 18 definition (export and import commands)
- dokku-datastore 0.4.0 source: backup (object name, temporary file, errors)
- dokku-datastore 0.4.0 source: link (alias handling)
- docker-s3backup 0.19.2 (the image dokku-datastore 0.4.0 runs): backup.sh
- dokku-letsencrypt 0.25.2 README
- dokku-mysql README (export, import and backup commands)
- dokku-redis README (export, import and backup commands)
- PostgreSQL 18 source: pg_backup_archiver.c (archive format errors, PGDMP header)
- OpenSSH source: sshconnect.c (changed host key warning)
- GnuPG manual: Operational GPG Commands
- GNU tar man page (exit status)