Deploy static files over SFTP, FTPS, or FTP from a small, scriptable Node.js CLI.
Deploydo 3 is a security-focused major release: it uses modern transfer clients, validates configuration before connecting, supports CI-friendly credentials, and keeps a SHA-256 manifest to upload only changed files.
- Node.js 20 or newer
- An SFTP, FTPS, or FTP server
npm install --global deploydoCreate a configuration template in the current project:
deploydo initEdit deploydo.config.json, then validate it:
deploydo validateSet the configured password variable and inspect the transfer without changing the server:
DEPLOYDO_PASSWORD='your-secret' deploydo deploy --dry-runRun the deployment interactively, or use --yes --non-interactive in CI:
DEPLOYDO_PASSWORD='your-secret' deploydo deploy --profile default --yes --non-interactiveTo enter a password once and reuse it on this computer, add --save-password
to an interactive deployment. Deploydo saves it only after a successful
connection:
deploydo deploy --profile production --save-passworddeploydo --sample remains an alias for deploydo init.
V3 prefers deploydo.config.json. The sourcePath is relative to the
configuration file; remote paths always use POSIX separators.
{
"profiles": {
"production": {
"protocol": "sftp",
"host": "example.com",
"port": 22,
"username": "deploy",
"sourcePath": "dist",
"remotePath": "/var/www/html",
"exclude": ["**/*.map"],
"build": { "command": "npm run build" },
"auth": {
"passwordEnv": "DEPLOYDO_PRODUCTION_PASSWORD"
},
"sync": { "useManifest": true },
"connection": { "retries": 2 }
}
}
}Profiles support these fields:
| Field | Meaning |
|---|---|
protocol |
sftp, ftps, or ftp |
host, port, username |
Server connection details; ports default to 22 for SFTP and 21 for FTP/explicit FTPS |
sourcePath, remotePath |
Local source directory and remote target directory |
exclude |
A glob or a list of globs to omit from the upload |
verbose |
Print individual file operations; --verbose overrides it for one run |
build.command |
Optional shell command run before a real deployment |
auth.passwordEnv |
Environment variable containing the password |
auth.privateKeyPath |
SFTP private key path, relative to the configuration file |
auth.privateKeyPassphraseEnv |
Environment variable for the private-key passphrase |
auth.hostFingerprint |
Optional SHA-256 host-key pin for SFTP |
sync.useManifest |
Enables the changed-file manifest; enabled by default |
sync.manifestPath |
Relative remote manifest name; defaults to deploydo.manifest.json |
connection.retries |
Retry count for transient connection errors; defaults to 2 |
For FTPS, add an optional ftps block:
{
"protocol": "ftps",
"ftps": { "mode": "explicit", "rejectUnauthorized": true }
}mode may be explicit or implicit; implicit FTPS defaults to port 990.
Plain FTP remains available for old servers, but Deploydo warns because it does
not encrypt credentials or transferred files.
Never put passwords in deploydo.config.json.
- Use
auth.passwordEnvfor password authentication. - Use
auth.privateKeyPathandauth.privateKeyPassphraseEnvfor SFTP keys. - Use
--password-stdinfor a one-time pipeline secret. It requires--yesfor a real deployment. deploy --save-passwordstores the interactively entered password in the operating-system credential store after a successful connection. The next interactive deployment uses it automatically when no password environment variable is set. Environment variables and--password-stdintake priority.- Remove a stored password with
deploydo credentials delete --profile <name>. - Deploydo only prompts on an interactive TTY.
--non-interactivefails if a secret is missing.
On Windows this is Windows Credential Manager, on macOS the Keychain, and on
Linux the Secret Service. No password is written to deploydo.config.json, the
project directory, or a Deploydo-specific password file.
Example CI command:
deploydo deploy --profile production --yes --non-interactive --jsonIn JSON mode, stdout contains one final result object. Warnings and errors are written to stderr.
Deploydo scans regular files, including dotfiles such as .htaccess, and
calculates SHA-256 hashes. A successful deploy writes a remote
deploydo.manifest.json; unchanged files are skipped on later deploys. The
default deliberately avoids a leading dot because some hardened FTP servers
prohibit dot-file writes.
--dry-run reads the remote manifest but never uploads, deletes, or updates it.
It also skips the configured build command by default.
--delete is deliberately conservative: it removes only paths from a previous
Deploydo manifest that are no longer in the local source. It never mirrors or
deletes arbitrary remote files.
deploydo deploy [--profile <name>|--env <name>] [--config <file>]
[--dry-run] [--delete] [--yes] [--no-build]
[--non-interactive] [--password-stdin] [--save-password] [--json]
deploydo validate [--profile <name>|--env <name>] [--config <file>] [--json]
deploydo credentials delete [--profile <name>|--env <name>] [--config <file>]
deploydo init [--output <file>] [--force]
deploydo migrate [--config <legacy-file>] [--output <file>] [--force]
deploy.conf.json is still read as a legacy configuration and emits a warning.
The legacy --env and --deployConfigFile flags remain available.
Create a V3 file without copying any passwords:
deploydo migrate --config deploy.conf.jsonV2 source paths beginning with / retain their old working-directory-relative
meaning while the legacy file is used. The generated V3 configuration converts
such paths to the explicit relative form, for example /dist becomes dist.
Existing deploy.pw.conf.json files are ignored unless
--allow-legacy-password-file is explicitly passed. This switch is intended as
a short migration bridge only.
npm test
npm run lint
npm run check
npm run pack:checkRun the protocol integrations locally with Docker:
npm run integration:up
DEPLOYDO_INTEGRATION=1 npm run test:integration
docker compose -f docker-compose.integration.yml down -vThe integration stack provides isolated SFTP, FTP, and explicit-FTPS servers.
FTPS uses a self-signed certificate, so its test profile deliberately sets
rejectUnauthorized to false; production profiles should keep the default
of true.
Equivalent :rtk scripts are available in environments that use Rust Token
Killer. The test suite covers configuration validation and migration, secrets,
dotfiles, SHA-256 manifests, dry-runs, safe deletion, and failure exit paths.