Chapter 14Lesson 02~100 minutes

SSH for Non-Interactive Remote Commands

SSH turns a local Bash script into a remote process orchestrator. Production use requires non-interactive authentication, trusted host identity, bounded connection time, and explicit control over stdin, TTYs, environment, and exit status.

IntermediateNetworking & remote automationHands-on lab

Learning objectives

By the end of this lesson

  • Configure SSH for non-interactive execution.
  • Preserve host-key verification.
  • Control identity and connection timeout.
  • Manage stdin and TTY behavior.
  • Propagate remote execution status.

1. SSH is a remote process boundary

SSH command execution
flowchart LR
  L["local Bash"] --> S["ssh client"]
  S --> D["sshd"]
  D --> R["remote command"]
  R --> E["remote exit status"]
  E --> L

A non-interactive SSH invocation starts a command on another machine and returns the remote command's exit status through the SSH client, unless the connection itself fails.

2. Run one remote command explicitly

ssh ops@example-host 'uname -a'

Quote the remote command so the local shell does not expand its variables or metacharacters before SSH sends it.

3. Disable interactive prompts for automation

ssh \
  -o BatchMode=yes \
  -o ConnectTimeout=5 \
  ops@example-host \
  'systemctl is-active myapp'

BatchMode=yes prevents password/passphrase prompts from hanging unattended automation.

4. Host-key verification is part of the trust model

SSH host keys protect against connecting to an unexpected server. Do not disable host-key checking globally just to make automation easier.

ssh \
  -o BatchMode=yes \
  -o StrictHostKeyChecking=yes \
  -o UserKnownHostsFile="$known_hosts_file" \
  ops@example-host \
  'hostname'
Trust bootstrap

Populate known-hosts data through a trusted provisioning channel. Automatically accepting unknown keys changes the security model.

5. Select credentials explicitly

ssh \
  -i "$HOME/.ssh/deploy_ed25519" \
  -o IdentitiesOnly=yes \
  -o BatchMode=yes \
  ops@example-host \
  'id'

Explicit identity selection reduces surprises when an SSH agent contains several keys.

6. Preserve remote exit status

if ssh -o BatchMode=yes ops@example-host \
  'test -f /etc/myapp/config'; then
  printf 'remote config exists\n'
else
  status=$?
  printf 'ssh/remote command failed status=%d\n' "$status" >&2
fi

SSH status may represent a remote command failure or an SSH-level connection problem. Status 255 is commonly used by OpenSSH for SSH client errors.

7. SSH consumes stdin unless you control it

ssh -n ops@example-host 'long-running-readless-command'

-n redirects SSH stdin from /dev/null. This is useful inside loops so SSH does not accidentally consume the loop's input stream.

8. Avoid allocating a TTY unless the remote command needs one

ssh -T ops@example-host 'printf "non-interactive\n"'

TTY allocation changes signal, buffering, and shell behavior. Automation should normally run without a pseudo-terminal unless a remote tool explicitly requires one.

9. Remote environments are often smaller than interactive shells

ssh ops@example-host \
  'PATH=/usr/local/bin:/usr/bin:/bin; command -v mytool && mytool --version'

Do not assume interactive startup files run. Define required PATH or invoke absolute paths for critical remote tools.

10. Remote privilege escalation must be non-interactive too

ssh -T ops@example-host \
  'sudo -n systemctl restart myapp.service'

sudo -n fails instead of prompting. Whether passwordless sudo is appropriate depends on the host's security policy and should be narrowly scoped.

11. Hands-on lab: build a non-interactive SSH wrapper

mkdir -p "$HOME/devops-academy/bash/chapter14/lesson02"
cd "$HOME/devops-academy/bash/chapter14/lesson02"

cat > remote-run.sh <<'EOF'
#!/usr/bin/env bash
set -u

host=${1:-}
shift || true

[[ -n $host && $# -gt 0 ]] || {
  printf 'usage: %s HOST COMMAND [ARG...]\n' "$0" >&2
  exit 64
}

if ssh \
  -o BatchMode=yes \
  -o ConnectTimeout=5 \
  -T \
  "$host" \
  "$@"; then
  exit 0
else
  status=$?
  printf 'remote execution failed host=%s status=%d\n' \
    "$host" "$status" >&2
  exit "$status"
fi
EOF

chmod u+x remote-run.sh
printf 'Example: ./remote-run.sh user@host hostname\n'

Verification checklist

12. Knowledge check

Question 1. Why use BatchMode=yes?

Question 2. Should host-key checking simply be disabled for automation?

Question 3. Why use ssh -n inside input loops?

Question 4. Why can remote PATH differ from an interactive login?

13. Summary

Non-interactive SSH automation needs explicit trust, credentials, timeout, stdin/TTY behavior, remote environment, and exit-status handling. Treat SSH as a process boundary whose remote command and connection can fail independently.

14. Further reading

  • OpenSSH ssh(1) manual.
  • OpenSSH configuration documentation.
  • sudo(8) manual — non-interactive mode.
  • GNU Bash Reference Manual — quoting and command status.
Next lesson

scp, sftp, and rsync Transfer Workflows

Continue Chapter 14 by making remote networking and automation boundaries safer and more explicit.

Keep the academy open

Support free, practical DevOps education.

Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.