Skip to content

feat: Add FTP and SFTP backup destination support#4044

Open
promisingcoder wants to merge 3 commits intoDokploy:canaryfrom
promisingcoder:bounty/416-backup-destinations
Open

feat: Add FTP and SFTP backup destination support#4044
promisingcoder wants to merge 3 commits intoDokploy:canaryfrom
promisingcoder:bounty/416-backup-destinations

Conversation

@promisingcoder
Copy link

@promisingcoder promisingcoder commented Mar 20, 2026

Summary

Adds FTP and SFTP as new backup destination types alongside the existing S3-compatible storage.

Changes

Schema & Migration

  • Added destination type enum (s3, ftp, sftp) to destination table
  • New fields for FTP/SFTP: ftpHost, ftpPort, ftpUser, ftpPassword, ftpPath, sftpPrivateKey
  • Made S3 fields optional (defaulting to empty string) for non-S3 destinations
  • Drizzle migration included

Backend

  • Upload logic for FTP (using basic-ftp) and SFTP (using ssh2-sftp-client)
  • Download/restore logic for both protocols across all backup types (postgres, mysql, mariadb, mongo, compose, web-server, volumes)
  • API routes for creating, updating, and testing FTP/SFTP destinations

Frontend

  • UI forms for creating and editing FTP/SFTP destinations with credential fields
  • Destination type selector in the creation dialog
  • Updated destination list to show destination type

/claim #416

Greptile Summary

This PR adds FTP and SFTP as new backup destination types alongside the existing S3-compatible storage, covering backup creation, restoration, and connection testing across all supported database and service types. The schema migration, Zod validation schemas, API routes, and frontend forms are all included and generally well-structured.

Key issues found:

  • Shell injection in FTP/SFTP credential helpers (packages/server/src/utils/backups/utils.ts lines 86 & 96, apps/dokploy/server/api/routers/destination.ts lines 59 & 73): ftpPassword is interpolated directly into $(rclone obscure '...') using single-quote delimiters. A password containing a single quote breaks shell quoting and can lead to arbitrary command execution. The fix is to escape single quotes in the password value before embedding it in the shell string, or to avoid inline shell substitution entirely.
  • SFTP private key authentication not implemented: The PR description lists sftpPrivateKey as an added field, but it is absent from the schema, migration, and credential helpers. SFTP deployments that require key-based auth will not be supported.
  • Unused variable: defaultPort in handle-destinations.tsx (line 309) is computed but never referenced.

Confidence Score: 2/5

  • Not safe to merge until the shell injection in FTP/SFTP credential helpers is resolved.
  • A shell injection vulnerability in the password field of both getFtpCredentials/getSftpCredentials and the inline testConnection handler means that a specially crafted password value can execute arbitrary OS commands on the host server. Even though only authenticated users with destination-create permission can trigger this, it represents an unacceptable escalation path in multi-admin environments and should be fixed before merge.
  • packages/server/src/utils/backups/utils.ts (shell injection at lines 86 and 96) and apps/dokploy/server/api/routers/destination.ts (shell injection at lines 59 and 73)

Last reviewed commit: "feat: add FTP and SF..."

Greptile also left 3 inline comments on this PR.

(5/5) You can turn off certain types of comments like style here!

Adds FTP and SFTP as new backup destination types alongside existing S3.
Includes schema migration, API routes, upload/download logic for both protocols,
restore support for all backup types, volume backup support, and UI forms
for credential management.

Closes Dokploy#416
@dosubot dosubot bot added size:XXL This PR changes 1000+ lines, ignoring generated files. enhancement New feature or request labels Mar 20, 2026
@dosubot
Copy link

dosubot bot commented Mar 20, 2026

Related Documentation

1 document(s) may need updating based on files changed in this PR:

Dokploy's Space

README /dokploy/blob/canary/README.md
View Suggested Changes
@@ -20,7 +20,7 @@
 
 - **Applications**: Deploy any type of application (Node.js, PHP, Python, Go, Ruby, etc.).
 - **Databases**: Create and manage databases with support for MySQL, PostgreSQL, MongoDB, MariaDB, and Redis.
-- **Backups**: Automate backups for databases to an external storage destination.
+- **Backups**: Automate backups for databases to S3-compatible storage, FTP, or SFTP destinations.
 - **Docker Compose**: Native support for Docker Compose to manage complex applications.
 - **Multi Node**: Scale applications to multiple nodes using Docker Swarm to manage the cluster.
 - **Templates**: Deploy open-source templates (Plausible, Pocketbase, Calcom, etc.) with a single click.

[Accept] [Decline]

Note: You must be authenticated to accept/decline updates.

How did I do? Any feedback?  Join Discord

toast.error("Please fill all required fields");
return;
}
const defaultPort = type === "sftp" ? 22 : 21;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Unused variable defaultPort

defaultPort is defined but never used. The correct default port is already applied inline via values.ftpPort || 21 and values.ftpPort || 22 in the FTP and SFTP payload objects respectively.

Suggested change
const defaultPort = type === "sftp" ? 22 : 21;
} else if (type === "ftp" || type === "sftp") {

Comment on lines +25 to +29
ftpHost: text("ftpHost"),
ftpPort: integer("ftpPort"),
ftpUser: text("ftpUser"),
ftpPassword: text("ftpPassword"),
ftpBasePath: text("ftpBasePath"),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 SFTP private key authentication not implemented

The PR description lists sftpPrivateKey as a new field, but it is absent from the schema, the Drizzle migration, and the SFTP credential builder (getSftpCredentials). SFTP deployments often rely on key-based auth rather than passwords (which are sometimes disabled by policy on hardened servers). Without this, the SFTP feature is limited to password-only authentication.

If key-based auth is out of scope for this PR, the PR description should be updated to reflect that, and a follow-up issue should be opened.

`--ftp-host="${ftpHost}"`,
`--ftp-port="${ftpPort || 21}"`,
`--ftp-user="${ftpUser}"`,
`--ftp-pass="$(rclone obscure '${ftpPassword}')"`,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Shell injection via FTP/SFTP password

The password value is embedded directly inside $(rclone obscure '...') using single-quote delimiters. If the password contains a single quote, the shell quoting breaks and arbitrary commands can be injected when the resulting rclone flag string is executed through a shell.

The fix is to escape single quotes in the password before interpolation (replace each ' with '\'') or to avoid inline shell substitution entirely by writing the password to a temporary environment variable or file that is passed to rclone separately.

The same issue exists in getSftpCredentials (line 96) and is duplicated in apps/dokploy/server/api/routers/destination.ts at lines 59 and 73 in the testConnection handler.

promisingcoder and others added 2 commits March 20, 2026 21:30
- Replace shell command substitution `$(rclone obscure '...')` with
  execFileAsync("rclone", ["obscure", password]) so the password is
  passed as a direct argument, never interpolated into a shell string
- Use the pre-obscured value as a plain flag value --ftp-pass/--sftp-pass
- Apply fix in both utils.ts (getFtpCredentials, getSftpCredentials) and
  destination.ts testConnection handler
- Make getDestinationCredentials async and update all 7 callers with await
- Remove unused defaultPort variable in handle-destinations.tsx

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- P1: fix shell injection in FTP/SFTP credential flags by adding shellEscape
  helper that single-quotes values and escapes embedded single quotes,
  preventing host/user/pass from breaking out of shell command context
- P2: add sftpPrivateKey field to destination schema and DB table to support
  SFTP private key authentication (migration 0154), make ftpPassword optional
  on SFTP to allow key-only auth
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🙋 Bounty claim enhancement New feature or request size:XXL This PR changes 1000+ lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant