Summary
S3 destination credentials are logged in plaintext to Dokploy's structured logger output (service stdout) every time a backup job runs. Any operator who can read Dokploy service logs — including log aggregators, syslog forwarders, or anyone with docker service logs dokploy access — can recover S3 access keys and secret access keys.
Version
Dokploy v0.29.4 through at least v0.29.8 (current latest). Not addressed by PR #4566 (restore logging refactor) or PR #4579 (registry password fix).
Environment
Any Dokploy instance with at least one configured S3 backup destination and an enabled backup schedule.
Root cause
In packages/server/src/utils/backups/utils.ts, getS3Credentials() returns rclone flags with credentials embedded as inline flag values:
`--s3-access-key-id="${accessKey}"`,
`--s3-secret-access-key="${secretAccessKey}"`,
getBackupCommand() then logs the fully assembled rcloneCommand string (which includes those flags verbatim) via logger.info:
logger.info(
{ containerSearch, backupCommand, rcloneCommand, logPath },
`Executing backup command: ${backup.databaseType} ${backup.backupType}`,
);
This structured log line — containing the full rclone rcat --s3-access-key-id="..." --s3-secret-access-key="..." ... string — is emitted to Dokploy's stdout on every backup run, not just on errors.
Additionally, keepLatestNBackups() in packages/server/src/utils/backups/index.ts constructs equivalent rclone commands (rcloneList, rcloneDelete) with the same credential flags inline, and surfaces them via console.error(error) on failure — also reaching stdout.
What is leaked
- S3 / object-storage access key ID
- S3 / object-storage secret access key
- Bucket name, region, endpoint
One log line per backup run (database type × configured schedule frequency).
What is NOT leaked
No credentials appear in the UI, in Dokploy deployment log files written to disk, or in notification webhooks — only in the service process stdout captured by the container runtime.
Suggested fix
-
Do not log the rclone command string. Remove rcloneCommand from the logger.info payload in getBackupCommand(), or log only a redacted summary (e.g. rclone rcat :s3:<bucket>/...).
-
Pass credentials via environment variables instead of inline flags. rclone supports RCLONE_S3_ACCESS_KEY_ID and RCLONE_S3_SECRET_ACCESS_KEY environment variables, which avoids embedding secrets in the command string at all (they never appear in process args or logs).
-
Alternatively, use an rclone config file written to a temp path with 0600 permissions, and pass --config <path> — credentials stay out of the command line entirely.
Approach 2 (env vars) is the lowest-change fix and consistent with how the recently merged PR #4579 handled registry passwords.
Related code
packages/server/src/utils/backups/utils.ts — getS3Credentials(), getBackupCommand()
packages/server/src/utils/backups/index.ts — keepLatestNBackups()
- All per-database backup runners (
postgres.ts, mysql.ts, mariadb.ts, mongo.ts, libsql.ts, compose.ts) build and pass the rclone command constructed from getS3Credentials()
Summary
S3 destination credentials are logged in plaintext to Dokploy's structured logger output (service stdout) every time a backup job runs. Any operator who can read Dokploy service logs — including log aggregators, syslog forwarders, or anyone with
docker service logs dokployaccess — can recover S3 access keys and secret access keys.Version
Dokploy v0.29.4 through at least v0.29.8 (current latest). Not addressed by PR #4566 (restore logging refactor) or PR #4579 (registry password fix).
Environment
Any Dokploy instance with at least one configured S3 backup destination and an enabled backup schedule.
Root cause
In
packages/server/src/utils/backups/utils.ts,getS3Credentials()returns rclone flags with credentials embedded as inline flag values:getBackupCommand()then logs the fully assembledrcloneCommandstring (which includes those flags verbatim) vialogger.info:This structured log line — containing the full
rclone rcat --s3-access-key-id="..." --s3-secret-access-key="..." ...string — is emitted to Dokploy's stdout on every backup run, not just on errors.Additionally,
keepLatestNBackups()inpackages/server/src/utils/backups/index.tsconstructs equivalent rclone commands (rcloneList,rcloneDelete) with the same credential flags inline, and surfaces them viaconsole.error(error)on failure — also reaching stdout.What is leaked
One log line per backup run (database type × configured schedule frequency).
What is NOT leaked
No credentials appear in the UI, in Dokploy deployment log files written to disk, or in notification webhooks — only in the service process stdout captured by the container runtime.
Suggested fix
Do not log the rclone command string. Remove
rcloneCommandfrom thelogger.infopayload ingetBackupCommand(), or log only a redacted summary (e.g.rclone rcat :s3:<bucket>/...).Pass credentials via environment variables instead of inline flags. rclone supports
RCLONE_S3_ACCESS_KEY_IDandRCLONE_S3_SECRET_ACCESS_KEYenvironment variables, which avoids embedding secrets in the command string at all (they never appear in process args or logs).Alternatively, use an rclone config file written to a temp path with
0600permissions, and pass--config <path>— credentials stay out of the command line entirely.Approach 2 (env vars) is the lowest-change fix and consistent with how the recently merged PR #4579 handled registry passwords.
Related code
packages/server/src/utils/backups/utils.ts—getS3Credentials(),getBackupCommand()packages/server/src/utils/backups/index.ts—keepLatestNBackups()postgres.ts,mysql.ts,mariadb.ts,mongo.ts,libsql.ts,compose.ts) build and pass the rclone command constructed fromgetS3Credentials()