Execute the same command on multiple machines in parallel via SSH.
# Make it executable and symlink to your PATH
chmod +x pexec
sudo ln -s $(pwd)/pexec /usr/local/bin/pexec# Single command
pexec -H web1,web2,db1 "uptime"
# Multiple -H flags
pexec -H web1,web2 -H db1,db2 "df -h"
# With user
pexec -H root@web1,deploy@web2 "systemctl status nginx"
# From a hosts file (one host per line)
pexec -f hosts.txt "uname -r"All -c commands run in a single shell session, so sourced
environment variables, aliases, and working directory persist across commands:
# Source an env file, then use its variables
pexec -H web1,web2 -c "source /opt/app/env.sh" -c 'echo $APP_HOME' -c "run_app"
# Login shell (auto-sources ~/.bashrc, ~/.profile)
pexec -H web1,web2 --login -c "conda activate myenv" -c "python train.py"
# Execute a local script on remote hosts
pexec -H web1,web2 -s deploy.sh
# Setup env first, then run a script
pexec -H web1,web2 -c "source /opt/env.sh" -s deploy.sh
# Pipe commands from stdin
echo "uptime && df -h" | pexec -H web1,web2Define hosts, commands, and settings in a YAML file instead of CLI flags:
pexec --config deploy.yaml# deploy.yaml
hosts:
- web1
- web2
- root@db1
commands:
- source /opt/app/env.sh
- echo $APP_HOME
- systemctl restart myapp
parallel: 4
timeout: 60
login: true
stream: true
# identity: ~/.ssh/deploy_key
# port: 22
# user: deploy
# script: deploy.sh
# no_strict: false
# json: falseCLI flags override config values, so you can use a config as a base and tweak per-run:
# Use config but add an extra host and override parallelism
pexec --config deploy.yaml -H extra-host -p 2Default config location: ~/.pexec.yaml (loaded automatically if it exists).
# Stream output in real-time (for long-running commands)
pexec -H web1,web2 --stream -c "source env.sh" -c "make build"
# JSON output (for scripting)
pexec -H web1,web2 --json "hostname"
# Limit parallelism
pexec -H host1,host2,host3,host4 -p 2 "apt update"
# Custom SSH key and port
pexec -H web1 -i ~/.ssh/mykey --port 2222 "whoami"| Flag | Description |
|---|---|
--config |
YAML config file (default: ~/.pexec.yaml) |
-c, --cmd |
Command to run (repeatable; all run in one session) |
-s, --script |
Local script file to execute on remote hosts |
--login |
Use a login shell (sources ~/.profile, ~/.bashrc) |
--shell |
Custom shell for sessions (default: bash -s) |
-H, --hosts |
Comma-separated hosts (repeatable) |
-f, --hosts-file |
File with hosts, one per line |
-u, --user |
SSH user for all hosts |
-p, --parallel |
Max parallel connections (0=unlimited) |
-t, --timeout |
SSH connect timeout in seconds (default: 30) |
--stream |
Stream output in real-time |
--json |
Output as JSON |
--no-strict |
Disable strict host key checking |
-i, --identity |
SSH private key path |
--port |
SSH port |
When you use -c multiple times, pexec joins all commands with newlines and
pipes them into bash -s (or bash -ls with --login) over a single SSH
connection. This means:
- Environment variables set or sourced in earlier
-ccommands are available in later ones cdin one command affects subsequent commands- It behaves like typing commands into an interactive shell
If no hosts are specified, pexec looks for ~/.pexec_hosts.