-
Notifications
You must be signed in to change notification settings - Fork 0
Home
SFTPresso — SFTP/FTP sync for Visual Studio Code. Actively maintained fork of
vscode-sftp.
- Publisher:
jmwerk· Current version: 1.19.0 · License: MIT- Repository: https://github.com/jmwerk/vscode-sftp
- Requires: VS Code
^1.64.2- Lineage: forked from Natizyskunk/vscode-sftp, which continued liximomo's original SFTP plugin after it went unmaintained.
- Overview
- Installation and Setup
- Command Reference
-
Configuration Reference (
sftp.json) -
Usage Examples and Common Workflows
- Simple single-server setup
- Start from a remote project
- Upload on save
- Profiles (dev / prod)
- Multiple contexts (array config)
- Connection hopping (SSH proxy / bastion)
- Two-way automatic sync with the watcher
- Uploading a folder's contents without the folder itself
- Configuration in User Settings (remote-fs)
- Using the Remote Explorer
- Monitoring and cancelling transfers
- Comparing folders with the remote
- Best Practices
- Troubleshooting and Known Issues
- Frequently Asked Questions
- Development and Contributing
- Credits
SFTPresso lets you add, edit, or delete files in a local directory and have those changes sync to a remote server directory over SFTP (SSH) or FTP/FTPS. The most basic setup requires only a few lines of configuration, while a wide array of options covers advanced needs — multiple servers, switchable profiles, SSH connection hopping, file watchers, and atomic uploads.
- Remote deployment while editing locally — keep a familiar local editor and environment and mirror your work to a web server, staging box, or embedded device on every save.
- Remote file management — browse, view, edit, create, and delete files on the server directly from the SFTP sidebar without leaving VS Code.
- Directory synchronization — one-shot or continuous sync in either (or both) directions, with fine-grained control over deletes, creates, and overwrites.
-
Multi-environment workflows — one config with named profiles (e.g.
dev,prod) or several configs mapped to different subfolders of the same workspace.
| Feature | Where to find it | Details |
|---|---|---|
| Remote Explorer | SFTP icon in the Activity Bar | Browse remote files, multi-select download/upload — see Using the Remote Explorer |
| Transfers view | SFTP sidebar → Transfers | Live per-file status (queued / transferring / failed) with per-file cancel — see Monitoring and cancelling transfers |
| Status-bar progress | Status bar during bulk transfers | "Transferring X/Y files" counter; click to cancel all |
| Diff local ↔ remote | SFTP: Diff with Remote |
Opens VS Code's diff view against the remote copy |
| Compare Folders | SFTP: Compare Folders with Remote |
Recursive local/remote diff with per-file actions — see Comparing folders |
| Test Connection |
SFTP: Test Connection / CodeLens on sftp.json
|
Verifies the active profile can connect |
| Guided config setup |
SFTP: Config → Quick setup
|
Step-by-step wizard that generates sftp.json and tests the connection — see First-time setup
|
| Secure password storage |
SFTP: Save Password / SFTP: Clear Password
|
Keep passwords in VS Code's secret storage (OS keychain) instead of plaintext sftp.json — see Storing passwords securely
|
| Upload on save | uploadOnSave |
Mirrors every VS Code save to the server |
| File watcher | watcher |
Reacts to changes made outside VS Code (build tools, git checkout, …) |
| Multiple configurations | Array config | Different servers per workspace subfolder |
| Switchable profiles |
profiles + SFTP: Set Profile
|
One config, many targets — the status bar shows the active profile; click it to switch |
| Temp-file / atomic uploads |
useTempFile, openSsh
|
Avoid serving half-written files |
| Connection hopping | hop |
Reach a target server through one or more SSH bastions |
| Upload to all profiles | SFTP: Upload … To All Profiles |
Push one file/folder/project to every profile at once |
SFTPresso is not yet published to the VS Code Marketplace under its new name. Until it is, install from a VSIX package:
- Grab a
.vsixfrom GitHub Releases — or build one from source (see Development and Contributing). - In VS Code, open the Extensions view (
Ctrl+Shift+X/Cmd+Shift+X). - If you still have an older
sftpextension installed (from@liximomoor@Natizyskunk), uninstall it first to avoid command conflicts. - Open the ⋯ (More Actions) menu at the top of the Extensions view and choose Install from VSIX….
- Locate the
.vsixfile and select it. - Reload VS Code.
To build the VSIX yourself:
git clone https://github.com/jmwerk/vscode-sftp.git
cd vscode-sftp
npm install # also applies bundled patches via patch-package
npm run package # produces sftpresso-<version>.vsix via vsce- Open the local folder you want to sync (
File → Open Folder…). - Open the Command Palette (
Ctrl+Shift+P/Cmd+Shift+P) and runSFTP: Config. - When no
sftp.jsonexists yet, pick how to create it:-
Quick setup — a guided wizard that asks for the protocol (sftp/ftp), host, port (pre-filled 22 or 21 per protocol), username, authentication method, remote path, and whether to upload on save. For SFTP the auth choices are a password prompt at connect time (with an offer to remember it in secret storage), a private key file (
~is expanded and the file is checked to exist), or a running ssh-agent. Answers are validated against the config schema,sftp.jsonis written, andSFTP: Test Connectionruns immediately to confirm the connection works. -
Edit JSON — the extension creates
.vscode/sftp.jsonwith a starter template. Edit it with your server details:
-
Quick setup — a guided wizard that asks for the protocol (sftp/ftp), host, port (pre-filled 22 or 21 per protocol), username, authentication method, remote path, and whether to upload on save. For SFTP the auth choices are a password prompt at connect time (with an offer to remember it in secret storage), a private key file (
{
"name": "My Server",
"host": "server.example.com",
"protocol": "sftp",
"port": 22,
"username": "user1",
"remotePath": "/var/www/project",
"uploadOnSave": false
}- Save and close
sftp.json. The extension activates automatically whenever a workspace contains.vscode/sftp.json. - Type
SFTPin the Command Palette to see all available commands (see the Command Reference). Many are also available from the file explorer and editor context menus.
Notes:
-
passwordis optional — if omitted you'll be prompted when a connection is made, and offered to have the password remembered in VS Code's secret storage (see Storing passwords securely). If you do store it insftp.json, be aware it is plain text (see Best Practices). - Backslashes and other special characters in JSON values must be escaped with a backslash.
-
sftp.jsongets schema-validated in the editor, so you get completions and warnings for unknown or mistyped option names.
Run SFTP: Test Connection from the Command Palette, or click the Test Connection CodeLens shown at the top of sftp.json. It connects using the active profile's settings and reports success or failure with an actionable message (see SSH connection error messages).
Instead of writing password into sftp.json (which is plain text), you can keep it in VS Code's secret storage, which is backed by the operating system keychain:
- Run
SFTP: Save Password, pick the remote, and enter the password. Future connections use it automatically — no prompt, nothing insftp.json. - Or just connect: when you're prompted for a password and the connection succeeds, the extension offers to remember it.
- Run
SFTP: Clear Passwordto delete a saved password (for example after it changed on the server).
Saved passwords are keyed by protocol://username@host:port, so each server/user pair is stored independently (configs and profiles that point at the same server share one saved password). A saved password is only used when the config provides no other authentication — configs that set password, privateKeyPath, agent, or interactiveAuth behave exactly as before. If sftp.json still contains a plaintext password, the extension logs a one-time reminder in the output channel.
All commands live under the SFTP category in the Command Palette. Most are also exposed via context menus (file explorer, editor, editor title, SCM view, Remote Explorer). Commands are only available when the extension is active (workspace contains .vscode/sftp.json).
| Command | ID | Description |
|---|---|---|
SFTP: Config |
sftp.config |
Create a new sftp.json for the workspace — via a guided quick-setup wizard or a starter template — or open the existing one. See First-time setup. |
SFTP: Set Profile |
sftp.setProfile |
Switch the active profile. |
SFTP: Test Connection |
sftp.testConnection |
Connect to the active profile's remote and report success/failure. Also available as a CodeLens on sftp.json. |
SFTP: Open SSH in Terminal |
sftp.openConnectInTerminal |
Open a VS Code terminal auto-logged-in to the server. Extra CLI flags can be added via sshCustomParams. |
SFTP: Save Password |
sftp.savePassword |
Store a password for a remote in VS Code's secret storage (OS keychain). See Storing passwords securely. |
SFTP: Clear Password |
sftp.clearPassword |
Remove a saved password from secret storage. |
| Command | ID | Description |
|---|---|---|
SFTP: Upload Active File |
sftp.upload.activeFile |
Upload the file currently open in the editor. |
SFTP: Upload Active Folder |
sftp.upload.activeFolder |
Upload the folder containing the current file. |
SFTP: Upload Project |
sftp.upload.project |
Upload the whole project (respecting ignore rules). |
SFTP: Upload Changed Files |
sftp.upload.changedFiles |
Upload all files changed or created since the last Git commit (including deletions). Default keybinding Ctrl+Alt+U; also shown in the Source Control view. |
| Upload File / Upload Folder |
sftp.upload.file / sftp.upload.folder
|
Context-menu variants that act on the right-clicked explorer item. |
SFTP: Upload … To All Profiles |
sftp.upload.activeFile.to.allProfiles, sftp.upload.activeFolder.to.allProfiles, sftp.upload.project.to.allProfiles, sftp.upload.file.to.allProfiles, sftp.upload.folder.to.allProfiles
|
Same as above but pushes to every profile defined in the config, not just the active one. |
| Command | ID | Description |
|---|---|---|
SFTP: Download Active File |
sftp.download.activeFile |
Download the remote version of the current file, overwriting the local copy. |
SFTP: Download Active Folder |
sftp.download.activeFolder |
Download the folder containing the current file. |
SFTP: Download Project |
sftp.download.project |
Download everything under remotePath into the workspace. |
| Download File / Download Folder |
sftp.download.file / sftp.download.folder
|
Context-menu variants (explorer and Remote Explorer). |
Sync compares timestamps and transfers only what differs; behavior is tuned with syncOption.
| Command | ID | Description |
|---|---|---|
SFTP: Sync Local -> Remote |
sftp.sync.localToRemote |
Copies files that differ by timestamp, plus files that exist only locally. |
SFTP: Sync Remote -> Local |
sftp.sync.remoteToLocal |
Same, in the opposite direction. |
SFTP: Sync Both Directions |
sftp.sync.bothDirections |
Compares modification times and always keeps the newest version on both sides. Only syncOption.skipCreate and syncOption.ignoreExisting apply to this command. |
If local and remote clocks disagree (server in another timezone, clock drift), set
remoteTimeOffsetInHoursso timestamp comparison stays accurate.
| Command | ID | Description |
|---|---|---|
SFTP: Diff Active File with Remote |
sftp.diff.activeFile |
Open VS Code's diff view: current file vs. its remote counterpart. |
| Diff with Remote | sftp.diff |
Context-menu variant for any explorer file. |
SFTP: Compare Folders with Remote |
sftp.compareFolders |
Recursively diffs a local folder against its remote counterpart; results (new-local / new-remote / modified) are listed in a QuickPick with per-file actions to open a diff, upload, or download. |
| Command | ID | Description |
|---|---|---|
SFTP: List / SFTP: List Active Folder / SFTP: List All
|
sftp.list / sftp.listActiveFolder / sftp.listAll
|
List remote directory contents in a QuickPick. List All includes ignored files. |
| Delete | sftp.delete.remote |
Delete the selected file/folder on the remote (Remote Explorer context menu). |
| Create Folder / Create File |
sftp.create.folder / sftp.create.file
|
Create a remote folder or file from the Remote Explorer. |
| Edit in Local | sftp.remoteExplorer.editInLocal |
Download the remote file into the workspace so it can be edited (Remote Explorer opens files read-only by default). |
| View Content | sftp.viewContent |
Open a read-only view of a remote file. |
| Reveal in Explorer | sftp.revealInExplorer |
Jump from a remote file to its local counterpart in the file explorer. |
| Reveal in Remote Explorer | sftp.revealInRemoteExplorer |
Jump from a local file to its remote counterpart in the Remote Explorer. |
| Refresh | sftp.remoteExplorer.refresh |
Refresh the Remote Explorer tree. |
| Refresh Active Remote File | sftp.remoteExplorer.refreshActiveFile |
Re-fetch the remote file open in the editor. |
| Command | ID | Description |
|---|---|---|
SFTP: Cancel All Transfers |
sftp.cancelAllTransfer |
Stop every in-flight upload/download. Also triggered by clicking the status-bar progress counter, and from the Transfers view title bar. |
| Cancel Transfer | sftp.cancelTransfer |
Cancel a single in-flight file — the inline ✕ button on items in the Transfers view. |
Hold Alt while a context menu is open to reveal the force variants, which disregard ignore rules:
| Command | ID | Description |
|---|---|---|
| Force Upload | sftp.forceUpload |
Upload even files matched by ignore rules. |
| Force Download | sftp.forceDownload |
Download even files matched by ignore rules. |
| Force Upload To All Profiles | sftp.forceUpload.to.allProfiles |
Force-upload to every profile. |
These accept arguments when bound in keybindings.json or invoked from tasks.json:
| Command | Signature |
|---|---|
sftp.setProfile |
func(profileName: string) |
sftp.upload |
func(fspaths: string[]) — upload the given files/folders |
sftp.download |
func(fspaths: string[]) — download the given files/folders |
Example — a keybinding that switches straight to the prod profile:
{
"key": "ctrl+alt+p",
"command": "sftp.setProfile",
"args": "prod"
}| Keys | Command | When |
|---|---|---|
Ctrl+Alt+U |
SFTP: Upload Changed Files |
Extension active |
Configuration lives in <workspace>/.vscode/sftp.json — either a single object, or an array of objects for multiple contexts. Open it any time via SFTP: Config.
The file is read as JSONC: // line comments, /* block comments */, and trailing commas are all allowed, so you can annotate a config or comment out options without breaking it. Parse errors are reported with their line and column.
Apply to both SFTP and FTP unless noted.
A string to identify your configuration. Shown in pickers when multiple configs exist. Required when using an array (multi-context) config.
| Key | Type | Default |
|---|---|---|
name |
string | — |
{ "name": "My Server" }A path relative to the workspace root. Use it to map a subfolder (rather than the whole workspace) to remotePath. Only available at the root level of a config (not inside profiles).
| Key | Type | Default |
|---|---|---|
context |
string | workspace root |
{ "context": "./build" }Transfer protocol.
| Key | Type | Default |
|---|---|---|
protocol |
"sftp" | "ftp"
|
"sftp" |
Hostname or IP address of the server.
| Key | Type |
|---|---|
host |
string |
Server port. Typically 22 for SFTP, 21 for FTP, 990 for implicit FTPS.
| Key | Type |
|---|---|
port |
integer |
Username for authentication.
| Key | Type |
|---|---|
username |
string |
Password for password-based authentication. Optional — omit it to use a password saved via SFTP: Save Password, or to be prompted at connect time (see Storing passwords securely).
⚠️ Warning: passwords insftp.jsonare stored as plain text. PreferprivateKeyPathoragent— or keep the password in secret storage — and keepsftp.jsonout of version control. See Best Practices.
| Key | Type |
|---|---|
password |
string |
The absolute path on the remote host that maps to your local context. This is what SFTP: Download Project downloads.
| Key | Type | Default |
|---|---|---|
remotePath |
string | / |
Octal permissions applied to newly created remote files.
| Key | Type | Default |
|---|---|---|
filePerm |
number | unset |
{ "filePerm": 644 }Octal permissions applied to newly created remote directories.
| Key | Type | Default |
|---|---|---|
dirPerm |
number | unset |
{ "dirPerm": 750 }Upload the file on every VS Code save. See the Upload on save workflow.
| Key | Type | Default |
|---|---|---|
uploadOnSave |
boolean | false |
Upload to a temporary file first, then move it into place — avoids serving a half-written file to visitors while the upload is in progress.
| Key | Type | Default |
|---|---|---|
useTempFile |
boolean | false |
Enable atomic file uploads (rename-into-place). Only supported by OpenSSH servers.
💡 If
openSshistrue,useTempFilemust also betrue.
| Key | Type | Default |
|---|---|---|
openSsh |
boolean | false |
{ "openSsh": true, "useTempFile": true }Download the remote version of a file whenever it is opened locally.
| Key | Type | Default |
|---|---|---|
downloadOnOpen |
boolean | false |
Tunes the Sync commands.
| Key | Type | Default |
|---|---|---|
syncOption |
object | {} |
| Sub-option | Type | Effect |
|---|---|---|
syncOption.delete |
boolean | Delete extraneous files from the destination. |
syncOption.skipCreate |
boolean | Don't create files that are new to the destination. |
syncOption.ignoreExisting |
boolean | Don't update files that already exist on the destination. |
syncOption.update |
boolean | Only overwrite the destination if the source copy is newer. |
{
"syncOption": {
"delete": true,
"skipCreate": false,
"ignoreExisting": false,
"update": true
}
}
Sync Both Directionshonors onlyskipCreateandignoreExisting.
Files/folders excluded from transfers and sync. Gitignore-style patterns (wildcards with *), relative to the config's context. Bypass with the Force commands.
| Key | Type | Default |
|---|---|---|
ignore |
string[] | [] |
{
"ignore": [
"/.vscode",
"/.git",
".DS_Store",
"*.log"
]
}Path to an ignore file (e.g. .gitignore-style list) — absolute, or relative to the workspace root.
| Key | Type |
|---|---|
ignoreFile |
string |
{ "ignoreFile": ".gitignore" }Watches for file changes made outside the VS Code editor (build output, git checkout, external tools) and reacts automatically. Only available at the root level of a config (not inside profiles). See Two-way automatic sync.
| Key | Type | Default |
|---|---|---|
watcher |
object | {} |
| Sub-option | Type | Effect |
|---|---|---|
watcher.files |
string (glob) | Which files to watch (required). |
watcher.autoUpload |
boolean | Upload when a watched file changes. |
watcher.autoDelete |
boolean | Delete on the remote when a watched file is removed locally. |
💡 Set
uploadOnSavetofalsewhen watching everything ("**/*") — otherwise every save triggers both mechanisms.
{
"watcher": {
"files": "dist/*.{js,css}",
"autoUpload": true,
"autoDelete": false
}
}Hours of clock difference between the remote server and your machine (remote minus local). Needed for accurate timestamp-based sync when the server is in another timezone or its clock drifts.
FTP note: servers that support the
MLSDcommand (e.g. pure-ftpd, ProFTPD) report exact UTC timestamps, so this option should normally stay0for them. It's mainly needed for FTP servers limited toLIST(e.g. vsftpd), whose listings carry only server-local, minute-precision dates, and for SFTP servers with a skewed clock.
| Key | Type | Default |
|---|---|---|
remoteTimeOffsetInHours |
number | 0 |
Tunes the Remote Explorer view.
| Key | Type | Default |
|---|---|---|
remoteExplorer |
object | {} |
| Sub-option | Type | Effect |
|---|---|---|
remoteExplorer.filesExclude |
string[] | Patterns for files/folders to hide in the Remote Explorer. |
remoteExplorer.order |
number | Sort position of this config among Remote Explorer roots (default 0). |
{
"remoteExplorer": {
"filesExclude": ["**/node_modules"],
"order": 1
}
}Maximum simultaneous transfers. Lower it if your server limits concurrent connections/operations.
| Key | Type | Default |
|---|---|---|
concurrency |
number | 4 |
Maximum time (ms) to wait when establishing a connection.
| Key | Type | Default |
|---|---|---|
connectTimeout |
number | 10000 |
Cap the number of file descriptors opened on the remote server. Set true for the default limit (222), or a number for a custom limit. Use it if transfers fail with the generic Error: Failure because the server runs out of descriptors.
💡 Do not set this unless you have to.
| Key | Type | Default |
|---|---|---|
limitOpenFilesOnRemote |
boolean | number | false |
A collection of named profiles keyed by profile name. Each profile is merged over the top-level config; switch with SFTP: Set Profile. See Profiles (dev / prod).
| Key | Type |
|---|---|
profiles |
object |
defaultProfile |
string — profile activated by default |
Reference a connection defined in User Settings under remotefs.remote instead of repeating host details per project. See Configuration in User Settings.
| Key | Type |
|---|---|
remote |
string |
Path to the ssh-agent UNIX socket for agent-based authentication (usually $SSH_AUTH_SOCK). Windows users: set to pageant to authenticate with Pageant, or the path to a Cygwin "UNIX socket".
| Key | Type |
|---|---|
agent |
string |
{ "agent": "/run/user/1000/ssh-agent.socket" }Absolute path to your private key file.
| Key | Type |
|---|---|
privateKeyPath |
string |
{ "privateKeyPath": "/Users/me/.ssh/id_rsa" }Passphrase for an encrypted private key. Set the string itself, or set true to be prompted with a dialog (keeps the passphrase out of the config file — recommended).
| Key | Type |
|---|---|
passphrase |
string | boolean |
{ "passphrase": true }Enable keyboard-interactive authentication (e.g. multi-factor / Google Authenticator). Set true for a verification-code dialog, or pass an array of predefined responses to answer prompts automatically.
💡 Requires the server to have keyboard-interactive authentication enabled.
| Key | Type | Default |
|---|---|---|
interactiveAuth |
boolean | string[] | false |
{ "interactiveAuth": true }Explicit overrides for the SSH transport-layer algorithms. Mainly needed for older servers — see Error: Connection closed.
Default:
{
"algorithms": {
"kex": [
"ecdh-sha2-nistp256",
"ecdh-sha2-nistp384",
"ecdh-sha2-nistp521",
"diffie-hellman-group-exchange-sha256"
],
"cipher": [
"aes128-gcm",
"aes128-gcm@openssh.com",
"aes256-gcm",
"aes256-gcm@openssh.com",
"aes128-cbc",
"aes192-cbc",
"aes256-cbc",
"aes128-ctr",
"aes192-ctr",
"aes256-ctr"
],
"serverHostKey": [
"ssh-rsa",
"ssh-dss",
"ssh-ed25519",
"ecdsa-sha2-nistp256",
"ecdsa-sha2-nistp384",
"ecdsa-sha2-nistp521",
"rsa-sha2-512",
"rsa-sha2-256"
],
"hmac": [
"hmac-sha2-256",
"hmac-sha2-512"
]
}
}Path to your OpenSSH client config file; matching Host entries contribute settings to the connection.
| Key | Type | Default |
|---|---|---|
sshConfigPath |
string | ~/.ssh/config |
Extra parameters appended to the ssh command used by SFTP: Open SSH in Terminal.
| Key | Type |
|---|---|
sshCustomParams |
string |
{ "sshCustomParams": "-g" }Connect through one or more intermediate SSH hosts. A single object for one hop, or an array for multiple. See Connection hopping. Each hop accepts the same host/auth options (host, port, username, privateKeyPath, …).
Variable substitution does not work inside a hop configuration.
| Key | Type |
|---|---|
hop |
object | object[] |
Connection encryption mode:
-
true— encrypt both control and data connections (explicit FTPS) -
"control"— encrypt the control connection only -
"implicit"— implicitly encrypted control connection (legacy; usually port 990)
| Key | Type | Default |
|---|---|---|
secure |
boolean | "control" | "implicit"
|
false |
{ "protocol": "ftp", "port": 21, "secure": true }Extra options passed straight to Node's tls.connect() — e.g. to accept a self-signed certificate.
| Key | Type |
|---|---|
secureOptions |
object |
{ "secureOptions": { "rejectUnauthorized": false } }These are regular VS Code settings (File → Preferences → Settings, or settings.json), separate from sftp.json:
| Setting | Type | Default | Description |
|---|---|---|---|
sftp.debug |
boolean | false |
Print debug output to the sftp Output channel. Reload VS Code after changing.
|
sftp.printDebugLog |
boolean | false |
Legacy alias for the same debug output. |
sftp.downloadWhenOpenInRemoteExplorer |
boolean | false |
When opening a file in the Remote Explorer, download it ("Edit in Local") instead of showing a read-only "View Content". |
The minimum viable config — SFTP on port 22, prompted for the password:
{
"host": "host",
"username": "username",
"remotePath": "/remote/workspace"
}If the latest files already live on the server, start with an empty local folder:
- Open the empty folder in VS Code and run
SFTP: Config(see First-time setup). - Set
remotePathto the server directory you want. - Run
SFTP: Download Project— the remote tree is downloaded into your workspace. - From here on, edit locally and upload/sync as needed.
Mirror every save to the server:
{
"host": "server.example.com",
"username": "deploy",
"remotePath": "/var/www/site",
"uploadOnSave": true
}If your site is live, add temp-file/atomic uploads so visitors never see a half-written file:
{
"uploadOnSave": true,
"useTempFile": true,
"openSsh": true
}(See useTempFile and openSsh.)
One config, several environments. Profile values merge over the top-level config; switch with SFTP: Set Profile.
When a config defines profiles, the status bar item shows the active profile (e.g. SFTP: dev, or SFTP: (no profile) when none is active), and clicking it opens the profile picker — same as running SFTP: Set Profile. Without profiles, the status bar keeps its usual behavior (shows SFTP, click to toggle the output panel).
{
"username": "username",
"password": "password",
"remotePath": "/remote/workspace/a",
"watcher": {
"files": "dist/*.{js,css}",
"autoUpload": false,
"autoDelete": false
},
"profiles": {
"dev": {
"host": "dev-host",
"remotePath": "/dev",
"uploadOnSave": true
},
"prod": {
"host": "prod-host",
"remotePath": "/prod"
}
},
"defaultProfile": "dev"
}
contextandwatcherare only available at the root level, not inside a profile.
To deploy to every environment at once, use the … To All Profiles upload commands.
Map different workspace subfolders to different servers (or different remote paths). The context values must differ, and name is required:
[
{
"name": "server1",
"context": "project/build",
"host": "host",
"username": "username",
"password": "password",
"remotePath": "/remote/project/build"
},
{
"name": "server2",
"context": "project/src",
"host": "host",
"username": "username",
"password": "password",
"remotePath": "/remote/project/src"
}
]Reach a target server through an intermediate host with the SSH protocol. Note: variable substitution does not work in a hop configuration.
Single hop (local → hop → target):
{
"name": "target",
"remotePath": "/path/in/target",
"host": "hopHost",
"username": "hopUsername",
"privateKeyPath": "/Users/localUser/.ssh/id_rsa",
"hop": {
"host": "targetHost",
"username": "targetUsername",
"privateKeyPath": "/Users/hopUser/.ssh/id_rsa"
}
}The top-level privateKeyPath is a file on your local machine; the one inside hop is a file on the hop host.
Multiple hops (local → hopA → hopB → target): make hop an array — each entry authenticates from the previous host, and the last entry is the target:
{
"name": "target",
"remotePath": "/path/in/target",
"host": "hopAHost",
"username": "hopAUsername",
"privateKeyPath": "/Users/localUser/.ssh/id_rsa",
"hop": [
{
"host": "hopBHost",
"username": "hopBUsername",
"privateKeyPath": "/Users/hopAUser/.ssh/id_rsa"
},
{
"host": "targetHost",
"username": "targetUsername",
"privateKeyPath": "/Users/hopBUser/.ssh/id_rsa"
}
]
}Keep the server updated with no manual interaction — including changes made outside VS Code, e.g. when Git checks out a branch or reverts commits:
{
"name": "My Server",
"host": "<host_ip_address>",
"protocol": "sftp",
"port": 22,
"username": "user1",
"remotePath": "/folder1/folder2/folder3",
"uploadOnSave": false,
"watcher": {
"files": "**/*",
"autoUpload": true,
"autoDelete": true
},
"syncOption": {
"delete": true
}
}Keep uploadOnSave false here — the watcher already covers saves when watching **/* (see watcher).
Set context to the folder — its contents then map directly onto remotePath. Here everything in ./build lands in /folder1/folder2/folder3 (with only JS/HTML auto-uploaded by the watcher):
{
"name": "My Server",
"host": "<host_ip_address>",
"protocol": "sftp",
"port": 22,
"username": "user1",
"remotePath": "/folder1/folder2/folder3",
"context": "./build",
"uploadOnSave": false,
"watcher": {
"files": "*.{js,html}",
"autoUpload": true,
"autoDelete": false
}
}Define connections once in User Settings (via remote-fs) and reference them by name from any project's sftp.json with the remote option.
In User Settings:
"remotefs.remote": {
"dev": {
"scheme": "sftp",
"host": "host",
"username": "username",
"rootPath": "/path/to/somewhere"
},
"projectX": {
"scheme": "sftp",
"host": "host",
"username": "username",
"privateKeyPath": "/Users/xx/.ssh/id_rsa",
"rootPath": "/home/foo/some/projectx"
}
}In sftp.json:
{
"remote": "dev",
"remotePath": "/home/xx/",
"uploadOnSave": false,
"ignore": [".vscode", ".git", ".DS_Store"]
}Open it by clicking the SFTP icon in the Activity Bar, or run View: Show SFTP.
- Browsing opens files in a read-only view by default. Run
SFTP: Edit in Local(context menu) to download a file into the workspace for editing — or flip thesftp.downloadWhenOpenInRemoteExplorersetting to make downloading the default. -
Multi-select works like the regular explorer: hold
Ctrl/CmdorShiftwhile clicking to select several files/folders, then upload or download them all at once. - Create and delete remote files/folders from the context menu (file commands).
- Hide noise (e.g.
node_modules) withremoteExplorer.filesExclude, and control root ordering withremoteExplorer.order. - After a delete, manually refresh the parent folder if the tree doesn't update on its own (known issue).
During bulk operations (folder upload/download, sync, project transfers):
- The status bar shows a live "Transferring X/Y files" counter — click it to cancel everything.
- The Transfers view in the SFTP sidebar lists each file with its status (queued / transferring / failed) and an inline ✕ button to cancel just that file.
-
SFTP: Cancel All Transfersis also available from the Command Palette and the Transfers view title bar.
Right-click any folder (or run SFTP: Compare Folders with Remote) to get a recursive diff against its remote counterpart. Results are grouped into new-local, new-remote, and modified files; picking a file offers per-file actions to open a diff, upload, or download. Use it before a sync to preview exactly what would change.
-
Don't commit credentials.
passwordandpassphraseare stored in plain text insftp.json. Prefer key-based auth (privateKeyPathoragent), keep passwords in secret storage viaSFTP: Save Password, set"passphrase": truefor a prompt instead of a stored string, and add.vscode/sftp.jsonto.gitignoreif it contains secrets. -
Ignore what you don't deploy. Add
/.git,/.vscode,node_modules, build caches, and OS junk (.DS_Store) toignore— transfers get faster and you avoid clobbering the server with noise. Use the Force commands for one-off exceptions. -
Protect live sites with atomic uploads. Enable
useTempFile(plusopenSshon OpenSSH servers) so a visitor never receives a half-uploaded file. -
Pick one auto-upload mechanism. Use either
uploadOnSaveor a broadwatcher("**/*"withautoUpload), not both — doubling up causes redundant transfers. -
Be careful with
syncOption.deleteandwatcher.autoDelete. They remove files on the destination. Run a Compare Folders first if you're unsure what a sync will do. -
Set
remoteTimeOffsetInHourswhen clocks differ — otherwise timestamp-based sync may copy in the wrong direction (seeremoteTimeOffsetInHours). -
Tune
concurrencydown for picky servers. Shared hosts often cap concurrent SFTP operations;"concurrency": 1–3trades speed for reliability. -
Verify early. After editing
sftp.json, runSFTP: Test Connectionrather than discovering an auth typo mid-upload. -
Use profiles for environments rather than juggling multiple config files — and remember the
… To All Profilescommands when a release must land everywhere.
- Open Settings (
File → Preferences → Settings, orCode → Preferences → Settingson macOS). - Set
sftp.debugtotrueand reload VS Code. - View the logs in
View → Outputand select the sftp channel.
If a connection drops mid-session, the underlying error is logged there (since 1.16.5) instead of being silently discarded.
As of 1.16.5, common SSH failures surface as plain-language messages instead of raw ssh2 errors:
| Message | Meaning |
|---|---|
| Connection refused | Nothing is listening on the configured host/port, or a firewall is blocking it. |
| Connection timed out | Host unreachable — wrong address, network/VPN issue, or a firewall silently dropping packets. |
| Host not found | Hostname couldn't be resolved; check for typos or DNS issues. |
| Authentication failed | Username, password, or private key was rejected by the server. |
Check the sftp Output channel (see Enabling debug logs) for the underlying detail.
With secure: true, some servers (notably pure-ftpd) require the data connection to reuse the control connection's TLS session, which can fail under TLS 1.3 with errors like "Client network socket disconnected before secure TLS connection was established" on every listing or transfer, even though the connection itself succeeds. Cap the TLS version via secureOptions:
{ "secureOptions": { "maxVersion": "TLSv1.2" } }This generic message comes from the remote SFTP server when a syscall fails. To pinpoint it, enable debug output on the server side and retry. Two common causes:
-
remotePathpoints at a symlink — change it to the actual (resolved) path. -
The server ran out of file descriptors — raise the server's descriptor limit, or if you can't, set
limitOpenFilesOnRemoteinsftp.json.
On legacy/old servers the connection may keep closing because of a key-exchange algorithm mismatch. Override the algorithms to drop diffie-hellman-group-exchange-sha256 from kex:
{
"algorithms": {
"kex": [
"ecdh-sha2-nistp256",
"ecdh-sha2-nistp384",
"ecdh-sha2-nistp521"
],
"cipher": [
"aes128-gcm",
"aes128-gcm@openssh.com",
"aes256-gcm",
"aes256-gcm@openssh.com",
"aes128-cbc",
"aes192-cbc",
"aes256-cbc",
"aes128-ctr",
"aes192-ctr",
"aes256-ctr"
],
"serverHostKey": [
"ssh-rsa",
"ssh-dss",
"ssh-ed25519",
"ecdsa-sha2-nistp256",
"ecdsa-sha2-nistp384",
"ecdsa-sha2-nistp521",
"rsa-sha2-256",
"rsa-sha2-512"
],
"hmac": [
"hmac-sha2-256",
"hmac-sha2-512"
]
}
}macOS has a harsh default limit on open files. Raise it:
echo kern.maxfiles=65536 | sudo tee -a /etc/sysctl.conf
echo kern.maxfilesperproc=65536 | sudo tee -a /etc/sysctl.conf
sudo sysctl -w kern.maxfiles=65536
sudo sysctl -w kern.maxfilesperproc=65536
ulimit -n 65536Historically the command was hidden and had no shortcut (see liximomo/vscode-sftp#854). It is now visible in the Command Palette and Source Control view and bound to Ctrl+Alt+U by default. It requires a Git repository with at least one commit — it uploads files changed or created since the last commit.
After deleting a remote file, the tree may not update — manually refresh the parent folder (the ↻ button or sftp.remoteExplorer.refresh).
For historical context (these are already fixed on develop):
-
TypeError: isDate is not a functionfromssh2/lib/protocol/SFTP.json upload/download — Node removedutil.isDate, whichssh2@1.13.0still used. Fixed viapatches/ssh2+1.13.0.patch, applied automatically bypatch-packageonnpm install. - Compile errors on
develop(missing command-constant imports,vscode-uridefault-export mismatch,string/URItype mismatch) — fixed;npm run compilesucceeds. - Test-suite breakage under Jest 28+ and
memfsstream-close bugs — fixed; all suites pass.
Q: How do I upload the contents of a folder, but not the folder itself?
Set context to that folder (e.g. "context": "./build") — its contents then map directly to remotePath. Full example: Uploading a folder's contents without the folder itself.
Q: How can I upload files as root? A community workaround (may not work everywhere — see liximomo/vscode-sftp#559) is:
"sshCustomParams": "sudo su -;"Q: How do I sync both ways automatically, without any user interaction?
Use a watcher on **/* with autoUpload/autoDelete plus syncOption.delete — full config in Two-way automatic sync with the watcher. This also keeps the server updated when Git changes files (branch checkout, revert).
Q: Why don't I see dotfiles/hidden files in the Remote Explorer?
Often a server-side listing setting. With proftpd, edit proftpd.conf (commonly /etc/proftpd.conf, /etc/proftpd/proftpd.conf, /usr/local/etc/proftpd.conf, or /usr/local/etc/proftpd/proftpd.conf) and change ListOptions "-l" to ListOptions "-la":
<Global>
ListOptions "-la"
</Global>Q: Do I have to store my password in sftp.json?
No — leave password out and you'll be prompted on connect, with an offer to remember the password in VS Code's secret storage. You can also save it up front with SFTP: Save Password (see Storing passwords securely). Better yet, use key-based auth (see Best Practices).
Q: What do the "Connection refused" / "Connection timed out" / "Host not found" / "Authentication failed" messages mean? See SSH connection error messages.
Q: Can I edit files directly in the Remote Explorer?
Files open read-only by default — use SFTP: Edit in Local, or set sftp.downloadWhenOpenInRemoteExplorer to make opening a file download it. See Using the Remote Explorer.
Q: Sync copies files in the wrong direction / re-uploads unchanged files.
Sync is timestamp-based; correct clock/timezone differences with remoteTimeOffsetInHours.
Q: Transfers randomly fail on my shared host.
Lower concurrency (some servers cap simultaneous operations) and/or set limitOpenFilesOnRemote if the server runs out of file descriptors.
Issues and pull requests are welcome — the project is under active maintenance again at jmwerk/vscode-sftp.
Development requires Node 22 or newer (see .nvmrc).
git clone https://github.com/jmwerk/vscode-sftp.git
cd vscode-sftp
npm install # applies patches (ssh2, memfs) via patch-package
npm run compile # production build (esbuild)
npm run dev # watch mode for development
npm run typecheck # TypeScript type check (tsc --noEmit)
npm run lint # ESLint
npm test # jest test suites
npm run package # build the .vsixHighlights of the codebase:
-
src/extension.ts/src/app.ts— activation and wiring -
src/commands/— one module per command -
src/core/— config, file system abstractions, transfer scheduling -
src/modules/remoteExplorer— the Remote Explorer view -
schema/— the JSON schema that validates.vscode/sftp.json -
patches/—patch-packagefixes applied on install
See also CONTRIBUTING.md and the CHANGELOG.
This project builds on the work of @Natizyskunk and @liximomo. If their earlier work helped you, their original donation links are in the upstream README.