HOWTO · Python
How to Use SCP Protocol in Python
This tutorial discusses the use of Secure Copy Protocol with Python.
On this page
The scp command and Python’s SCPClient can both transfer files between a local machine and a remote server over SSH. Use SCPClient when you want to manage the SSH connection and transfer calls in Python; use subprocess.run() when the OpenSSH scp program is already installed and you want Python to launch it. In either case, configure authentication and trusted host keys before transferring data.
Use the SCP Module to Use SCP Protocol in Python
Install the paramiko and scp packages in the environment that runs your application. SSHClient opens the SSH connection, and SCPClient uses that connection’s transport to send or receive files. This library communicates through its SCP implementation over SSH; the older SCP1/SSH1 description sometimes repeated in examples should not be used as a general description of current Paramiko and scp package behavior.
The following script accepts a host, user, and local file path from environment variables. Replace the defaults with your connection details, and set SCP_MODE to download, recursive, or memory for the other transfer forms. The default upload mode sends the local file to /home/<user>/. The example loads known host keys and leaves Paramiko’s normal missing-host-key policy in place, so an unknown host is not silently trusted. It uses the SSH agent or configured key files when available; set the relevant authentication options for your environment. This network example is not accompanied by observed output.
import io
import os
def transfer():
host = os.environ.get("SCP_HOST")
if not host:
return
import paramiko
from scp import SCPClient
user = os.environ.get("SCP_USER", "your_user")
local_path = os.environ.get("SCP_LOCAL_FILE", "report.txt")
remote_path = f"/home/{user}/report.txt"
mode = os.environ.get("SCP_MODE", "upload")
ssh = paramiko.SSHClient()
ssh.load_system_host_keys()
scp = None
try:
ssh.connect(hostname=host, username=user)
scp = SCPClient(ssh.get_transport())
if mode == "download":
scp.get(remote_path, local_path)
elif mode == "recursive":
scp.put("local_directory", remote_path=f"/home/{user}/", recursive=True)
elif mode == "memory":
scp.putfo(io.BytesIO(b"report contents\n"), remote_path)
else:
scp.put(local_path, remote_path)
finally:
try:
if scp is not None:
scp.close()
finally:
ssh.close()
if __name__ == "__main__":
transfer()
put(local_path, remote_path) uploads a local file, while get(remote_path, local_path) downloads it. put(..., recursive=True) sends a directory tree. putfo() accepts a binary file-like object, which is useful when the content is already in memory; its first argument must be readable from its current position. The finally block closes the SCP client and then the SSH client, including when a transfer raises an error. Keep host-key checking enabled and configure the intended key, agent, or other supported authentication method rather than embedding a password in source code. Loading system host keys only supplies trusted keys; it does not provide credentials or guarantee that every server is already known.
Use the subprocess.run() Function to Use SCP Protocol in Python
subprocess.run() starts a local process. It does not execute a shell command on the remote server: the local scp executable handles the transfer and opens its own SSH connection. The local machine must have scp installed, and its SSH configuration, keys or agent, and known-hosts file must allow the connection. Use an argument list with shell=False (the default) so paths and options are passed as separate arguments.
Set the environment variables below to transfer a local file to the remote destination. For a download, reverse the source and destination arguments so the remote path comes first. The script catches a missing local executable and reports a nonzero exit status through its return value; it does not claim a successful transfer without a configured server and working authentication. This example is likewise unexecuted against a remote host.
import os
import subprocess
def copy_file():
host = os.environ.get("SCP_HOST")
if not host:
return 0
user = os.environ.get("SCP_USER", "your_user")
local_path = os.environ.get("SCP_LOCAL_FILE", "report.txt")
remote_path = os.environ.get("SCP_REMOTE_FILE", f"/home/{user}/report.txt")
command = ["scp", local_path, f"{user}@{host}:{remote_path}"]
try:
completed = subprocess.run(command, check=False)
except FileNotFoundError:
return 127
return completed.returncode
if __name__ == "__main__":
copy_file()
A return code of zero from the child process indicates that scp reported success; a nonzero value indicates that the transfer command failed. In an application, inspect or propagate that return code and handle FileNotFoundError so a missing executable is distinguishable from an SSH or transfer failure. Avoid shell=True for values that may contain user input. For remote commands, use an SSH library or a separate, deliberately constructed SSH invocation; subprocess.run() itself only launches the local process. Choose SCPClient for transfers integrated with Python code, or the scp executable when you prefer the system OpenSSH client and its configuration.