Python SCP: Securely Copy Files Over SSH

Quick answer: Python SCP workflows copy files over an SSH transport. Use a maintained SSH library, keep host-key verification enabled, select the intended remote path explicitly, set timeouts, verify the transferred file, and close the client in cleanup code.

Python Pool infographic showing Python SCP local file SSH connection remote path and verification
A dependable SCP transfer opens a verified SSH connection, copies a known path, checks the result, and closes the session explicitly.

Python can transfer files over SCP by using an SSH connection and the third-party scp package. The usual stack is Paramiko for SSH plus SCPClient for file upload and download operations.

Primary references include scp on PyPI, the Paramiko SSHClient documentation, Paramiko SFTP documentation, and the OpenBSD scp manual.

Install the packages with python -m pip install paramiko scp. Keep credentials out of source code. Prefer SSH keys, environment configuration, or a secrets manager over hard-coded passwords.

Host-key handling is a security boundary. Avoid blindly adding unknown host keys in reusable examples. Load known hosts from the system or application configuration, then reject unknown hosts unless you have a deliberate enrollment step.

SCP is useful for straightforward file copies. If you need directory listings, remote file metadata, or more file-management operations, Paramiko SFTP may be a better fit than SCP.

The examples below show the structure of the code. Replace hostnames, usernames, paths, and key paths with values from your environment before running them.

Use least-privilege accounts for automated transfers. A deployment account that can write only to a staging directory is safer than a broad shell account that can modify unrelated files.

Also plan failure behavior. Network transfers can fail midway, remote paths can be missing, and permissions can change. Wrap production transfers with logging, exception handling, and cleanup for partially written files where that matters.

Upload A File With SCP

Create an SSH client, load trusted host keys, connect, then pass the transport to SCPClient.

import paramiko
from scp import SCPClient

host = "example.com"
username = "deploy"
key_path = "/home/me/.ssh/id_ed25519"

ssh = paramiko.SSHClient()
ssh.load_system_host_keys()
ssh.set_missing_host_key_policy(paramiko.RejectPolicy())
ssh.connect(hostname=host, username=username, key_filename=key_path)

with SCPClient(ssh.get_transport()) as scp:
    scp.put("report.csv", "/var/tmp/report.csv")

ssh.close()

RejectPolicy() prevents silent trust of unknown hosts.

The with block closes the SCP client after the transfer.

Close the SSH connection when the transfer work is finished.

In a larger script, use a try and finally block or a helper function so the SSH client closes even when a transfer raises an exception.

Download A File

Use get() to copy from the remote server to the local machine.

import paramiko
from scp import SCPClient

ssh = paramiko.SSHClient()
ssh.load_system_host_keys()
ssh.set_missing_host_key_policy(paramiko.RejectPolicy())
ssh.connect("example.com", username="deploy", key_filename="/home/me/.ssh/id_ed25519")

with SCPClient(ssh.get_transport()) as scp:
    scp.get("/var/log/app.log", "app.log")

ssh.close()

The first path is remote and the second path is local.

Make sure the local directory exists before downloading into it.

For repeated transfers, keep connection setup in one helper and handle errors around the transfer call.

Downloading into a temporary path and renaming after success can prevent later code from reading a partially downloaded file.

Python Pool infographic showing a local file, SCP over SSH, remote host, and destination
SCP copies files through an authenticated SSH connection.

Upload A Directory Recursively

Set recursive=True when uploading a directory tree.

import paramiko
from scp import SCPClient

ssh = paramiko.SSHClient()
ssh.load_system_host_keys()
ssh.set_missing_host_key_policy(paramiko.RejectPolicy())
ssh.connect("example.com", username="deploy", key_filename="/home/me/.ssh/id_ed25519")

with SCPClient(ssh.get_transport()) as scp:
    scp.put("build", "/var/www/build", recursive=True)

ssh.close()

Recursive uploads can overwrite files on the remote side depending on the destination path.

Review the target directory before copying generated assets or deployment output.

For complex sync behavior, a tool such as rsync may be more appropriate than SCP.

Recursive copies can also transfer files you did not intend if the source directory contains caches, logs, or generated artifacts. Build the directory intentionally before sending it.

Show Transfer Progress

SCPClient accepts a progress callback.

import paramiko
from scp import SCPClient

def show_progress(filename, size, sent):
    percent = sent / size * 100 if size else 100
    print(f"{filename}: {percent:.1f}%")

ssh = paramiko.SSHClient()
ssh.load_system_host_keys()
ssh.set_missing_host_key_policy(paramiko.RejectPolicy())
ssh.connect("example.com", username="deploy", key_filename="/home/me/.ssh/id_ed25519")

with SCPClient(ssh.get_transport(), progress=show_progress) as scp:
    scp.put("archive.tar.gz", "/var/tmp/archive.tar.gz")

ssh.close()

The callback receives the filename, total size, and bytes transferred.

For polished terminal progress bars, adapt the callback to update a library such as tqdm.

Keep progress logging lightweight so it does not slow down large transfers.

Progress callbacks are useful for user feedback and logs, but they should not be the only success signal. Check that the transfer call completes and, when necessary, verify the remote size or checksum afterward.

Python Pool infographic mapping Python, Paramiko, SSH client, SFTP transport, and file
Paramiko provides Python SSH and SFTP primitives for programmatic transfers.

Upload File-Like Objects

putfo() uploads a file-like object.

from io import BytesIO
import paramiko
from scp import SCPClient

payload = BytesIO(b"generated report\n")

ssh = paramiko.SSHClient()
ssh.load_system_host_keys()
ssh.set_missing_host_key_policy(paramiko.RejectPolicy())
ssh.connect("example.com", username="deploy", key_filename="/home/me/.ssh/id_ed25519")

with SCPClient(ssh.get_transport()) as scp:
    scp.putfo(payload, "/var/tmp/report.txt")

ssh.close()

This is useful when content is generated in memory.

For large generated files, writing to a temporary file may be easier to monitor and retry.

Always validate the remote path before sending generated content.

Reset the file-like object’s position before upload if it has already been read. A BytesIO object should normally be at position zero when passed to putfo().

Use SFTP When You Need File Management

SFTP is often better when you need listing, stat calls, or explicit remote file operations.

import paramiko

ssh = paramiko.SSHClient()
ssh.load_system_host_keys()
ssh.set_missing_host_key_policy(paramiko.RejectPolicy())
ssh.connect("example.com", username="deploy", key_filename="/home/me/.ssh/id_ed25519")

with ssh.open_sftp() as sftp:
    sftp.put("settings.json", "/var/tmp/settings.json")
    info = sftp.stat("/var/tmp/settings.json")
    print(info.st_size)

ssh.close()

SFTP runs over SSH but exposes a richer file API than SCP.

Use SCP for simple copies and SFTP for remote file inspection or management.

SFTP also makes it easier to create remote directories, check file sizes, and remove temporary files as part of the same connection.

In short, use Python SCP through a trusted SSH connection, reject unknown host keys by default, keep secrets out of source code, and switch to SFTP when the task is more than a simple upload or download.

Python Pool infographic comparing host key, user key, authentication, and trusted session
Verify host identity and handle credentials without disabling SSH verification.

Create A Verified SSH Client

Keep connection setup separate from the file transfer so authentication, host verification, and timeout policy are visible. Prefer keys or a secret manager over passwords embedded in source.

import paramiko

client = paramiko.SSHClient()
client.load_system_host_keys()
client.set_missing_host_key_policy(paramiko.RejectPolicy())
client.connect("server.example", username="deploy", timeout=10)
print(client.get_transport().is_active())
client.close()

Copy A Known File

The SCP helper should receive explicit local and remote paths. Do not build a remote path from untrusted input without validating the allowed directory.

from pathlib import Path

local_path = Path("release.tar.gz")
remote_path = "/srv/releases/release.tar.gz"
if not local_path.is_file():
    raise FileNotFoundError(local_path)
print(local_path, remote_path)
Python Pool infographic testing permissions, paths, integrity, timeouts, and validation
Check host keys, permissions, destination paths, integrity, timeouts, and secret handling.

Verify The Transfer

A completed SSH write does not automatically prove that the expected artifact arrived. Check the remote file size or checksum through the same verified connection.

import hashlib
from pathlib import Path

path = Path("release.tar.gz")
checksum = hashlib.sha256(path.read_bytes()).hexdigest()
print(checksum)

Close In Finally

Long-running transfer jobs need predictable cleanup on both success and failure. Keep the client and SCP helper in a context manager or a try/finally block.

client = None
try:
    client = object()
    print("transfer started")
finally:
    client = None
    print("cleanup complete")

Paramiko’s official documentation covers SSH client behavior and host keys. Related references include local file operations, safe persistence formats, and bytes and text.

For related file workflows, compare local file operations, persistence formats, and bytes and text when transferring data.

Frequently Asked Questions

What is SCP in Python?

SCP is a file-copy protocol that transfers data between local and remote systems over an SSH connection.

Which Python package can copy files with SCP?

Common workflows use an SSH library such as Paramiko together with an SCP client helper, or a maintained transfer library that supports SSH.

Should I disable SSH host-key checking?

No. Keep host-key verification enabled and manage known hosts deliberately so a connection is not silently redirected.

How do I handle failed transfers?

Use timeouts, catch the narrowest expected exceptions, verify the remote file, and close the transport in a finally block.

Subscribe
Notify of
guest
0 Comments
Oldest
Newest Most Voted