Shell Scripting SSH Automation Intermediate May 2026

Shell Scripting SSH & Remote Execution

Automate SSH key setup, run commands across remote servers, transfer files with scp and rsync, build parallel multi-server deployment scripts, and use SSH multiplexing for performance.

SSH automation is the backbone of fleet management. Whether deploying to 3 servers or 300, the same patterns apply: key-based auth, non-interactive flags, and parallel execution via background jobs. Master these and you can drive any number of remote servers from a single script.

BASH
# ── Generate key pair for automation ─────────────────────
ssh-keygen -t ed25519 -C "deploy-bot@prod" \
           -f ~/.ssh/deploy_key -N ""   # no passphrase

# ── Copy public key to remote server ─────────────────────
ssh-copy-id -i ~/.ssh/deploy_key.pub vriddh@prod-01
ssh-copy-id -i ~/.ssh/deploy_key.pub -p 2222 vriddh@prod-02

# ── ~/.ssh/config — per-host settings ────────────────────
# Host prod-*
#   User vriddh
#   IdentityFile ~/.ssh/deploy_key
#   StrictHostKeyChecking no
#   ConnectTimeout 10
#   ServerAliveInterval 60
#
# Host prod-01
#   HostName 10.0.1.10
#
# Host prod-db-01
#   HostName 10.0.1.20

# ── SSH multiplexing — reuse connection ───────────────────
# Host *
#   ControlMaster auto
#   ControlPath ~/.ssh/mux/%r@%h:%p
#   ControlPersist 60s
# Subsequent SSH connections to same host reuse the socket
BASH
# ── Single command ────────────────────────────────────────
ssh vriddh@prod-01 "df -h"
ssh vriddh@prod-01 "uptime && free -h"

# ── Non-interactive (for scripts) ────────────────────────
ssh -o BatchMode=yes \
    -o ConnectTimeout=10 \
    -o StrictHostKeyChecking=no \
    vriddh@prod-01 "systemctl status nginx"

# ── Run local script on remote ────────────────────────────
ssh vriddh@prod-01 "bash -s" < local_script.sh

# ── Multi-line commands via here-doc ─────────────────────
ssh vriddh@prod-01 << 'REMOTE'
  set -e
  echo "Host: $(hostname)"
  cd /opt/myapp
  git pull origin main
  systemctl restart myapp
  echo "Deploy complete"
REMOTE

# ── Capture remote output ─────────────────────────────────
disk=$(ssh vriddh@prod-01 "df -h / | awk 'NR==2{print \$5}'")
echo "prod-01 disk: ${disk}"
BASH
#!/usr/bin/env bash
# deploy_all.sh — Deploy to all servers in parallel
set -euo pipefail

SERVERS=(web-01 web-02 web-03 web-04)
DEPLOY_SCRIPT=/opt/myapp/scripts/deploy.sh

deploy_one() {
  local host="${1}"
  local log="/tmp/deploy_${host}.log"
  echo "  [${host}] Starting..."
  if ssh -o BatchMode=yes -o ConnectTimeout=10 \
         "vriddh@${host}" "bash -s" \
         < "${DEPLOY_SCRIPT}" >> "${log}" 2>&1; then
    echo "  [${host}] ✔ Success"
  else
    echo "  [${host}] ✘ Failed — see ${log}" >&2
    return 1
  fi
}

echo "Deploying to ${#SERVERS[@]} servers..."
pids=()
for srv in "${SERVERS[@]}"; do
  deploy_one "${srv}" &
  pids+=($!)
done

failed=0
for pid in "${pids[@]}"; do
  wait "${pid}" || (( failed++ ))
done

(( failed > 0 )) && { echo "FAILED: ${failed} server(s)" >&2; exit 1; }
echo "All servers deployed successfully"
BASH
# ── scp — simple transfers ────────────────────────────────
scp config.env vriddh@prod-01:/etc/myapp/
scp vriddh@prod-01:/var/log/myapp/app.log ./logs/
scp -r ./dist/ vriddh@prod-01:/opt/myapp/

# ── rsync — preferred for large/repeated transfers ────────
rsync -avz -e ssh ./app/ vriddh@prod-01:/opt/myapp/
rsync -avz --delete ./app/ vriddh@prod-01:/opt/myapp/

# ── Collect logs from all servers in parallel ─────────────
LOG_DIR=/tmp/server_logs
mkdir -p "${LOG_DIR}"
for srv in web-01 web-02 web-03; do
  rsync -az "vriddh@${srv}:/var/log/myapp/app.log" \
            "${LOG_DIR}/${srv}_app.log" &
done
wait
echo "Logs collected to ${LOG_DIR}"
bash — deploy_all.sh
vriddh@bastion:~/scripts$./deploy_all.sh
Deploying to 4 servers...
[web-01] Starting...
[web-02] Starting...
[web-03] Starting...
[web-04] Starting...
[web-01] ✔ Success
[web-02] ✔ Success
[web-03] ✘ Failed — see /tmp/deploy_web-03.log
[web-04] ✔ Success
FAILED: 1 server(s)
✔ SSH automation rules — Always use key-based auth — never passwords in scripts. Always use -o BatchMode=yes to prevent interactive prompts hanging scripts. Always set -o ConnectTimeout=10 to fail fast. Use rsync over scp for large or repeated transfers. Enable SSH multiplexing (ControlMaster auto) when running many SSH commands to the same host.