ssherd
Run commands on many servers at once — one Python file, zero dependencies, powered by your OpenSSH client.
ssherd is a small batch-SSH CLI for server fleets. Your ~/.ssh/config (or a project-local .ssh/config) is the inventory: ssherd reads the Host aliases, fans out commands in parallel, and lets the real ssh client handle everything it already does best — addresses, jump hosts (ProxyJump), users, keys, host keys.
Works on Linux, macOS, and Windows (Git-Bash / PowerShell).
Features
- Single file, zero dependencies — copy
ssherd.pyanywhere, needs only Python 3.9+ andssh - SSH config as inventory — selectors by name, comma list, or range (
web-01..web-16,all) - Parallel execution with a live per-device progress bar
- Smart grouped output — identical results collapse into one block, outliers shown as compact diffs (
--diff) - Jump hosts / keys / users — all resolved natively by OpenSSH, never re-implemented
- Password auth support via OpenSSH's official
SSH_ASKPASS_REQUIRE=force(no sshpass, never touches your tty) - Sudo fan-out —
sudo -Swrapping with one local password prompt add-user— generates plain, auditable bash commands by default (--execto run them)
Installation
pipx install ssherd # isolated venv + global command (recommended)
pip install ssherd # or plain pip — works on any OS
Requirements: Python 3.9+ and the OpenSSH client (ssh) you already have.
Prefer no installer at all? It's one self-contained file — grab it, read it, hack it:
curl -fsSL https://raw.githubusercontent.com/trdthg/ssherd/master/ssherd.py \
-o ~/.local/bin/ssherd && chmod +x ~/.local/bin/ssherd
Uninstall: pipx uninstall ssherd or pip uninstall ssherd.
Quick start
# the inventory is just your ssh config Host aliases
python -m ssherd list
# run in parallel across a range
python -m ssherd run web-01..web-16 -- whoami
# comma lists and single hosts work too
python -m ssherd run web-01,web-05,db-01 -- hostname
# sudo: prompts once, wraps remotely with sudo -S
python -m ssherd run --sudo db-01..db-32 -- apt update
# scriptable: pass passwords inline (note: lands in shell history)
python -m ssherd run --sudo=SUDOPASS db-01 -- systemctl restart nginx
# group identical outputs, show only the differences
python -m ssherd run all -- 'cat /etc/os-release' --diff
Inventory
Devices are the Host entries in your SSH config (Host * wildcards are skipped):
Host web-01
HostName 192.0.2.10
User deploy
# ProxyJump jumpbox ← jump hosts just work, ssh handles them
A .ssh/config in the current directory takes precedence over ~/.ssh/config,
so a fleet per project is one folder away. Put .ssh/ in .gitignore and real
IPs never get committed. Selectors accept aliases, comma lists, ranges
(web-01..web-16), and all / *.
Creating users
add-user prints the plain bash commands (useradd / usermod / chpasswd) for you
to audit and paste, or to wrap in your own loop. Add --exec to fan them out
through the same parallel channel:
python -m ssherd add-user web-01..web-16 deploy
python -m ssherd add-user web-01..web-16 deploy --sudo --password 'TempPass-123' --exec
Why not Ansible / pssh / fabric?
They are great; this is for the other 90% of ops work: one-off fleet commands
where you refuse to install anything, write a playbook, or learn a DSL.
ssherd delegates all connection semantics to OpenSSH, so anything your
ssh can reach, ssherd can reach — including bastion hosts.
Tests
python -m unittest discover -v
License
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ssherd-0.1.1.tar.gz.
File metadata
- Download URL: ssherd-0.1.1.tar.gz
- Upload date:
- Size: 12.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a3a1b822ad3933e5500bcbe718c902261a46dd7519d09cab03a7239f392702b4
|
|
| MD5 |
8aa5c28bbe0b60367cb1a9c3a1d081f7
|
|
| BLAKE2b-256 |
ca171659a282edf77fcb213fd7a6f55560ea07840e0672bdf63c987018409318
|
Provenance
The following attestation bundles were made for ssherd-0.1.1.tar.gz:
Publisher:
publish.yml on trdthg/ssherd
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ssherd-0.1.1.tar.gz -
Subject digest:
a3a1b822ad3933e5500bcbe718c902261a46dd7519d09cab03a7239f392702b4 - Sigstore transparency entry: 2706890716
- Sigstore integration time:
-
Permalink:
trdthg/ssherd@902efdf826f41af1bd6037c78d778c2839fe9407 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/trdthg
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@902efdf826f41af1bd6037c78d778c2839fe9407 -
Trigger Event:
push
-
Statement type:
File details
Details for the file ssherd-0.1.1-py3-none-any.whl.
File metadata
- Download URL: ssherd-0.1.1-py3-none-any.whl
- Upload date:
- Size: 12.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b3495643caa424b2a4ad6262b9e9152b0613312ad5de5044029fcc3436c9f17d
|
|
| MD5 |
01f78a2233318a3d7348caae64baa4ef
|
|
| BLAKE2b-256 |
2550a77b3417597ee02adac83de33619fbb28d28824c110f7a994c7b990b47b7
|
Provenance
The following attestation bundles were made for ssherd-0.1.1-py3-none-any.whl:
Publisher:
publish.yml on trdthg/ssherd
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ssherd-0.1.1-py3-none-any.whl -
Subject digest:
b3495643caa424b2a4ad6262b9e9152b0613312ad5de5044029fcc3436c9f17d - Sigstore transparency entry: 2706890750
- Sigstore integration time:
-
Permalink:
trdthg/ssherd@902efdf826f41af1bd6037c78d778c2839fe9407 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/trdthg
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@902efdf826f41af1bd6037c78d778c2839fe9407 -
Trigger Event:
push
-
Statement type: