| Tested on | RHEL 10.2 with openssh-clients 9.9p1-25.el10_2.x86_64, sshpass 1.09-9.el10_0 from AppStream, expect 5.45.4 |
|---|---|
| Package | sshpass, expect, openssh-clients |
| Applies to | Linux and Unix hosts with OpenSSH client; examples use bash on RHEL and Ubuntu |
| Privilege | Normal user on the SSH client; password belongs to the remote account you automate |
| Scope | Non-interactive password authentication with SSH and SCP using sshpass, Expect, and OpenSSH SSH_ASKPASS. Explains why piping a password to ssh fails, password versus keyboard-interactive authentication, and when SSH keys are the better design. Does not cover PSSH inventory runs or full ssh-agent scripting. |
| Related guides | OpenSSH tutorial SSH through a jump host |
OpenSSH does not provide a --password option, and echo "password" | ssh ... does not satisfy the login prompt because the ssh command reads authentication input from a terminal rather than ordinary stdin.
Pick a tool by how interactive the session is:
- one password prompt —
sshpassis the simplest helper - several or changing prompts — Expect
- repeated production automation — SSH public-key authentication avoids storing account passwords in scripts
Quick answer
The shortest sshpass example people search for is:
sshpass -p 'secret' ssh user@server.example.comThat works in a one-off lab, but sshpass -p is the least safe password-source option:
- places the password on the command line at process startup
- can end up in shell history, scripts, and audit logs
- should stay in demos, not persistent automation
For a script, prefer a protected runtime secret with -d, -f, or -e rather than embedding the password after -p:
export SSHPASS='secret'
sshpass -e ssh user@server.example.com
unset SSHPASS| Method | Best use |
|---|---|
sshpass |
Simple password-based SSH/SCP automation |
expect |
Multiple or varying interactive prompts |
SSH_ASKPASS |
OpenSSH-native askpass helper when you already manage one |
SSH key + ssh-agent |
Preferred long-term automation |
Why echo password | ssh does not work
OpenSSH deliberately collects sensitive prompts, including the login password, through the controlling TTY instead of stdin. Piping text into ssh does not feed that prompt.
This pattern fails for password login:
echo "$PASSWORD" | ssh user@server.example.comThe lab capture below forces the password authentication path so the failure is obvious. Those PreferredAuthentications and PubkeyAuthentication options are not required for normal sshpass usage:
echo 'secret' | ssh -o PreferredAuthentications=password -o PubkeyAuthentication=no sshlab@127.0.0.1 hostnamePermission denied, please try again.
Permission denied, please try again.
sshlab@127.0.0.1: Permission denied (publickey,password).sshpass and Expect work because they watch the pseudo-terminal for the password prompt and send the secret there. They do not rely on ssh reading the password from a pipe.
┌──────────┐ ┌─────────────────────┐ ┌──────────────┐ ┌──────────┐
│ sshpass │ ───► │ watches PTY for │ ───► │ ssh prompts: │ ───► │ sshd │
│ wrapper │ │ Password: │ │ Password: │ │ checks │
└──────────┘ └─────────────────────┘ └──────────────┘ └──────────┘sshpass makes SSH behave as if a human typed the password at the terminal prompt. That mental model matters more than trying to push the secret through stdin.
The stdin distinction matters:
ssh— does not read login passwords from a pipesshpass— reads a password from stdin when you omit-p,-e,-f, and-d
printf '%s\n' "$SSH_PASSWORD" | sshpass ssh user@server.example.com hostnameFor scripts, -d is generally clearer when a process already has the secret available.
Method 1: Use sshpass
sshpass wraps ssh or scp, supplies the password when OpenSSH asks for it, and can return sshpass-specific exit codes when authentication or host-key checks fail.
Install sshpass
Package names match across distros, but the repository differs:
- Ubuntu / Debian — main archive (1.09 on current releases); install with the apt command:
sudo apt install sshpass - Fedora — standard repos; install with dnf:
sudo dnf install sshpass - RHEL / Rocky / AlmaLinux — often AppStream; if
dnf install sshpassreturnsNo match for argument, enable AppStream or use Ubuntu/Fedora for the lab
On the RHEL 10.2 host used for this article, sshpass 1.09-9.el10_0 came from AppStream:
dnf info sshpassName : sshpass
Version : 1.09
Release : 9.el10_0
Repository : rhel-10-for-x86_64-appstream-rpms
Summary : Non-interactive SSH authentication utilityConfirm the binary after install:
sshpass -Vsshpass 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
See the COPYING file for more information.
Using "assword" as the default password prompt indicator.Basic password login
For everyday use, export the password once and call sshpass -e:
export SSHPASS='secret'
sshpass -e ssh user@server.example.com hostname
unset SSHPASSsshpass forwards remaining arguments to ssh. You do not need extra -o flags unless you are deliberately forcing a specific authentication method.
The quick -p form is common in search results but should stay in demos:
sshpass -p 'secret' ssh user@server.example.comsshpass -p places the password on the command line at process startup and is the least safe password-source option. The upstream project warns about command-line exposure and race conditions. Some implementations try to obscure the argument in ps after startup, but the secret can still appear in shell history, scripts, and audit logs. Do not use -p for persistent automation.
Choose a password source: -d, -f, -e, -p
sshpass accepts the password from four built-in sources. A practical security-oriented order is:
| Rank | Option | When to use |
|---|---|---|
| 1 | -d file descriptor |
Often best when a runtime process feeds the secret without argv exposure |
| 2 | -f file |
Persistent secret on disk with tight permissions |
| 3 | -e environment |
Convenient, but visible to the same user and child processes |
| 4 | -p argument |
Lab or demo only |
A root-owned 0600 secret file can be preferable to an environment variable in some automation environments, so treat the table as guidance rather than a fixed rule.
Avoid putting the password on the command line: use a file descriptor
The sshpass manual recommends an anonymous pipe or file descriptor instead of -p. This keeps the password out of sshpass command arguments, but the secret still exists in:
- the shell variable from
read - process memory for the duration of the command
- the here-string behind file descriptor
3until you close it
read -rsp "SSH password: " SSH_PASSWORD
echo
exec 3<<<"$SSH_PASSWORD"
sshpass -d 3 ssh user@server.example.com hostname
exec 3<&-
unset SSH_PASSWORDFor unattended jobs, replace the interactive read with your approved secret source.
Read the password from a protected file
Restrict the password file before sshpass reads it:
chmod 600 ~/.ssh/ssh-password
sshpass -f ~/.ssh/ssh-password ssh user@server.example.com hostnameThe file becomes the secret. Restrict ownership and mode, and keep it out of backups you do not control.
Use the SSHPASS environment variable
After the first export SSHPASS='secret' example above, later sections assume SSHPASS is already set in the shell unless noted otherwise.
An environment variable is still a secret-bearing process resource. It is not a substitute for a vault or configuration-management secret store.
Run remote commands and SCP
Assuming SSHPASS is already set for this shell, run one remote command:
sshpass -e ssh user@server.example.com 'hostname'Run several commands in one remote shell session:
sshpass -e ssh user@server.example.com 'hostname; uptime; id'The same mechanism applies to scp because scp uses SSH authentication. Modern OpenSSH may prefer SFTP mode for scp, but password prompting still follows the SSH authentication path.
Assuming SSHPASS is already set:
- Upload —
sshpass -e scp file.txt user@server.example.com:/tmp/ - Download —
sshpass -e scp user@server.example.com:/var/log/app.log . - Rsync over SSH —
sshpass -e rsync -av -e ssh ./data/ user@server.example.com:/backup/(see rsync command)
Lab verification on 127.0.0.1 adds client options only to force the password path for a reproducible capture:
sshpass -e scp /tmp/local.txt sshlab@127.0.0.1:/tmp/remote.txt
sshpass -e ssh -o PreferredAuthentications=password -o PubkeyAuthentication=no sshlab@127.0.0.1 cat /tmp/remote.txtlab scp testDo not copy those PreferredAuthentications and PubkeyAuthentication=no flags into production unless you intentionally want to disable public-key fallback.
Keep host-key verification enabled
Password automation should not disable server identity checks by default.
Avoid making this the normal pattern:
-o StrictHostKeyChecking=no \
-o UserKnownHostsFile=/dev/nullThat accepts any host key on every connection and removes MITM protection.
Host-key handling depends on how long you keep the server:
- Persistent infrastructure — verify the server host key out of band and populate
~/.ssh/known_hostsbefore automation runs - Disposable lab hosts — trust-on-first-use may be acceptable:
sshpass -e ssh -o StrictHostKeyChecking=accept-new user@server.example.com hostnameaccept-new adds a previously unknown host key but still refuses a host whose key has changed since you stored it. It helps only with new hosts; a key rotation on an existing host still stops the connection.
Common sshpass exit codes and troubleshooting
Before you blame sshpass, confirm the host is reachable and port 22 accepts connections with the layered checks in test an SSH connection.
sshpass defines its own exit statuses for password and host-key problems. Failures from the wrapped ssh or scp command may surface as SSH's own status, commonly 255 for a generic client failure. The table below is a quick subset, not an exhaustive list.
| Exit | Typical meaning |
|---|---|
0 |
Command succeeded |
1 |
Invalid sshpass command-line argument |
3 |
sshpass runtime error |
5 |
Invalid password |
6 |
Host public key is unknown |
7 |
Host public key changed |
255 |
Propagated SSH failure (network, config, or rejected connection) |
Wrong password on the lab host:
sshpass -p 'wrong' ssh -o PreferredAuthentications=password -o PubkeyAuthentication=no sshlab@127.0.0.1 hostnamePermission denied, please try again.Check the exit status immediately:
echo $?5Status 5 means sshpass detected bad credentials.
Unknown host key with strict checking:
No ED25519 host key is known for 127.0.0.1 and you have requested strict checking.
Host key verification failed.Network failure looks different:
Connection timed out during banner exchange
Connection to 192.168.99.99 port 22 timed outA non-zero exit status only tells you that something failed. Read the code and stderr together to separate:
- authentication (
5fromsshpass, orPermission denied) - host-key verification (exit
6/7, orHost key verification failed) - networking (
Connection timed out,Connection refused, or SSH exit255)
Use verbose mode when a manual login works but automation stops:
sshpass -v -e ssh sshlab@127.0.0.1 hostnameSSHPASS: searching for password prompt using match "assword"
SSHPASS: read:
sshlab@127.0.0.1's password:
SSHPASS: detected prompt. Sending password.
SSHPASS: read:
vm1.lab.exampleCustom password prompts with sshpass -P
By default, sshpass searches for the substring assword so both Password: and password: match. When verbose mode shows a different prompt string, override the pattern with -P:
sshpass -e -P 'Enter passcode:' ssh user@server.example.comUse -P only when authentication still involves one predictable prompt but the server uses text different from the normal Password: prompt. If authentication requires multiple prompts, use Expect instead.
| Situation | Tool |
|---|---|
| One standard password prompt | sshpass |
| One custom prompt text | sshpass -P |
| Multiple prompts, OTP, or MFA | Expect |
Password versus keyboard-interactive authentication
OpenSSH can authenticate through either the password method or keyboard-interactive. They are separate SSH authentication methods, but sshpass can handle a simple password-style prompt from either mechanism because it watches the pseudo-terminal for a matching pattern.
The problem appears when keyboard-interactive authentication produces multiple or unusual prompts, such as:
Password:Verification code:OTP:Enter token:
In that situation, sshpass may not be enough because it normally waits for a single password-prompt pattern. Use sshpass -P when only the prompt text differs, or Expect when the session needs responses to several different prompts.
Check what the server advertises:
ssh -vvv user@server.example.comLook for a line such as one of these:
Authentications that can continue: publickey,passwordAuthentications that can continue: publickey,keyboard-interactive
PasswordAuthentication and KbdInteractiveAuthentication are configured independently in sshd. Either line can still work with sshpass when the session ends in one normal password-style prompt. See OpenSSH authentication methods in sshd_config for how those directives interact on the server.
On the server, inspect the effective settings with sudo:
sudo sshd -T | grep -E 'passwordauthentication|kbdinteractiveauthentication'passwordauthentication yes
kbdinteractiveauthentication yesWhen passwordauthentication is no, the client cannot use the password method, but keyboard-interactive may still present a single password prompt that sshpass can answer.
Method 2: Use Expect
Use Expect when the session has more than one interactive prompt, or when prompts vary between hosts. For a single SSH password prompt, sshpass (or sshpass -P for custom prompt text) is usually enough.
Install Expect from the same package managers as sshpass:
- RHEL / Fedora / Rocky / AlmaLinux —
sudo dnf install expect - Ubuntu / Debian —
sudo apt install expect
Handle multiple prompts
You only need four concepts for this example:
spawn— startsshexpect— wait for output from the remote sessionsend— type a response at the prompteof— the remote process has finished
The script below expects SSH_USER, SSH_HOST, and SSH_PASSWORD to be set in the environment before it starts. If any variable is missing, Tcl throws an error immediately.
#!/usr/bin/expect -f
set timeout 15
if {![info exists env(SSH_USER)] || ![info exists env(SSH_HOST)] || ![info exists env(SSH_PASSWORD)]} {
puts stderr "Set SSH_USER, SSH_HOST, and SSH_PASSWORD before running this script."
exit 3
}
set user $env(SSH_USER)
set host $env(SSH_HOST)
set password $env(SSH_PASSWORD)
spawn ssh "$user@$host" hostname
expect {
-re "(?i)password:" {
send -- "$password\r"
exp_continue
}
-re "Permission denied" {
exit 1
}
-re "Connection refused" {
exit 2
}
-re "Connection timed out" {
exit 2
}
-re "Host key verification failed" {
exit 4
}
timeout {
exit 2
}
eof
}Run it after exporting the connection details and reading the password once:
export SSH_USER=sshlab
export SSH_HOST=127.0.0.1
read -rsp "Password: " SSH_PASSWORD
export SSH_PASSWORD
./ssh-login.exp
unset SSH_PASSWORD SSH_USER SSH_HOSTspawn ssh sshlab@127.0.0.1 hostname
sshlab@127.0.0.1's password:
vm1.lab.exampleDo not pass the password as a third script argument. Command-line arguments are visible in ps output.
Handle authentication errors
The expect block above stops on explicit failure text instead of looping on repeated password prompts until eof:
Permission denied— bad credentials or account policyConnection refused/Connection timed out— network orsshdnot listeningHost key verification failed— strict host-key checking blocked the sessiontimeout— no prompt arrived within 15 seconds
That is still a teaching script, not production-grade error reporting, but it shows the control-flow model Expect expects: match prompts, send input, and exit on known failure strings.
Use Expect with SCP
Reuse the same password-prompt and error-handling block for scp:
spawn scp file.txt "$user@$host:/tmp/"Match the same -re "(?i)password:" branch and let exp_continue run until eof or an explicit failure pattern matches.
Method 3: Use OpenSSH SSH_ASKPASS
OpenSSH can call an external askpass program when it needs a password. This answers searches for automating SSH without sshpass or Expect, but it takes more setup than sshpass -e.
OpenSSH selects an askpass helper in two main ways:
- Classic — no controlling terminal and
DISPLAYis set - Modern —
SSH_ASKPASS_REQUIRE=forceselects the helper even when a terminal exists
┌─────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ ssh client │ ───► │ SSH_ASKPASS │ ───► │ helper prints │
│ needs pass │ │ helper program │ │ password line │
└─────────────┘ └──────────────────┘ └─────────────────┘Create a helper that reads the secret from the environment:
cat > ~/bin/ssh-askpass-helper <<'EOF'
#!/bin/sh
printf '%s\n' "$MY_SSH_PASSWORD"
EOF
chmod 700 ~/bin/ssh-askpass-helperForce askpass even when a TTY is present:
export MY_SSH_PASSWORD='secret'
SSH_ASKPASS=~/bin/ssh-askpass-helper SSH_ASKPASS_REQUIRE=force \
ssh user@server.example.com hostname
unset MY_SSH_PASSWORDOn OpenSSH 9.9 in this lab, SSH_ASKPASS_REQUIRE=force invoked the helper without setting DISPLAY. When askpass is not selected:
- try
DISPLAY=:0as a compatibility hint for older graphical helpers - confirm the helper path is executable and prints one password line on stdout
SSH_ASKPASS_REQUIRE also accepts prefer and never. Use force only when you understand that the helper program becomes the secret store.
Better long-term option: SSH keys
For scripts that run daily, public-key authentication is the better default. The automation process unlocks a key or uses an agent session instead of storing the remote account password.
Generate a modern key:
ssh-keygen -t ed25519Install the public key on the server:
ssh-copy-id user@server.example.comConnect without a password prompt when the key has no passphrase:
ssh user@server.example.comWhen the private key has a passphrase, load it into ssh-agent for the current session:
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_ed25519Pick the key model to match who unlocks it:
- Interactive or agent-backed runs — prefer a passphrase plus
ssh-agent - Unattended service automation — some environments use dedicated passphrase-less keys with tight filesystem permissions and server-side restrictions; treat those keys as high-value credentials
See SSH key authentication for authorized_keys layout, permissions, and troubleshooting, and generate SSH keys for algorithm choice.
Which method should you use?
| Situation | Tool |
|---|---|
| One standard password prompt | sshpass |
| One custom prompt text | sshpass -P |
| Multiple prompts, OTP, or interactive workflow | Expect |
| Existing askpass integration | SSH_ASKPASS |
| Recurring unattended automation | SSH keys |
Common mistakes
| Symptom | Likely cause | What to check |
|---|---|---|
Manual ssh works, sshpass fails |
Multiple or unusual prompts; custom prompt text; or host-key stall | sshpass -v; try -P for one custom prompt; Expect for MFA or multi-step flows |
sshpass exits 5 |
Wrong password | Credentials; account lockout |
Host-key error or exit 6/7 |
Unknown or changed server key | known_hosts; use accept-new only on disposable labs |
passwordauthentication no on server |
Password method disabled in sshd |
Server policy; use keys instead |
| Automation hangs at a prompt | Host-key confirmation or non-password prompt | sshpass -v; preload known_hosts |
Summary
OpenSSH will not take a login password from a pipe because it reads sensitive prompts through the controlling terminal. That design choice explains most scripting failures that start with echo "$PASSWORD" | ssh.
When password automation is unavoidable, sshpass is the practical default for one SSH or SCP prompt. Prefer -d, -f, or -e over -p, keep host-key verification enabled, and remember that accept-new helps only with brand-new hosts. If manual login works but sshpass does not, check for multiple prompts or custom prompt text before you blame the password itself.
Expect belongs in the toolbox when prompts multiply or change shape. SSH_ASKPASS is legitimate OpenSSH plumbing, but the helper still holds the secret. For scheduled or large-scale automation, SSH keys with ssh-agent, or a proper secret-management workflow, remain the better long-term design.
References
- sshpass(1) — Debian manpages — password sources, exit codes, and security notes
- ssh(1) —
SSH_ASKPASS,SSH_ASKPASS_REQUIRE, and client options - Expect — Tcl Developer Xchange —
spawn,expect, andsend
Frequently Asked Questions
Can I pass a password directly to the ssh command?
OpenSSH has no normal --password flag. Use an interactive terminal, sshpass, Expect, an askpass helper, or key-based authentication.
Why doesn't piping a password to ssh work?
SSH reads sensitive authentication prompts through the controlling TTY, not through ordinary standard input. sshpass can read a password from its own stdin when no -p, -e, -f, or -d option is supplied.
Is sshpass safe?
It automates password authentication, which means some process must hold the password. Avoid -p for real automation and protect whichever password source you choose.
Can sshpass automate SCP?
Yes. The same sshpass -e, -f, or -d options work when sshpass wraps scp instead of ssh.
Which is better, sshpass or Expect?
Use sshpass when the only interaction is an SSH password prompt. Use Expect when you must answer several changing prompts in one session.
Should I use a password or SSH key for scripts?
For repeated or production server automation, SSH keys with a passphrase and ssh-agent generally avoid embedding account passwords in scripts.

