termique
Blog
Guide10 min read

SFTP for developers: the complete guide (2026)

SFTP for developers: how it works, CLI commands, automation, security, and picking a client. The 2026 guide covering everything you need.

SFTP for developers: the complete guide (2026)

SFTP for developers usually starts as a one-off: a client asks for a file, you open whatever GUI tool is already installed, or paste together an scp command from memory, and it works, until the day it doesn’t. Port 21 gets blocked by a firewall, IT asks for an audit trail you don’t have, or a script that ran fine in your terminal fails silently in CI. This guide covers SFTP for developers end to end: how it actually works, when to reach for it instead of scp or rsync, the command line and automation patterns that hold up in production, and the security choices that matter more than the protocol itself.

What is SFTP, and why do developers still rely on it in 2026?

SFTP stands for SSH File Transfer Protocol. It is not FTP running over an encrypted tunnel, despite the similar name. SFTP is a completely separate protocol built on top of SSH: it reuses SSH’s authentication and encrypted channel, then layers a binary file-transfer subsystem on top. That’s why a working SSH connection is the only prerequisite: if you can SSH into a host, you can almost always SFTP into it too, using the same key, the same port, and the same access controls.

The reason it’s still the default in 2026 is simple: nobody has shipped a compelling reason to replace it. Plain FTP transmits credentials and file contents unencrypted, which rules it out for anything internet-facing. FTPS bolts TLS onto FTP but keeps FTP’s multi-port, stateful-connection headaches. SFTP inherits none of that: one port, one encrypted channel, one authentication step, and a real set of file operations (list, rename, delete, resume, set permissions) instead of just upload and download.

How does SFTP actually work under the hood?

An SFTP session goes through the same three steps as any SSH connection, plus one extra step at the end:

  • TCP connection and SSH handshake: client and server negotiate the encryption and agree on a shared session key.
  • Authentication: password, SSH key, or both, all transmitted over the already-encrypted channel, never in the clear.
  • Channel setup: instead of dropping you into a shell, the server starts the SFTP subsystem (typically /usr/lib/openssh/sftp-server on Linux).
  • File operations: the client and server exchange SFTP protocol messages, a binary request/response format, not the human-readable command sequences you’d see in an FTP capture.

The practical upshot is that SFTP needs exactly one open port (22, same as SSH) for both the control messages and the file data. FTP, by contrast, opens a control connection on port 21 and then a separate data connection per transfer, which is why FTP is such a headache behind NAT and corporate firewalls. SFTP also supports resuming an interrupted transfer partway through, something plain scp historically couldn’t do.

SFTP vs FTP vs SCP vs rsync: which one should developers actually use?

These four get lumped together constantly, but they solve different problems. Here’s the short version:

When SFTP wins

Reach for SFTP when you need interactive browsing (listing remote directories, checking file sizes and permissions before you commit to a transfer), when you’re in an environment that blocks raw scp or rsync but allows SSH, or when you want a resumable transfer without re-sending a whole file after a dropped connection. SFTP is also the only one of the four with a real permission and rename API, which matters if a script needs to do more than copy bytes.

When SCP or rsync wins instead

SCP (secure copy) is worth knowing still works, since scp as a command is common in muscle memory, but the underlying SCP protocol is deprecated in modern OpenSSH; recent versions of the scp command actually use the SFTP protocol internally by default. Use rsync instead of SFTP when you’re syncing an entire directory tree repeatedly and only want to send the bytes that changed: rsync’s delta-transfer algorithm compares source and destination and skips anything already identical, which SFTP and scp both re-send in full every time. For a one-off copy of a single file where you don’t need directory browsing, plain scp syntax is still the fastest thing to type.

How do you use SFTP from the command line?

The OpenSSH sftp client ships on every Linux distribution and macOS by default, and on Windows via OpenSSH’s built-in client or WSL. A basic interactive session looks like this:

sftp user@host
sftp> ls
sftp> cd /var/www/releases
sftp> get deploy.log
sftp> put ./build/app.tar.gz
sftp> bye

Core commands: put, get, mput, mget, cd and lcd

  • get remote-file downloads a file to your current local directory; get -r remote-dir does it recursively.
  • put local-file uploads a file to the remote working directory; put -r local-dir uploads a whole tree.
  • mget *.log and mput *.tar.gz apply a wildcard pattern to multiple files in one command.
  • cd changes the remote working directory; lcd changes your local one, which is easy to mix up until you’ve done it a few times.
  • lpwd/pwd print the local and remote working directories, useful when a script needs to confirm where it actually landed.

Connecting with key-based auth instead of a password

For anything beyond an interactive one-off, use a key instead of a password: sftp -i ~/.ssh/deploy_key user@host. Key-based auth is what makes automation possible in the first place, since there’s no prompt to answer when the connection comes from a cron job or a CI runner.

How do you automate SFTP with batch scripts and CI pipelines?

Writing a batch file and running it with sftp -b

The sftp client accepts a batch file of commands, one per line, run non-interactively with the -b flag. This is the pattern behind most scheduled and CI-triggered transfers:

# deploy.batch
cd /var/www/releases
put build/app.tar.gz
ls -la
bye
sftp -i ~/.ssh/deploy_key -b deploy.batch user@host

Handling failures mid-script

By default, batch mode aborts the whole script the moment any command fails, which is usually what you want in a deploy step. When a specific command is allowed to fail without stopping the rest (for example, deleting a file that might not exist yet), prefix that single line with a dash: -rm old-release.tar.gz. Check the exit code of the sftp process itself in your CI step so a failed transfer actually fails the pipeline instead of silently continuing to the next stage.

Can you use SFTP programmatically, outside the command line?

Most languages have a mature SFTP library, so you rarely need to shell out to the sftp binary from application code. Python’s paramiko and the higher-level pysftp wrapper, Node’s ssh2-sftp-client, and Go’s golang.org/x/crypto/ssh paired with pkg/sftp all expose connect, list, upload, download, and rename as plain function calls instead of parsed command output. This matters for anything beyond a simple deploy script: a backend service that accepts customer file drops, a data pipeline pulling exports from a partner’s SFTP endpoint, or a monitoring job that needs to check file freshness on a schedule.

import paramiko

transport = paramiko.Transport(("host", 22))
transport.connect(username="deploy", pkey=paramiko.RSAKey.from_private_key_file("key.pem"))
sftp = paramiko.SFTPClient.from_transport(transport)
sftp.put("build/app.tar.gz", "/var/www/releases/app.tar.gz")
sftp.close()
transport.close()

The same rule applies here as anywhere else: never hardcode the private key path or password in the script itself. Load it from an environment variable, a secrets manager, or a vault the runtime resolves at execution time, and scope that key to only the directories it actually needs. If your organization would rather not run and patch its own SFTP server at all, managed options exist too, AWS Transfer Family and similar services expose an SFTP endpoint in front of object storage, trading operational overhead for a monthly bill.

What are the SFTP security best practices for production?

Key-based auth, chrooted jails, and transfer-only accounts

Disable password authentication for any account that exposes SFTP, and require keys instead: it removes brute-force login as a viable attack entirely. For accounts that only need to move files, not run commands, configure OpenSSH’s ForceCommand internal-sftp with ChrootDirectory in sshd_config. That combination drops the user into a jailed directory with no shell access at all, so a compromised or careless client account can’t do anything beyond the files it was given.

Why credential storage matters as much as the transfer protocol

SFTP’s transport is encrypted by design, but that only protects data in transit. Most real incidents involving file transfer aren’t a broken protocol, they’re a private key or password sitting in plaintext in a config file, a shared drive, or a teammate’s notes app. This is the same problem SSH credential security covers more broadly: where a key lives at rest matters as much as how it’s used on the wire. Store SFTP keys the same way you’d store any SSH key, in an OS keychain or an encrypted vault, never in a plaintext file that outlives the person who created it.

How do you choose an SFTP client for daily developer work?

Most developers end up choosing an SFTP client at the same time they choose an SSH client, since the two workflows overlap so heavily; see this blog’s comparison of SSH clients for developers for the fuller picture. A few things are worth checking specifically for SFTP work, regardless of which app you land on:

  • Does it let you browse and transfer files in the same session as your terminal, or does it require switching apps entirely?
  • Are credentials and private keys encrypted at rest on your machine, or stored as plaintext config?
  • Does it keep any kind of log of what was transferred, useful for anyone who needs to explain a file movement after the fact?
  • Does drag-and-drop actually work reliably for large directory trees, or does it choke past a few hundred files?

A GUI-only SFTP client is fine for occasional manual transfers. Anyone scripting transfers regularly, or working across more than a couple of servers, benefits more from a tool that keeps the terminal, the file browser, and the credential vault in one place instead of three separate apps.

Common SFTP errors and how to fix them

  • Connection refused: the SSH daemon isn’t running, or a firewall is blocking port 22. Confirm with ssh -v user@host first, since a plain SFTP client often gives a less specific error than the SSH client does.
  • Connection timed out: usually a security group or firewall rule dropping the packet silently rather than rejecting it. Check the network path before assuming the server itself is misconfigured.
  • Permission denied (publickey): the key isn’t the one the server expects, or its file permissions are too open (SSH refuses to use a private key that’s world-readable; chmod 600 fixes most of these).
  • Received message too long: something in the shell’s startup (a .bashrc/.profile that prints output) is polluting the SFTP subsystem’s binary channel. Keep login scripts silent for non-interactive sessions.
  • Host key verification failed: the server’s key changed, whether from a legitimate reinstall or something worth investigating. Never blanket-disable host key checking to make the error go away; remove the specific stale entry from known_hosts instead.

Where this fits into the rest of your file-transfer workflow

This guide covers the core of SFTP for developers: the protocol, the command line, automation, and security. Two things deliberately sit outside its scope and get their own dedicated coverage on this blog: choosing between SFTP and rsync specifically for automated backup jobs, and safely scheduling SFTP transfers on a cron timer without leaking credentials into a crontab. Both build directly on the fundamentals above.

termique’s SFTP file browser runs over the same encrypted session as its SSH terminal, with credentials encrypted on-device before they ever leave the machine, and has been open to every plan, including the free tier, since v0.3.0. If the workflow above is how you already work, it’s worth trying without switching to a separate app for file transfers.

Try termique free.

SSH manager with end-to-end encrypted credentials, AI assistant, and cross-device sync.

Download free

Keep reading

All articles ⟶