Skip to content
github-actions[bot] edited this page Jul 15, 2026 · 16 revisions

SFTPresso — Complete Wiki

SFTPresso — SFTP/FTP sync for Visual Studio Code. Actively maintained fork of vscode-sftp.


Table of Contents

  1. Overview
  2. Installation and Setup
  3. Command Reference
  4. Configuration Reference (sftp.json)
  5. Usage Examples and Common Workflows
  6. Best Practices
  7. Troubleshooting and Known Issues
  8. Frequently Asked Questions
  9. Development and Contributing
  10. Credits

1. Overview

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.

Primary purposes

  • 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.

Key features

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 byte-level progress, per-file cancel, and retry for failed transfers — 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
Connection status Status bar (when enabled) An icon reflects the live remote connection state — idle, connecting/reconnecting, connected, or error; click it to run SFTP: Test Connection
Guided config setup SFTP: ConfigQuick 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

2. Installation and Setup

Installing the extension

As of v1.20.2, every tagged release is published automatically to both the VS Code Marketplace and Open VSX by .github/workflows/publish.yml, so you can install it straight from your editor:

  1. Open the Extensions view (Ctrl+Shift+X / Cmd+Shift+X).
  2. If you still have an older sftp extension installed (from @liximomo or @Natizyskunk), uninstall it first to avoid command conflicts.
  3. Search for SFTPresso and install it — or run ext install jmwerk.sftpresso from the Command Palette.

Listings: VS Code Marketplace · Open VSX (for VSCodium, Gitpod, Eclipse Theia, and other editors that use Open VSX).

To sideload a specific build instead, install from a VSIX package:

  1. Grab a .vsix from GitHub Releases — or build one from source (see Development and Contributing).
  2. In VS Code, open the Extensions view (Ctrl+Shift+X / Cmd+Shift+X).
  3. If you still have an older sftp extension installed (from @liximomo or @Natizyskunk), uninstall it first to avoid command conflicts.
  4. Open the ⋯ (More Actions) menu at the top of the Extensions view and choose Install from VSIX….
  5. Locate the .vsix file and select it.
  6. Reload VS Code.

To build the VSIX yourself:

git clone https://github.com/jmwerk/SFTPresso.git
cd vscode-sftp
npm install          # also applies bundled patches via patch-package
npm run package      # produces sftpresso-<version>.vsix via vsce

First-time setup

  1. Open the local folder you want to sync (File → Open Folder…).
  2. Open the Command Palette (Ctrl+Shift+P / Cmd+Shift+P) and run SFTP: Config.
  3. When no sftp.json exists 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.json is written, and SFTP: Test Connection runs immediately to confirm the connection works.
    • Edit JSON — the extension creates .vscode/sftp.json with a starter template. Edit it with your server details:
{
    "name": "My Server",
    "host": "server.example.com",
    "protocol": "sftp",
    "port": 22,
    "username": "user1",
    "remotePath": "/var/www/project",
    "uploadOnSave": false
}
  1. Save and close sftp.json. The extension activates automatically whenever a workspace contains .vscode/sftp.json.
  2. Type SFTP in 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:

  • password is 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 in sftp.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.json gets schema-validated in the editor, so you get completions and warnings for unknown or mistyped option names.

Verifying your connection

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).

A connection-status indicator in the status bar reflects the live state of the remote connection at a glance: a plug icon when idle, a spinner while connecting or reconnecting, an active-VM icon when connected, and an error icon (with a highlighted background) when a connection fails. Because connections reconnect lazily on the next operation, this makes silent reconnects and authentication failures visible. Clicking it runs SFTP: Test Connection.

Storing passwords securely

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 in sftp.json.
  • Or just connect: when you're prompted for a password and the connection succeeds, the extension offers to remember it.
  • Run SFTP: Clear Password to 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.


3. Command Reference

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).

Configuration and connection commands

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.

Upload commands

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.

Download commands

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 commands

Sync compares timestamps and transfers only what differs; behavior is tuned with syncOption. Enable syncConfirm to preview and confirm exactly what a sync will upload, overwrite, and delete before it runs.

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 remoteTimeOffsetInHours so timestamp comparison stays accurate.

Diff and compare commands

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.

Remote Explorer and file commands

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.

Transfer management commands

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.
Retry Transfer sftp.retryTransfer Re-queue a single failed file — the inline ↻ button on failed items in the Transfers view.

Force (Alt) commands

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.

Commands with keybinding arguments

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"
}

Default keybindings

Keys Command When
Ctrl+Alt+U SFTP: Upload Changed Files Extension active

4. Configuration Reference (sftp.json)

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.

Common options

Apply to both SFTP and FTP unless noted.

name

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" }

context

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" }

protocol

Transfer protocol.

Key Type Default
protocol "sftp" | "ftp" "sftp"

host

Hostname or IP address of the server.

Key Type
host string

port

Server port. Typically 22 for SFTP, 21 for FTP, 990 for implicit FTPS.

Key Type
port integer

username

Username for authentication.

Key Type
username string

password

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 in sftp.json are stored as plain text. Prefer privateKeyPath or agent — or keep the password in secret storage — and keep sftp.json out of version control. See Best Practices.

Key Type
password string

remotePath

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 /

filePerm

Octal permissions applied to newly created remote files.

Key Type Default
filePerm number unset
{ "filePerm": 644 }

dirPerm

Octal permissions applied to newly created remote directories.

Key Type Default
dirPerm number unset
{ "dirPerm": 750 }

uploadOnSave

Upload the file on every VS Code save. See the Upload on save workflow.

Key Type Default
uploadOnSave boolean false

useTempFile

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

openSsh

Enable atomic file uploads (rename-into-place). Only supported by OpenSSH servers.

💡 If openSsh is true, useTempFile must also be true.

Key Type Default
openSsh boolean false
{ "openSsh": true, "useTempFile": true }

downloadOnOpen

Download the remote version of a file whenever it is opened locally.

Key Type Default
downloadOnOpen boolean false

syncOption

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 Directions honors only skipCreate and ignoreExisting.

syncConfirm

Show a dry-run preview before a Sync command runs. The extension walks the local/remote diff, computes exactly what the sync would do — respecting syncOption — and shows a modal summary such as "Sync Local → Remote: 3 uploads, 1 overwrite, 2 deletions. Proceed?" with the affected files listed. Cancel leaves everything untouched; Proceed runs the sync unchanged. If nothing differs, an info message is shown and no sync runs.

Key Type Default
syncConfirm boolean true when syncOption.delete is enabled, otherwise false

The default is deliberately conservative: syncs that can delete files on the destination prompt by default, while non-destructive syncs don't. Set it explicitly to always (or never) confirm.

{
  "syncConfirm": true
}

ignore

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"
  ]
}

ignoreFile

Path to an ignore file (e.g. .gitignore-style list) — absolute, or relative to the workspace root.

Key Type
ignoreFile string
{ "ignoreFile": ".gitignore" }

watcher

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 uploadOnSave to false when watching everything ("**/*") — otherwise every save triggers both mechanisms.

{
  "watcher": {
    "files": "dist/*.{js,css}",
    "autoUpload": true,
    "autoDelete": false
  }
}

remoteTimeOffsetInHours

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 MLSD command (e.g. pure-ftpd, ProFTPD) report exact UTC timestamps, so this option should normally stay 0 for them. It's mainly needed for FTP servers limited to LIST (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

remoteExplorer

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
  }
}

concurrency

Maximum simultaneous transfers. Lower it if your server limits concurrent connections/operations.

Key Type Default
concurrency number 4

connectTimeout

Maximum time (ms) to wait when establishing a connection.

Key Type Default
connectTimeout number 10000

limitOpenFilesOnRemote

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

profiles

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

remote

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

SFTP-only options

agent

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" }

privateKeyPath

Absolute path to your private key file.

Key Type
privateKeyPath string
{ "privateKeyPath": "/Users/me/.ssh/id_rsa" }

passphrase

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 }

interactiveAuth

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 }

algorithms

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"
    ]
  }
}

sshConfigPath

Path to your OpenSSH client config file; matching Host entries contribute settings to the connection.

Key Type Default
sshConfigPath string ~/.ssh/config

sshCustomParams

Extra parameters appended to the ssh command used by SFTP: Open SSH in Terminal.

Key Type
sshCustomParams string
{ "sshCustomParams": "-g" }

hop

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[]

FTP(S)-only options

secure

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 }

secureOptions

Extra options passed straight to Node's tls.connect() — e.g. to accept a self-signed certificate.

Key Type
secureOptions object
{ "secureOptions": { "rejectUnauthorized": false } }

VS Code extension settings

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".

5. Usage Examples and Common Workflows

Simple single-server setup

The minimum viable config — SFTP on port 22, prompted for the password:

{
  "host": "host",
  "username": "username",
  "remotePath": "/remote/workspace"
}

Start from a remote project

If the latest files already live on the server, start with an empty local folder:

  1. Open the empty folder in VS Code and run SFTP: Config (see First-time setup).
  2. Set remotePath to the server directory you want.
  3. Run SFTP: Download Project — the remote tree is downloaded into your workspace.
  4. From here on, edit locally and upload/sync as needed.

Upload on save

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.)

Profiles (dev / prod)

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"
}

context and watcher are only available at the root level, not inside a profile.

To deploy to every environment at once, use the … To All Profiles upload commands.

Multiple contexts (array config)

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"
  }
]

Connection hopping (SSH proxy / bastion)

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"
    }
  ]
}

Two-way automatic sync with the watcher

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).

Uploading a folder's contents without the folder itself

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
  }
}

Configuration in User Settings (remote-fs)

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"]
}

Using the Remote Explorer

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 the sftp.downloadWhenOpenInRemoteExplorer setting to make downloading the default.
  • Multi-select works like the regular explorer: hold Ctrl/Cmd or Shift while 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) with remoteExplorer.filesExclude, and control root ordering with remoteExplorer.order.
  • After a delete, manually refresh the parent folder if the tree doesn't update on its own (known issue).

Monitoring and cancelling transfers

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.
  • While a file is transferring, its row shows byte-level progress in the description — e.g. 42% — 3.1 MB / 7.4 MB, or just the bytes transferred when the total size isn't known. Updates are throttled to a couple per second per file.
  • A failed transfer keeps its row (marked failed) with an inline ↻ Retry button (sftp.retryTransfer). Retrying re-queues just that file with its original direction and options and resets its status to queued.
  • SFTP: Cancel All Transfers is also available from the Command Palette and the Transfers view title bar.

Comparing folders with the remote

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.


6. Best Practices

  • Don't commit credentials. password and passphrase are stored in plain text in sftp.json. Prefer key-based auth (privateKeyPath or agent), keep passwords in secret storage via SFTP: Save Password, set "passphrase": true for a prompt instead of a stored string, and add .vscode/sftp.json to .gitignore if it contains secrets.
  • Ignore what you don't deploy. Add /.git, /.vscode, node_modules, build caches, and OS junk (.DS_Store) to ignore — 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 (plus openSsh on OpenSSH servers) so a visitor never receives a half-uploaded file.
  • Pick one auto-upload mechanism. Use either uploadOnSave or a broad watcher ("**/*" with autoUpload), not both — doubling up causes redundant transfers.
  • Be careful with syncOption.delete and watcher.autoDelete. They remove files on the destination. Leave syncConfirm on (its default when delete is enabled) to preview and confirm deletions before they happen, or run a Compare Folders first if you're unsure what a sync will do.
  • Set remoteTimeOffsetInHours when clocks differ — otherwise timestamp-based sync may copy in the wrong direction (see remoteTimeOffsetInHours).
  • Tune concurrency down for picky servers. Shared hosts often cap concurrent SFTP operations; "concurrency": 1–3 trades speed for reliability.
  • Verify early. After editing sftp.json, run SFTP: Test Connection rather than discovering an auth typo mid-upload.
  • Use profiles for environments rather than juggling multiple config files — and remember the … To All Profiles commands when a release must land everywhere.

7. Troubleshooting and Known Issues

Enabling debug logs

  1. Open Settings (File → Preferences → Settings, or Code → Preferences → Settings on macOS).
  2. Set sftp.debug to true and reload VS Code.
  3. View the logs in View → Output and 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.

SSH connection error messages

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.

FTPS transfers fail after connecting (TLS session reuse)

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" } }

Error: Failure

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:

  1. remotePath points at a symlink — change it to the actual (resolved) path.
  2. The server ran out of file descriptors — raise the server's descriptor limit, or if you can't, set limitOpenFilesOnRemote in sftp.json.

Error: Connection closed

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"
    ]
  }
}

ENFILE: file table overflow (macOS)

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 65536

Upload Changed Files does nothing

Historically 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.

Remote Explorer not refreshing after delete

After deleting a remote file, the tree may not update — manually refresh the parent folder (the ↻ button or sftp.remoteExplorer.refresh).

Known issues fixed in this fork

For historical context (these are already fixed on develop):

  • TypeError: isDate is not a function from ssh2/lib/protocol/SFTP.js on upload/download — Node removed util.isDate, which ssh2@1.13.0 still used. Fixed via patches/ssh2+1.13.0.patch, applied automatically by patch-package on npm install.
  • Compile errors on develop (missing command-constant imports, vscode-uri default-export mismatch, string/URI type mismatch) — fixed; npm run compile succeeds.
  • Test-suite breakage under Jest 28+ and memfs stream-close bugs — fixed; all suites pass.

8. Frequently Asked Questions

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.


9. Development and Contributing

Issues and pull requests are welcome — the project is under active maintenance again at jmwerk/SFTPresso.

Development requires Node 22 or newer (see .nvmrc).

git clone https://github.com/jmwerk/SFTPresso.git
cd SFTPresso
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 .vsix

Highlights 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-package fixes applied on install

See also CONTRIBUTING.md and the CHANGELOG.

10. Credits

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.

Clone this wiki locally