๐Ÿš€ SSH for GitHub Actions

January 28, 2026 ยท View on GitHub

English | ็น้ซ”ไธญๆ–‡ | ็ฎ€ไฝ“ไธญๆ–‡

Table of Contents


๐Ÿ“– Introduction

SSH for GitHub Actions is a powerful GitHub Action for executing remote SSH commands easily and securely in your CI/CD workflows.
Built with Golang and drone-ssh, it supports a wide range of SSH scenarios, including multi-host, proxy, and advanced authentication.

ssh workflow

testing main branch Trivy Security Scan

Slides: SSH for GitHub Actions


๐Ÿงฉ Core Concepts & Input Parameters

This action provides flexible SSH command execution with a rich set of configuration options.

For full details, see action.yml.

๐Ÿ”Œ Connection Settings

These parameters control how the action connects to your remote host.

ParameterDescriptionDefault
hostSSH host address
portSSH port number22
usernameSSH username
passwordSSH password
protocolSSH protocol version (tcp, tcp4, tcp6)tcp
syncRun synchronously if multiple hosts are specifiedfalse
timeoutTimeout for SSH connection to host30s
keyContent of SSH private key (e.g., raw content of ~/.ssh/id_rsa)
key_pathPath to SSH private key
passphrasePassphrase for the SSH private key
fingerprintSHA256 fingerprint of the host public key
use_insecure_cipherAllow additional (less secure) ciphersfalse
cipherAllowed cipher algorithms. Uses sensible defaults if unspecified

๐Ÿ› ๏ธ SSH Command Settings

These parameters control the commands executed on the remote host and related behaviors.

ParameterDescriptionDefault
scriptCommands to execute remotely
script_pathPath to a file in the repository containing commands to execute remotely
envsEnvironment variables to pass to the shell script
envs_formatFlexible configuration for environment variable transfer
allenvsPass all environment variables with GITHUB_ and INPUT_ prefixes to the scriptfalse
command_timeoutTimeout for SSH command execution10m
debugEnable debug modefalse
request_ptyRequest a pseudo-terminal from the serverfalse
curl_insecureAllow curl to connect to SSL sites without certificatesfalse
capture_stdoutCapture standard output from commands as action outputfalse
versiondrone-ssh binary version. If not specified, the latest version will be used.

๐ŸŒ Proxy Settings

These parameters control the use of a proxy (jump host) for connecting to your target host.

ParameterDescriptionDefault
proxy_hostSSH proxy host
proxy_portSSH proxy port22
proxy_usernameSSH proxy username
proxy_passwordSSH proxy password
proxy_passphraseSSH proxy key passphrase
proxy_protocolSSH proxy protocol versiontcp
proxy_timeoutTimeout for SSH connection to proxy host30s
proxy_keyContent of SSH proxy private key
proxy_key_pathPath to SSH proxy private key
proxy_fingerprintSHA256 fingerprint of the proxy host public key
proxy_cipherAllowed cipher algorithms for the proxy
proxy_use_insecure_cipherAllow insecure ciphers for the proxyfalse

Note: To mimic the removed script_stop option, add set -e at the top of your shell script.


๐Ÿ“ค Output Variables

This action provides the following outputs that you can use in subsequent steps:

OutputDescription
stdoutStandard output of the executed commands (requires capture_stdout: true)

โšก Quick Start

Run remote SSH commands in your workflow with minimal configuration:

name: Remote SSH Command
on: [push]
jobs:
  build:
    name: Build
    runs-on: ubuntu-latest
    steps:
      - name: Execute remote SSH commands using password
        uses: appleboy/ssh-action@v1
        with:
          host: ${{ secrets.HOST }}
          username: ${{ secrets.USERNAME }}
          password: ${{ secrets.PASSWORD }}
          port: ${{ secrets.PORT }}
          script: whoami

Output:

======CMD======
whoami
======END======
out: your_username
===============================================
โœ… Successfully executed commands to all hosts.
===============================================

๐Ÿ”‘ SSH Key Setup & OpenSSH Compatibility

Setting Up SSH Keys

It is best practice to create SSH keys on your local machine (not on a remote server). Log in with the username specified in GitHub Secrets and generate a key pair:

Generate RSA key

ssh-keygen -t rsa -b 4096 -C "your_email@example.com"

Generate ED25519 key

ssh-keygen -t ed25519 -a 200 -C "your_email@example.com"

Add the new public key to the authorized keys on your server. Learn more about authorized keys.

# Add RSA key
cat .ssh/id_rsa.pub | ssh user@host 'cat >> .ssh/authorized_keys'

# Add ED25519 key
cat .ssh/id_ed25519.pub | ssh user@host 'cat >> .ssh/authorized_keys'

Copy the private key content and paste it into GitHub Secrets.

# macOS
pbcopy < ~/.ssh/id_rsa
# Ubuntu
xclip < ~/.ssh/id_rsa

Tip: Copy from -----BEGIN OPENSSH PRIVATE KEY----- to -----END OPENSSH PRIVATE KEY----- (inclusive).

For ED25519:

# macOS
pbcopy < ~/.ssh/id_ed25519
# Ubuntu
xclip < ~/.ssh/id_ed25519

See more: SSH login without a password.

Note: Depending on your SSH version, you may also need to:

  • Place the public key in .ssh/authorized_keys2
  • Set .ssh permissions to 700
  • Set .ssh/authorized_keys2 permissions to 640

OpenSSH Compatibility

If you see this error:

ssh: handshake failed: ssh: unable to authenticate, attempted methods [none publickey]

On Ubuntu 20.04+ you may need to explicitly allow the ssh-rsa algorithm. Add this to your OpenSSH daemon config (/etc/ssh/sshd_config or a drop-in under /etc/ssh/sshd_config.d/):

CASignatureAlgorithms +ssh-rsa

Alternatively, use ED25519 keys (supported by default):

ssh-keygen -t ed25519 -a 200 -C "your_email@example.com"

๐Ÿ› ๏ธ Usage Scenarios & Advanced Examples

This section covers common and advanced usage patterns, including multi-host, proxy, and environment variable passing.

Using password authentication

- name: Execute remote SSH commands using password
  uses: appleboy/ssh-action@v1
  with:
    host: ${{ secrets.HOST }}
    username: ${{ secrets.USERNAME }}
    password: ${{ secrets.PASSWORD }}
    port: ${{ secrets.PORT }}
    script: whoami

Using private key authentication

- name: Execute remote SSH commands using SSH key
  uses: appleboy/ssh-action@v1
  with:
    host: ${{ secrets.HOST }}
    username: ${{ secrets.USERNAME }}
    key: ${{ secrets.KEY }}
    port: ${{ secrets.PORT }}
    script: whoami

Multiple commands

- name: Multiple commands
  uses: appleboy/ssh-action@v1
  with:
    host: ${{ secrets.HOST }}
    username: ${{ secrets.USERNAME }}
    key: ${{ secrets.KEY }}
    port: ${{ secrets.PORT }}
    script: |
      whoami
      ls -al

result

Run commands from a file

- name: File commands
  uses: appleboy/ssh-action@v1
  with:
    host: ${{ secrets.HOST }}
    username: ${{ secrets.USERNAME }}
    key: ${{ secrets.KEY }}
    port: ${{ secrets.PORT }}
    script_path: scripts/script.sh

Multiple hosts

  - name: Multiple hosts
    uses: appleboy/ssh-action@v1
    with:
-     host: "foo.com"
+     host: "foo.com,bar.com"
      username: ${{ secrets.USERNAME }}
      key: ${{ secrets.KEY }}
      port: ${{ secrets.PORT }}
      script: |
        whoami
        ls -al

Default port is 22.

Multiple hosts with different ports

  - name: Multiple hosts
    uses: appleboy/ssh-action@v1
    with:
-     host: "foo.com"
+     host: "foo.com:1234,bar.com:5678"
      username: ${{ secrets.USERNAME }}
      key: ${{ secrets.KEY }}
      script: |
        whoami
        ls -al

Synchronous execution on multiple hosts

  - name: Multiple hosts
    uses: appleboy/ssh-action@v1
    with:
      host: "foo.com,bar.com"
+     sync: true
      username: ${{ secrets.USERNAME }}
      key: ${{ secrets.KEY }}
      port: ${{ secrets.PORT }}
      script: |
        whoami
        ls -al

Pass environment variables to shell script

  - name: Pass environment
    uses: appleboy/ssh-action@v1
+   env:
+     FOO: "BAR"
+     BAR: "FOO"
+     SHA: ${{ github.sha }}
    with:
      host: ${{ secrets.HOST }}
      username: ${{ secrets.USERNAME }}
      key: ${{ secrets.KEY }}
      port: ${{ secrets.PORT }}
+     envs: FOO,BAR,SHA
      script: |
        echo "I am $FOO"
        echo "I am $BAR"
        echo "sha: $SHA"

All environment variables in the env object must be strings. Using integers or other types may cause unexpected results.

Capturing command output

You can capture the standard output of remote commands and use it in subsequent steps:

- name: Execute and capture output
  id: ssh
  uses: appleboy/ssh-action@v1
  with:
    host: ${{ secrets.HOST }}
    username: ${{ secrets.USERNAME }}
    key: ${{ secrets.KEY }}
    port: ${{ secrets.PORT }}
    capture_stdout: true
    script: |
      echo "Hello World"
      hostname

- name: Use captured output
  run: echo "SSH output was ${{ steps.ssh.outputs.stdout }}"

๐ŸŒ Proxy & Jump Host Usage

You can connect to remote hosts via a proxy (jump host) for advanced network topologies.

+--------+       +----------+      +-----------+
| Laptop | <-->  | Jumphost | <--> | FooServer |
+--------+       +----------+      +-----------+

Example ~/.ssh/config:

Host Jumphost
  HostName Jumphost
  User ubuntu
  Port 22
  IdentityFile ~/.ssh/keys/jump_host.pem

Host FooServer
  HostName FooServer
  User ubuntu
  Port 22
  ProxyCommand ssh -q -W %h:%p Jumphost

GitHub Actions YAML:

  - name: SSH proxy command
    uses: appleboy/ssh-action@v1
    with:
      host: ${{ secrets.HOST }}
      username: ${{ secrets.USERNAME }}
      key: ${{ secrets.KEY }}
      port: ${{ secrets.PORT }}
+     proxy_host: ${{ secrets.PROXY_HOST }}
+     proxy_username: ${{ secrets.PROXY_USERNAME }}
+     proxy_key: ${{ secrets.PROXY_KEY }}
+     proxy_port: ${{ secrets.PROXY_PORT }}
      script: |
        mkdir abc/def
        ls -al

๐Ÿ›ก๏ธ Security Best Practices

Protecting Your Private Key

A passphrase encrypts your private key, making it useless to attackers if leaked. Always store your private key securely.

  - name: SSH key passphrase
    uses: appleboy/ssh-action@v1
    with:
      host: ${{ secrets.HOST }}
      username: ${{ secrets.USERNAME }}
      key: ${{ secrets.KEY }}
      port: ${{ secrets.PORT }}
+     passphrase: ${{ secrets.PASSPHRASE }}
      script: |
        whoami
        ls -al

Host Fingerprint Verification

Verifying the SSH host fingerprint helps prevent man-in-the-middle attacks. To get your host's fingerprint (replace ed25519 with your key type and example.com with your host):

ssh example.com ssh-keygen -l -f /etc/ssh/ssh_host_ed25519_key.pub | cut -d ' ' -f2

Update your config:

  - name: SSH key passphrase
    uses: appleboy/ssh-action@v1
    with:
      host: ${{ secrets.HOST }}
      username: ${{ secrets.USERNAME }}
      key: ${{ secrets.KEY }}
      port: ${{ secrets.PORT }}
+     fingerprint: ${{ secrets.FINGERPRINT }}
      script: |
        whoami
        ls -al

๐Ÿšจ Error Handling & Troubleshooting

Q&A

Command not found (npm or other command)

If you encounter "command not found" errors, see this issue comment about interactive vs non-interactive shells.

On many Linux distros, /etc/bash.bashrc contains:

# If not running interactively, don't do anything
[ -z "$PS1" ] && return

Comment out this line or use absolute paths for your commands.


๐Ÿค Contributing

Contributions are welcome! Please submit a pull request to help improve appleboy/ssh-action.


๐Ÿ“ License

This project is licensed under the MIT License.