Backup File Management#
Dokploy stores database backups directly to S3-compatible storage via rclone, with no intermediate local file. Each backup driver (PostgreSQL, MySQL, MariaDB, MongoDB) constructs an S3 object key, streams the dump through rclone rcat, then triggers optional retention cleanup. The core utilities live in packages/server/src/utils/backups/.
File Naming#
All backup filenames are built from a single shared helper, getBackupTimestamp:
<ISO-timestamp-with-hyphens>.<ext>
getBackupTimestamp calls new Date().toISOString() and replaces every : and . with -, producing names like 2026-07-27T14-30-00-000Z. The extension differs by database type:
| Database | Extension |
|---|---|
| PostgreSQL | .sql.gz |
| MySQL | .sql.gz |
| MariaDB | .sql.gz |
| MongoDB | .bson.gz |
| Web-server | .zip |
S3 Path Layout#
The full S3 object key is assembled in each backup driver as:
<bucket>/<appName>/<normalized-prefix>/<filename>
appName— the service's auto-generated app name from the database record (e.g.postgres-xyz123). For Compose services it is{compose.appName}_{serviceName}.normalized-prefix— the user-configuredbackup.prefix, processed bynormalizeS3Path: leading/trailing slashes are stripped and a single trailing slash is appended; an empty prefix produces an empty string, so no extra segment is inserted.filename— the timestamp-based name described above.
Example for a PostgreSQL service with appName=postgres-abc and prefix=daily:
mybucket/postgres-abc/daily/2026-07-27T14-30-00-000Z.sql.gz
⚠️ Path history note: PR #3919 (merged March 2026) introduced the
appName/segment to namespace backups per service. PR #4405 (open as of May 2026) proposes reverting to a pure{prefix}/{filename}layout, makingprefixauthoritative and removing theappNamesegment. Verify which scheme is live in your deployed version before relying on path patterns for external tooling or scripts. Upgrading between these schemes orphans old S3 objects from retention management — manual cleanup is required.
Retention / Cleanup#
Retention is enforced by keepLatestNBackups, called after each successful backup run. It is a no-op when keepLatestCount is 0 or unset .
The retention flow:
- List —
rclone lsfon:s3:<bucket>/<appName>/<normalizedPrefix>, filtered by extension glob (*.sql.gz/*.bson.gzfor databases;*.zipfor web-server) to avoid touching unrelated objects . - Sort & truncate — output is piped through
sort -r | tail -n +$((keepLatestCount+1))to isolate the oldest files beyond the desired count . - Delete — each surplus file is passed to
rclone deletetargeting the same path prefix .
The list and delete paths must match the upload path exactly; a mismatch causes retention to silently skip old backups and accumulate files in S3 indefinitely (this was the root cause described in PR #4405).
Key Source Files#
| File | Purpose |
|---|---|
utils/backups/utils.ts | getBackupTimestamp, normalizeS3Path, getS3Credentials, getBackupCommand |
utils/backups/index.ts | keepLatestNBackups, getServiceAppName, cron initialization |
utils/backups/postgres.ts | PostgreSQL backup runner |
utils/backups/mysql.ts | MySQL backup runner |
utils/backups/mariadb.ts | MariaDB backup runner |
utils/backups/mongo.ts | MongoDB backup runner |