mrkeyoor.com_
Sun 20 Sept 04:55 UTC
PyPIInfraupdated 20 Sept 2026

paramiko review

Paramiko 5.0.0 implements SSHv2 clients, servers, channels, and SFTP in Python. `SSHClient` authenticates and runs remote processes, `SFTPClient` reads and changes remote files, and the lower-level `Transport` API opens forwarding channels or backs an SSH server written in Python. Paramiko's own README recommends Fabric for ordinary remote commands and transfers, leaving direct Paramiko use for code that needs protocol-level control. Version 5 can write OpenSSH private-key files and load encrypted keys through `PKey.from_type_string`; it also removes GSSAPI, SHA-1 key exchange, and SHA-1 RSA signatures. Our import worked, but took 0.99 seconds and the install occupied 22 MB.

Verdict

Paramiko 5.0.0 installed in 0.3 seconds but brought seven packages and 22 MB, while its import took 0.99 seconds in our sandbox; pay that cost for direct SSH channels, SFTP internals, or server code. Routine deployment commands belong one layer up in Fabric, and legacy SHA-1 or GSSAPI estates should not upgrade blindly.

We installed it

Lab card: what happened when we installed paramikoScreenshot of paramiko documentation
Install✓ · 0.3s7 packages on disk · 22 MB
Importimport paramiko in 0.99s · pure Python · requires Python >=3.9
Known vulns0(pip-audit)

Answers from our run

Does paramiko install cleanly?

Yes. In a fresh container with an empty cache, pip install paramiko finished in 0.3s, leaving 7 packages and 22 MB on disk. pip-audit reported no known vulnerabilities.

What does paramiko need to run?

Python >=3.9, and nothing compiled: it is pure Python. In our run import paramiko succeeded in 0.99s.

paramiko or fabric: which should you use?

fabric: Use it for routine remote commands, sudo, deployment tasks, connection settings, and file transfers built on Paramiko. Paramiko 5.0.0 installed in 0.3 seconds but brought seven packages and 22 MB, while its import took 0.99 seconds in our sandbox; pay that cost for direct SSH channels, SFTP internals, or server code.

When should you not use paramiko?

The work is mainly remote commands, uploads, and sudo. Paramiko's README recommends Fabric for those common client workflows.

API stability3/5Long-standing entry points such as `SSHClient`, `Transport`, `Channel`, host-key policies, and `SFTPClient` still organize the package. Major versions deliberately remove insecure or difficult compatibility paths: version 4 removed DSA, while version 5 removes GSSAPI, SHA-1 exchanges, and SHA-1 RSA signatures. Version 5 also changes an encrypted-key keyword on `PKey.from_path`, so both old Python code and old SSH servers can fail after an otherwise straightforward dependency bump.
Docs4/5The versioned site includes API references for high-level clients, transports, channels, key classes, SFTP, OpenSSH configuration parsing, and server callbacks. The changelog puts explicit warnings beside every incompatible version 5 removal and explains the security or maintenance reason. Practical material is less complete for bastion routing, host-key enrollment, output streaming, and deadlock avoidance, leaving readers to combine reference pages and issue knowledge into deployment recipes.
Maintenance4/5Paramiko 5.0.0 was uploaded on May 9, 2026, and GitHub records the repository's latest push on the same date. The project is unarchived and has 9,837 stars, though GitHub also counts 1,195 open issues and pull requests across clients, servers, operating systems, and unusual SSH peers. Releases are carefully documented, but that queue and the lack of GitHub Release entries make the changelog more useful than the repository's release tab.
Ecosystem5/5The supplied weekly snapshot shows 34,245,746 PyPI downloads. Fabric is explicitly built on Paramiko, and deployment plus network-automation projects have depended on its transports for years. The project uses OpenSSH as its reference behavior, which gives protocol troubleshooting a shared baseline. Compatibility with old appliances is narrowing in version 5, however, because SHA-1 and GSSAPI paths have been removed rather than kept behind unsafe defaults.

Use it if

  • A Python service must create SSH or SFTP sessions without shelling out to the system OpenSSH client.
  • You need forwarding channels, algorithm controls, custom transports, or protocol details that a task runner intentionally hides.
  • The application is an SSH server whose authentication and channel behavior are implemented in Python.
  • Remote-file logic needs SFTP metadata, permissions, streaming, rename operations, or an application-defined atomic upload sequence.
Skip it if

Setup reality

We installed Paramiko 5.0.0 in a fresh unprivileged Python 3.12 Bookworm sandbox. pip completed in 0.3 seconds and left seven packages using 22 MB. import paramiko succeeded in 0.99 seconds. The top-level package is pure Python, declares four direct dependencies, requires Python 3.9+, and has no py.typed marker. pip-audit found zero known vulnerabilities in that environment. Our package metadata did not identify a license.

The first production connection should fail on an unknown host key. Load a controlled known-hosts file and retain RejectPolicy until an enrollment process has verified the fingerprint. AutoAddPolicy silently trusts the first key it sees. Services should also set allow_agent and look_for_keys deliberately so a developer's agent or home-directory key cannot change which credential gets used.

Each exec_command call returns three streams on one SSH channel. Read stdout and stderr before calling recv_exit_status() when the command can fill the channel window; waiting first can deadlock. Stream long output incrementally. A pseudo-terminal changes buffering and terminal behavior, so ask for one only for software that truly expects a TTY. TCP connection, banner, authentication, and channel opening all have separate timeout settings.

Inventory servers before upgrading to version 5.0.0. GSSAPI, SHA-1 exchanges, and SHA-1 RSA signing have been removed, and group-exchange parameters are stricter. PKey.from_path() now accepts password= for an encrypted key instead of the earlier passphrase= name. Key writing can specify file_format=OPENSSH; choose the format explicitly because the changelog warns that a later major may stop defaulting to PEM.

Patterns

Run a command after checking the host key execute-verified-command

import paramiko

client = paramiko.SSHClient()
client.load_system_host_keys()
client.set_missing_host_key_policy(paramiko.RejectPolicy())
client.connect("host.example", username="deploy", timeout=10)
try:
    stdin, stdout, stderr = client.exec_command("uname -a")
    output = stdout.read().decode()
    error = stderr.read().decode()
    status = stdout.channel.recv_exit_status()
finally:
    client.close()

Known-host loading makes `RejectPolicy` useful. Drain both output streams before waiting for status when either stream may fill the channel window.

Open an encrypted private key in version 5 load-encrypted-key

from paramiko import PKey, SSHClient

key = PKey.from_path("/run/secrets/deploy_key", password=key_password)
client = SSHClient()
client.load_system_host_keys()
client.connect(
    "host.example", username="deploy", pkey=key,
    allow_agent=False, look_for_keys=False,
)

Paramiko 5 uses `password=` for `PKey.from_path()`. Disabling agent and key discovery prevents ambient developer credentials from changing authentication.

Verify against an application-owned host store use-private-known-hosts

import paramiko

client = paramiko.SSHClient()
client.load_host_keys("/etc/example/known_hosts")
client.set_missing_host_key_policy(paramiko.RejectPolicy())
client.connect("host.example", username="deploy")

Populate the file through a separate fingerprint-verification step. `AutoAddPolicy` accepts an unknown first key without that check.

Upload to a temporary name before publishing upload-atomic-sftp

sftp = client.open_sftp()
try:
    temporary = "/srv/releases/release.tar.gz.part"
    final = "/srv/releases/release.tar.gz"
    sftp.put("release.tar.gz", temporary, confirm=True)
    sftp.chmod(temporary, 0o640)
    sftp.rename(temporary, final)
finally:
    sftp.close()

`put()` does not create parent directories. Renaming only after a confirmed transfer keeps readers from opening a partial final file.

Fetch names and metadata together list-sftp-attributes

import stat

sftp = client.open_sftp()
try:
    for entry in sftp.listdir_attr("/srv/releases"):
        kind = "directory" if stat.S_ISDIR(entry.st_mode) else "file"
        print(kind, entry.st_size, entry.filename)
finally:
    sftp.close()

`listdir_attr()` returns stat-like fields with each name. Calling `stat()` afterward for every entry adds one network round trip per item.

Consume output from a long-running process stream-remote-output

stdin, stdout, stderr = client.exec_command("journalctl -fu example")
try:
    for line in iter(stdout.readline, ""):
        print(line.rstrip())
finally:
    stdout.channel.close()

Some remote programs buffer when no terminal is attached. `get_pty=True` can change that, but it also changes signal and stream behavior.

Connect to a target through a direct TCP channel tunnel-through-bastion

jump = paramiko.SSHClient()
jump.load_system_host_keys()
jump.connect("bastion.example", username="deploy")

channel = jump.get_transport().open_channel(
    "direct-tcpip", dest_addr=("10.0.1.20", 22),
    src_addr=("127.0.0.1", 0),
)
target = paramiko.SSHClient()
target.load_system_host_keys()
target.connect("10.0.1.20", username="deploy", sock=channel)

The target runs over the bastion transport. Close the target and its channel before shutting down the jump connection.

Resolve selected values from SSH config parse-openssh-alias

from pathlib import Path
import paramiko

config = paramiko.SSHConfig.from_path(Path.home() / ".ssh" / "config")
options = config.lookup("production")
client = paramiko.SSHClient()
client.load_system_host_keys()
client.connect(
    options["hostname"], port=int(options.get("port", 22)),
    username=options.get("user"), key_filename=options.get("identityfile"),
)

Parsing the file does not apply every OpenSSH option. Proxy commands and jump-host behavior still need explicit wiring.

Bound each stage of connection setup set-phase-timeouts

client.connect(
    "host.example", username="deploy",
    timeout=10, banner_timeout=15, auth_timeout=20, channel_timeout=30,
)
client.get_transport().set_keepalive(30)

TCP setup, banner exchange, authentication, and channel creation can stall separately. A keepalive helps discover idle sessions removed by intervening network devices.

Select OpenSSH private-key output explicitly write-openssh-key

from paramiko import PKey
from paramiko.pkey import OPENSSH

key = PKey.from_path("/secure/id_ed25519", password=old_password)
key.write_private_key_file(
    "/secure/id_ed25519.new", password=new_password, file_format=OPENSSH
)

Version 5 adds the format argument. The changelog says a later major may change the current PEM default, so explicit output is safer for stored keys.

Open SFTP on a live SSH transport reuse-transport-for-sftp

transport = client.get_transport()
if transport is None or not transport.is_active():
    raise RuntimeError("SSH transport is not active")

sftp = paramiko.SFTPClient.from_transport(transport)
try:
    with sftp.open("/var/log/example.log", "r") as remote:
        first_line = remote.readline()
finally:
    sftp.close()

Every SFTP client consumes another channel. Close it when finished if the process plans to keep the underlying SSH transport alive.

Remove an algorithm a server advertises incorrectly disable-broken-algorithm

client.connect(
    "host.example", username="deploy",
    disabled_algorithms={"pubkeys": ["rsa-sha2-512"]},
)

`disabled_algorithms` subtracts from the algorithms Paramiko 5 supports. It cannot bring back SHA-1 exchanges or signatures removed by this major version.

Alternatives

PackageRegistryPick it when
fabricPyPIUse it for routine remote commands, sudo, deployment tasks, connection settings, and file transfers built on Paramiko.
asyncsshPyPIUse it for asyncio clients, servers, forwarding, and high connection concurrency.
scpPyPIUse it when an existing Paramiko transport must transfer files with the SCP protocol.
ssh2-pythonPyPIUse it when libssh2 bindings fit the platform and lower native-library overhead or throughput is the priority.

More infra guides

boto3 · opentelemetry-api · psutil · distro · @opentelemetry/api · google-cloud-storage · the whole shelf →

How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.