Back to Resources
Cheat Sheet

SFTP Commands Cheat Sheet (2026): Complete sftp Command Reference

A complete SFTP command reference for 2026. Covers connecting, uploading, downloading, navigating directories, permissions, resuming transfers, and scripting with sftp and sftp batch mode. Includes Rilavek SFTP endpoint examples.

Most SFTP cheat sheets are either a wall of undifferentiated commands with no context, or a table with zero explanation for why a flag does what it does. This one's different. Commands are grouped by what you're actually trying to do — not by alphabetical order or whatever the man page happened to list first.

This guide covers connecting, uploading, downloading, recursive transfers, permissions, batch mode, scripting, CI usage, and debugging, all using the OpenSSH sftp client.

One important scope note: these examples target the OpenSSH sftp client, which is the default on Linux and macOS. Windows ships it in modern Windows 10/11 too, though plenty of Windows users reach for WinSCP or FileZilla as a GUI alternative — both speak the same SFTP protocol underneath. Commands like reget, reput, and progress are OpenSSH-specific, so don't expect them in other clients. If you're trying to move large files reliably, skip ahead to SFTP vs SCP vs rsync before picking your tool.

Jump to what you need:


Connecting

Port 22 is the default. Most servers you'll hit use it. But some — Rilavek included — run on a non-standard port, so always check before assuming the connection failed.

# Standard connection (port 22)
sftp user@hostname

# Non-standard port — use -P (uppercase)
sftp -P 2222 user@hostname

# Connect with a specific SSH private key
sftp -i ~/.ssh/id_rsa user@hostname

# Connect with verbose output (debug connection issues)
sftp -v user@hostname

# Connect through a bastion host / jump host
sftp -J jumpuser@bastion user@target

# Override the SFTP subsystem path (when server doesn't expose it as default)
sftp -s /usr/lib/ssh/sftp-server user@hostname

The flag trap: -p (lowercase) preserves modification times, access times, and modes during transfer. -P (uppercase) sets the port. They're not the same flag. Mix them up and you'll get an error that doesn't immediately point you to the problem.

-J (ProxyJump) is worth knowing if you work in environments where servers aren't directly reachable from the internet. It replaces the older and more verbose -o ProxyCommand=... approach and works on any OpenSSH >= 7.3. You can also bake the jump host into ~/.ssh/config using ProxyJump bastion so you don't have to type it every time.

-s (subsystem) is one of those flags you won't touch for months and then desperately need. If a connection succeeds but immediately drops, or you get "Received message too long," the server probably isn't advertising SFTP as its default subsystem. Passing the subsystem path directly fixes it.


Navigating Directories

Once connected, you're in an interactive prompt. Most commands look like shell commands you already know — but there's one thing that trips people up almost every time: any command prefixed with l runs on your local machine, not the remote server. ls lists remote files. lls lists local files. Same idea applies to cd vs lcd and pwd vs lpwd.

# Show your current remote directory
pwd

# Show your current LOCAL directory
lpwd

# List remote directory contents
ls
# long listing with hidden files
ls -la

# List LOCAL directory contents
lls
lls -la

# Change remote directory
cd /path/to/remote/dir

# Change LOCAL directory
lcd /path/to/local/dir

If you're getting "no such file" errors on upload, run lpwd and lls before anything else. The interactive shell does start in your current local directory, but after a few lcd commands it's easy to lose track of where you actually are.


Uploading Files

Uploading a Single file

put localfile.txt

# upload with a different name
put localfile.txt remotefile.txt    

# Upload from an absolute local path
put /full/path/to/localfile.txt     

Uploading Multiple files with a wildcard

put *.csv
put /data/exports/*.json

Recursive directory upload

put -R local_directory/
put -R local_directory/ remote_directory_name/

The recursive gotcha: -R doesn't create the parent remote directory for you. If remote_directory_name doesn't already exist on the server, put -R will either fail outright or behave in confusing ways. Create the directory first with mkdir remote_directory_name, then run the recursive upload.

Resume an interrupted transfer (OpenSSH only)

OpenSSH's SFTP client supports reget and reput for resuming interrupted transfers:

reget largefile.bin      # resume a partially downloaded file
reput largefile.bin      # resume a partially uploaded file

reget/reput only work if the partial file already exists at the destination with the same filename, and the server has to support partial transfers — not all do. When it works, it's great. When it doesn't, rsync -avz --partial -e ssh is the reliable fallback.


Downloading Files

Downloading a Single file

get remotefile.txt

# save with a different name
get remotefile.txt /local/save/path/localfile.txt   

Downloading Multiple files with a wildcard

get *.log
get /remote/path/*.csv /local/destination/

Recursive directory download

get -R remote_directory/
get -R /remote/path/directory/ /local/destination/

Wildcard expansion is a subtle one. Depending on the SFTP implementation, wildcards get expanded either by the client or by the server. If get *.csv comes back with "no such file," run ls *.csv first. If the server can resolve it there, it'll work in get too.


Managing Files and Permissions

Create and remove directories

mkdir new_directory
rmdir empty_directory               # only removes empty directories

mkdir -p isn't in the SFTP spec, and OpenSSH sftp doesn't support it. If you need a nested path, you have to build it layer by layer:

mkdir path
mkdir path/to
mkdir path/to/nested
mkdir path/to/nested/directory

Delete files

rm remotefile.txt

There's no SFTP equivalent of rm -rf. If you need to recursively wipe a remote directory, drop to an SSH session and do it there — ssh user@host "rm -rf /path/to/dir" — or remove files one by one. Not pretty, but that's the protocol.

Rename and move

rename old_filename.txt new_filename.txt
rename old_directory/ new_directory/

Set permissions

chmod 644 remotefile.txt
chmod 755 remote_directory/

Check file size and metadata

# timestamps, size, permissions
ls -la

# check available remote disk space 
# (not all servers support this)
df                      

Batch Mode and Scripting

Interactive SFTP is fine for one-off transfers. For anything repeatable — nightly jobs, deploy scripts, data pipelines — you want batch mode.

Run commands from a file

sftp -b commands.txt user@hostname

The contents of commands.txt look exactly like what you'd type at the interactive prompt:

cd /remote/uploads
lcd /local/exports
put report_2026.csv
put report_2026_summary.csv
bye

Error handling is more nuanced than you might expect. OpenSSH sftp aborts the batch when commands like get, put, rename, rm, mkdir, ls, or df fail. If there's a specific step you expect might fail — and you want to continue past it — prefix that line with - in the batch file. Either way, check $? in your wrapper and verify that the expected files actually made it to the destination. Don't just trust a zero exit code.

Pipe commands directly

echo "put localfile.txt" | sftp -b - user@hostname

Full automation script example

#!/bin/bash
HOST="sftp.example.com"
PORT="22"
USER="deploy_user"
KEY="~/.ssh/deploy_key"
REMOTE_DIR="/uploads/processed"
LOCAL_DIR="/tmp/ready_to_upload"

sftp -P "$PORT" -i "$KEY" -b - "$USER@$HOST" << EOF
cd $REMOTE_DIR
lcd $LOCAL_DIR
put *.csv
bye
EOF

if [ $? -ne 0 ]; then
  echo "SFTP transfer failed" >&2
  exit 1
fi

The << EOF heredoc sends commands to SFTP's batch mode via stdin. It's cleaner than writing a temp file, and in security-conscious environments where you're careful about what touches /tmp, it's good practice.


One-Liner Transfers

For quick, non-interactive uploads from the shell without writing a commands file:

# Upload a single file in one line
sftp -P 2222 user@hostname <<< $'put file.txt\nbye'

# Download a file in one line
sftp -P 2222 user@hostname <<< $'get remotefile.txt\nbye'

The $'...' ANSI-C quoting syntax is how you embed literal newlines in a single shell string. Works in bash and zsh. Handy for a quick one-off without spinning up a full script.


CI and GitHub Actions

SFTP in CI has one specific failure mode that will confuse you the first time: the host key prompt. If the known hosts file isn't pre-populated, the connection just... sits there waiting for input that never arrives, and the job times out.

StrictHostKeyChecking=no skips the prompt. UserKnownHostsFile=/dev/null keeps the runner filesystem clean. Both are fine on ephemeral CI machines that get destroyed after the job runs. Just don't copy-paste these flags onto a long-lived server. Disabling host key checking on a persistent machine removes your protection against man-in-the-middle attacks.

For GitHub Actions specifically:

- name: Upload artifacts via SFTP
  env:
    SFTP_KEY: ${{ secrets.SFTP_PRIVATE_KEY }}
  run: |
    echo "$SFTP_KEY" > /tmp/sftp_key
    chmod 600 /tmp/sftp_key
    sftp -o StrictHostKeyChecking=no \
         -o UserKnownHostsFile=/dev/null \
         -i /tmp/sftp_key \
         -P 2222 \
         -b deploy/sftp_commands.txt \
         [email protected]
    rm /tmp/sftp_key

Write the private key to a temp file, lock it down with chmod 600 (SSH refuses to use keys with loose permissions — it won't just warn you, it'll reject the key outright), then clean up after. Passing it in via -i keeps it out of ssh-agent, which simplifies the job environment considerably.


Common Flags

FlagWhat it doesNotes
-P portSet port numberUppercase P. Common values: 22, 2222
-i identity_fileSSH private key pathCan also be set in ~/.ssh/config
-b batch_fileRun commands from a fileUse -b - to read from stdin
-rRecursive (with get/put)Target remote directory must exist for put -r
-J jump_hostConnect via bastion/jump hostReplaces the older -o ProxyCommand pattern
-s subsystemOverride SFTP subsystem pathUse when server doesn't expose SFTP as default subsystem
-vVerbose outputAdd more -v flags (-vv, -vvv) for more detail
-qQuiet modeSuppresses progress meter — useful in scripts
-CCompressionRarely worth enabling; most connections are already bandwidth-limited by other factors
-o optionSSH option passthroughe.g., -o StrictHostKeyChecking=no for first-time connections in CI
-l limitBandwidth limit in Kbit/sUseful when sharing a connection

SSH config shortcut

Typing the full flag string on every sftp invocation gets old fast, especially when you're juggling multiple servers. Stick a host entry in ~/.ssh/config and you only type it once:

Host myserver
    HostName sftp.myserver.com
    User deploy
    Port 2222
    IdentityFile ~/.ssh/prod_key

Then sftp myserver is all you need.


Interactive Session Reference

Every command available at the interactive sftp> prompt, with accurate signatures for the OpenSSH client:

CommandDescription
pwdPrint remote working directory
lpwdPrint local working directory
ls [path]List remote directory
lls [path]List local directory
cd [path]Change remote directory
lcd [path]Change local directory
get [-afpR] remote [local]Download file
put [-afpR] local [remote]Upload file
mkdir pathCreate remote directory
rmdir pathRemove remote directory (must be empty)
rm fileDelete remote file
rename old newRename remote file or directory
chmod mode fileChange remote file permissions
chown uid fileChange remote file owner
chgrp gid fileChange remote file group
copy old new / cp old newCopy a remote file (server support required)
ln [-s] old newCreate a remote hard link or symlink
symlink old newCreate a remote symbolic link
lmkdir pathCreate a local directory
lumask umaskSet the local umask
reget [-fpR] remote [local]Resume interrupted download (OpenSSH only)
reput [-fpR] local [remote]Resume interrupted upload (OpenSSH only)
progressToggle progress meter on/off (OpenSSH client only)
dfShow remote disk usage (server must support it)
versionShow SFTP protocol version
help / ?List all available commands
bye / exit / quitClose the connection
!Drop to local shell (type exit to return)

The ! command is genuinely underused. If you're mid-session and you want to check something locally before uploading — inspect a file, run a quick grep — !ls -la or !cat localfile.txt drops you to a local shell without closing the connection. Type exit to come back.


Testing Your Rilavek SFTP Endpoint

Rilavek runs a standard SFTP endpoint on port 2222, which means any OpenSSH sftp client, CI runner, or automation script connects to it without any special SDK or library. Host is sftp.rilavek.com, credentials come from the pipe you set up in the dashboard.

Basic connection test

sftp -P 2222 [email protected]

You'll get a password prompt (or skip it entirely with key auth if you've added your SSH public key to the pipe). A successful connection drops you into the sftp> prompt.

Upload a test file

sftp -P 2222 [email protected]
sftp> put /tmp/test.txt
sftp> bye

Check your S3 bucket — or whichever data store the pipe points at — and the file should be there immediately. Rilavek streams straight to your storage. It doesn't write to its own disk.

Automated upload script for Rilavek

#!/bin/bash
RILAVEK_HOST="sftp.rilavek.com"
RILAVEK_PORT="2222"
RILAVEK_USER="your_user"
KEY_PATH="~/.ssh/rilavek_key"
LOCAL_FILES="/data/exports/*.csv"

sftp -P "$RILAVEK_PORT" -i "$KEY_PATH" -b - "$RILAVEK_USER@$RILAVEK_HOST" << EOF
put $LOCAL_FILES
bye
EOF

Using SSH config for convenience

Host rilavek
    HostName sftp.rilavek.com
    User your_user
    Port 2222
    IdentityFile ~/.ssh/rilavek_key

After adding that to ~/.ssh/config, connecting is just:

sftp rilavek

For a quick one-liner upload using the alias:

sftp rilavek <<< $'put /local/file.csv\nbye'

Verbose connection debug

Connection failing? Verbose mode breaks down exactly where the handshake stops:

sftp -v -P 2222 [email protected]

Common failure points:

  • Authentication failure: Check your Rilavek credentials or SSH key setup
  • Connection refused: Verify port 2222 is not blocked by your firewall or VPN
  • Connection hangs before password prompt: Almost always a firewall blocking outbound TCP 2222. The SYN packet gets through but the server's response is dropped. Test with nc -zv sftp.rilavek.com 2222 to confirm connectivity before debugging further.
  • Host key verification: On first connection, accept the host key. To skip in CI: -o StrictHostKeyChecking=no

The One Thing Most Cheat Sheets Don't Tell You

SFTP and SCP use the same SSH transport. They're not the same tool. The old SCP protocol was deprecated over security concerns, and modern OpenSSH versions now run scp over SFTP internally anyway — so your scp commands still work, they're just speaking SFTP under the hood now. For anything new, write to sftp or rsync -e ssh directly.


SFTP vs SCP vs rsync

All three tools move files over SSH. They're not interchangeable — each has a specific situation where it's the right call.

ToolBest forWeakness
sftpInteractive sessions, simple scripts, when the server speaks SFTP onlyNo built-in resume for arbitrary transfers; reget/reput are server-dependent
scpQuick one-off copies between two hostsLegacy SCP protocol deprecated; now uses SFTP internally in modern OpenSSH
rsyncLarge files, resume, syncing directories, skipping unchanged filesRequires rsync installed on both client and server

For big files, anything that might get interrupted, or syncing a whole directory, rsync is the better tool:

rsync -avz --partial --progress -e "ssh -p 2222" /local/path/ [email protected]:/

--partial keeps the partially transferred file in place so a retry picks up where it left off rather than starting over. rsync skips unchanged files, shows real progress output, and recovers from interruptions gracefully. Use sftp for interactive sessions, quick scripts, and servers that only speak SFTP. Use rsync when you need durability.


Ready to implement this workflow?

Start your free trial today and connect your data in minutes.

Get Started for Free