Every sysadmin hits the same wall: you’ve just provisioned 50 VMs, none of them have your SSH keys yet, and you need to run commands on all of them right now. Typing passwords manually into each session isn’t going to cut it. That’s where sshpass comes in. It feeds passwords to SSH non-interactively, bridging the gap between “I just created these machines” and “I’ve deployed my keys everywhere.”
To be clear, sshpass is not a replacement for SSH key-based authentication. Keys are always the right choice for production access, automation pipelines, and anything long-lived. But for bootstrapping new servers, one-off bulk operations on legacy systems, or deploying SSH keys themselves, sshpass is the pragmatic tool that gets the job done. This guide covers installation on Linux and macOS, all three password-passing methods, real-world use cases with SCP, rsync, and Ansible, plus the security trade-offs you need to understand before using it.
Tested April 2026 on Rocky Linux 9.5 (sshpass 1.09), Fedora 42, and macOS 26.3 (sshpass 1.06 via Homebrew)
When to Use sshpass (and When Not To)
sshpass exists because SSH was deliberately designed to prevent non-interactive password entry. SSH reads passwords directly from the terminal, not from stdin, which blocks scripting. sshpass works around this by using a pseudo-terminal to feed the password when SSH asks for it.
That said, using it carelessly creates real security risks. Passwords passed on the command line show up in ps output and shell history. The tool is best treated as a temporary bridge, not a permanent solution.
| Scenario | Use sshpass? | Better alternative |
|---|---|---|
| Bootstrapping new VMs (deploying SSH keys) | Yes | Cloud-init, Terraform provisioner |
| CI/CD pipeline initial setup | Yes (once) | SSH keys after first run |
| Legacy systems without key auth | Acceptable | Enable key-based auth if possible |
| Daily SSH access | No | SSH keys + ssh-agent |
| Production scripts running on cron | No | SSH keys with passphrase + ssh-agent |
| Interactive admin sessions | No | SSH keys, jump hosts, bastion |
Install sshpass
sshpass is available in the default repositories on most Linux distributions and through Homebrew on macOS.
Rocky Linux / RHEL / AlmaLinux / Fedora
Install from the base repos with dnf:
sudo dnf install -y sshpass
On Rocky Linux 9.5, this installs sshpass 1.09:
Installed:
sshpass-1.09-4.el9.x86_64
Ubuntu / Debian
On Debian-based systems, use apt:
sudo apt update && sudo apt install -y sshpass
macOS
Homebrew is the simplest route on macOS. The formula installs sshpass 1.06:
brew install sshpass
Arch Linux
Available in the community repository:
sudo pacman -S sshpass
Verify the Installation
Confirm sshpass is installed and check the version:
sshpass -V
The output shows the version and license information:
sshpass 1.09
(C) 2006-2011 Lingnu Open Source Consulting Ltd.
(C) 2015-2016, 2021 Shachar Shemesh
This program is free software, and can be distributed under the terms of the GPL
You can also view all available flags:
sshpass -h
This prints the usage summary with all password-passing options:
Usage: sshpass [-f|-d|-p|-e] [-hV] command parameters
-f filename Take password to use from file
-d number Use number as file descriptor for getting password
-p password Provide password as argument (security unwise)
-e Password is passed as env-var "SSHPASS"
With no parameters - password will be taken from stdin
-P prompt Which string should sshpass search for to detect a password prompt
-v Be verbose about what you're doing
-h Show help (this screen)
-V Print version information
At most one of -f, -d, -p or -e should be used
Enable Password Authentication on the Target Server
Before sshpass can work, the target server must accept password-based SSH logins. Many cloud images and hardened servers disable this by default. Check the current setting:
sudo grep -i "^PasswordAuthentication" /etc/ssh/sshd_config /etc/ssh/sshd_config.d/*.conf 2>/dev/null
If you see PasswordAuthentication no, open the SSH config and change it:
sudo vi /etc/ssh/sshd_config
Find the PasswordAuthentication line and set it to yes:
PasswordAuthentication yes
On RHEL/Rocky, also check drop-in configs in /etc/ssh/sshd_config.d/ because they override the main file. After making the change, restart the SSH daemon:
sudo systemctl restart sshd
On Ubuntu/Debian, the service name is ssh instead:
sudo systemctl restart ssh
Method 1: Password on Command Line (-p)
The -p flag is the simplest way to pass a password. It’s also the least secure because the password appears in your shell history and is visible to anyone running ps aux on the same machine. Use it only for quick one-off tests, never in scripts or production.
sshpass -p 'Str0ngP@ss!' ssh -o StrictHostKeyChecking=no [email protected] "hostname && uname -r"
The server returns its hostname and kernel version, confirming the login worked:
fedora42-test
6.14.2-300.fc42.x86_64
The -o StrictHostKeyChecking=no flag tells SSH to accept the remote host key automatically. Without it, SSH prompts for confirmation on first connection, which defeats the purpose of non-interactive login.
For troubleshooting, add the -v flag to see what sshpass is doing behind the scenes:
sshpass -v -p 'Str0ngP@ss!' ssh -o StrictHostKeyChecking=no [email protected] "whoami"
Verbose mode shows when sshpass detects the password prompt and sends the password, which helps diagnose connection failures.
Method 2: Password from Environment Variable (-e)
The -e flag reads the password from the SSHPASS environment variable. This is safer than -p because environment variables don’t appear in ps output (on most systems). The password still exists in the process environment, so it’s not bulletproof, but it eliminates the most obvious exposure vector.
export SSHPASS='Str0ngP@ss!'
sshpass -e ssh -o StrictHostKeyChecking=no [email protected] "echo 'Logged in via SSHPASS env var'"
The remote command executes successfully:
Logged in via SSHPASS env var
You can also set the variable inline for a single command without exporting it to the full session:
SSHPASS='Str0ngP@ss!' sshpass -e ssh -o StrictHostKeyChecking=no [email protected] "uptime"
The inline approach keeps the variable scoped to that single command, which is cleaner for scripting.
Method 3: Password from File (-f)
Of the three sshpass methods, -f is the most secure. The password lives in a file with restricted permissions, stays out of process listings, and doesn’t linger in shell history. This is the recommended approach for any scripted use of sshpass.
Create the password file and lock down its permissions:
echo 'Str0ngP@ss!' > ~/.ssh/pass_file
chmod 600 ~/.ssh/pass_file
Now use the file with -f:
sshpass -f ~/.ssh/pass_file ssh -o StrictHostKeyChecking=no [email protected] "echo 'Logged in via password file'"
Confirmed working:
Logged in via password file
The file should contain only the password on a single line, with no trailing newline or extra whitespace. The chmod 600 ensures only your user can read it.
Copy SSH Keys to Multiple Servers
This is the most common real-world use case for sshpass: you have a list of freshly provisioned servers, all sharing the same initial password, and you need to deploy your SSH public key to each one so you never need that password again.
First, create a file listing your servers:
cat > ~/servers.txt << 'LIST'
10.0.1.50
10.0.1.51
10.0.1.52
10.0.1.53
10.0.1.54
LIST
Generate an SSH key pair if you don't have one:
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519 -N ""
Now loop through the servers and push your public key to each one:
while read -r server; do
echo "Deploying key to $server..."
sshpass -f ~/.ssh/pass_file ssh-copy-id -o StrictHostKeyChecking=no admin@"$server"
done < ~/servers.txt
For each server, ssh-copy-id confirms the key was installed:
Deploying key to 10.0.1.50...
Number of key(s) added: 1
Now try logging into the machine, with: "ssh '[email protected]'"
and check to make sure that only the key(s) you wanted were added.
Verify that key-based authentication works by connecting without sshpass:
ssh [email protected] "echo 'Key auth works, no password needed'"
If this returns the echo string without prompting for a password, your key is deployed correctly:
Key auth works, no password needed
At this point, sshpass has served its purpose. All future connections use key-based auth. You can (and should) disable password authentication on these servers once keys are confirmed working.
Use sshpass with SCP
sshpass works with any command that invokes SSH under the hood, including scp. To copy a file to a remote server:
sshpass -f ~/.ssh/pass_file scp -o StrictHostKeyChecking=no /tmp/backup.tar.gz [email protected]:/backup/
Copy a file from the remote server back to your local machine:
sshpass -f ~/.ssh/pass_file scp -o StrictHostKeyChecking=no [email protected]:/var/log/syslog /tmp/remote-syslog.log
For recursive directory copies, add the -r flag:
sshpass -f ~/.ssh/pass_file scp -r -o StrictHostKeyChecking=no [email protected]:/etc/nginx/ /tmp/nginx-backup/
Use sshpass with rsync
For syncing directories with delta transfers, combine sshpass with rsync using the --rsh flag. This tells rsync to use sshpass-wrapped SSH as its remote shell:
SSHPASS='Str0ngP@ss!' rsync -avz --rsh="sshpass -e ssh -o StrictHostKeyChecking=no" /var/www/ [email protected]:/var/www/
rsync shows the transfer details as it runs:
sending incremental file list
./
index.html
css/
css/style.css
images/
images/logo.png
sent 45,832 bytes received 134 bytes 30,644.00 bytes/sec
total size is 44,291 speedup is 0.96
The file-based method works here too. Just swap -e for -f:
rsync -avz --rsh="sshpass -f $HOME/.ssh/pass_file ssh -o StrictHostKeyChecking=no" /var/www/ [email protected]:/var/www/
Use sshpass in Shell Scripts
Here's a practical script that provisions a batch of servers: updates packages, sets the timezone, and installs common tools. It uses the file-based method and includes basic error handling.
#!/bin/bash
# provision-servers.sh
# Bootstrap newly provisioned servers with base configuration
PASS_FILE="$HOME/.ssh/pass_file"
SERVER_LIST="$HOME/servers.txt"
SSH_OPTS="-o StrictHostKeyChecking=no -o ConnectTimeout=10"
if [[ ! -f "$PASS_FILE" ]]; then
echo "Error: Password file $PASS_FILE not found"
exit 1
fi
if [[ ! -f "$SERVER_LIST" ]]; then
echo "Error: Server list $SERVER_LIST not found"
exit 1
fi
while read -r server; do
[[ -z "$server" || "$server" =~ ^# ]] && continue
echo "=== Provisioning $server ==="
sshpass -f "$PASS_FILE" ssh $SSH_OPTS admin@"$server" bash -s << 'REMOTE'
set -e
echo "Updating packages..."
sudo dnf update -y -q
echo "Setting timezone..."
sudo timedatectl set-timezone UTC
echo "Installing base tools..."
sudo dnf install -y -q vim tmux htop curl wget git
echo "Server $(hostname) provisioned successfully"
REMOTE
if [[ $? -eq 0 ]]; then
echo "[OK] $server provisioned"
else
echo "[FAIL] $server failed (exit code: $?)"
fi
done < "$SERVER_LIST"
Make it executable and run it:
chmod +x provision-servers.sh
./provision-servers.sh
The sshpass exit codes are useful for error handling in scripts:
| Exit Code | Meaning |
|---|---|
| 0 | Success |
| 1 | Invalid arguments or conflicting options |
| 2 | Conflicting arguments |
| 3 | General runtime error |
| 4 | Invalid or incorrect password |
| 5 | Password was rejected by the host |
| 6 | Host public key is unknown (StrictHostKeyChecking) |
Use sshpass with Ansible
Ansible uses SSH for everything, and on first contact with new servers, you often don't have SSH keys deployed yet. sshpass fills this gap. Ansible detects sshpass automatically if it's installed and you provide a password.
The quickest way is the --ask-pass flag, which prompts for the SSH password once and uses it for all hosts:
ansible all -i inventory.ini -m ping --ask-pass
For fully automated runs (CI/CD, scheduled jobs), set the password in your inventory or group variables. Create a group_vars file:
sudo vi group_vars/new_servers.yml
Add the connection variables:
ansible_user: admin
ansible_ssh_pass: "Str0ngP@ss!"
ansible_ssh_common_args: "-o StrictHostKeyChecking=no"
A typical bootstrap playbook deploys SSH keys on the first run, then disables password auth:
---
- name: Bootstrap new servers
hosts: new_servers
tasks:
- name: Deploy SSH public key
ansible.posix.authorized_key:
user: admin
key: "{{ lookup('file', '~/.ssh/id_ed25519.pub') }}"
state: present
- name: Disable password authentication
lineinfile:
path: /etc/ssh/sshd_config
regexp: "^PasswordAuthentication"
line: "PasswordAuthentication no"
notify: restart sshd
handlers:
- name: restart sshd
service:
name: sshd
state: restarted
After this playbook runs, all subsequent Ansible runs use key-based auth. For sensitive environments, use Ansible Vault to encrypt the password variable instead of storing it in plain text.
Security Best Practices
sshpass trades security for convenience. That trade-off is acceptable when you understand the risks and limit exposure. Follow these practices:
- Never use
-pin scripts. The password appears in/proc/<pid>/cmdlineandps auxoutput. Anyone with shell access on the same machine can see it. Use-for-einstead. - Prefer
-fover-e. File-based passwords withchmod 600are the most controlled option. Environment variables can leak through/proc/<pid>/environon Linux, child processes, and crash dumps. - Treat sshpass as a bootstrapping tool only. Deploy SSH keys on first connection, then disable password authentication on the target servers. sshpass should never be part of your steady-state workflow.
- Restrict SSH access with firewall rules. Limit port 22 to known source IPs using
firewalldorufw. This reduces the blast radius if a password file is compromised. - Clean up after yourself. Delete password files (
rm ~/.ssh/pass_file), unset environment variables (unset SSHPASS), and clear shell history entries that contain passwords.
Troubleshooting
Error: "Permission denied, please try again"
This usually means password authentication is disabled on the target server, not that the password is wrong. Verify with verbose mode:
sshpass -v -p 'Str0ngP@ss!' ssh -o StrictHostKeyChecking=no [email protected] "whoami"
If sshpass says it sent the password but SSH still rejects it, check the target server's /etc/ssh/sshd_config for PasswordAuthentication no. Also check drop-in configs in /etc/ssh/sshd_config.d/ on RHEL-family systems, because those override the main config file.
Error: "Host key verification failed"
SSH refuses to connect because the remote host's key isn't in your known_hosts file. sshpass exits with code 6 in this case. The fix is to add -o StrictHostKeyChecking=no to your SSH command, or accept the key beforehand:
ssh-keyscan -H 10.0.1.50 >> ~/.ssh/known_hosts
The ssh-keyscan approach is more secure because it adds the key once without disabling verification for future connections.
sshpass Hangs Without Connecting
sshpass works by watching for the password prompt string. If the remote server uses a non-standard prompt (something other than "assword:"), sshpass never detects it and hangs indefinitely. Use the -P flag to specify the prompt string:
sshpass -P "Enter passphrase" -p 'Str0ngP@ss!' ssh [email protected]
Run with -v to see exactly what sshpass is looking for. If the connection hangs even with the correct prompt string, check whether the target server's SSH port is reachable (nc -zv 10.0.1.50 22) and that no firewall is blocking the connection.
Once you have deployed SSH keys to your servers, manage them with our guides on SSH key passphrases and SSH server configuration on Rocky Linux.
FAQ
Is sshpass secure?
No, not for production authentication. sshpass is a convenience tool that circumvents SSH's deliberate security design (refusing to accept passwords from non-terminal input). It's acceptable for bootstrapping, one-off operations, and deploying SSH keys to new servers. For anything persistent, switch to SSH key-based authentication.
Does sshpass work on macOS?
Yes. Install it via Homebrew with brew install sshpass. The macOS version (1.06 as of April 2026) works identically to the Linux version. All flags and methods (-p, -e, -f) behave the same way.
Can I use sshpass with Ansible?
Yes. Ansible uses sshpass automatically when you provide a password via --ask-pass or the ansible_ssh_pass inventory variable. sshpass must be installed on the Ansible control node (the machine running Ansible), not on the target hosts. This is the standard approach for bootstrapping new servers before SSH keys are deployed.