Skip to content

appleboy/ssh-action

Use this GitHub action with your project
Add this Action to an existing workflow or create a new one
View on Marketplace

Repository files navigation

🚀 SSH for GitHub Actions

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


🧩 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.

Parameter Description Default
host SSH host address
port SSH port number 22
username SSH username
password SSH password
protocol SSH protocol version (tcp, tcp4, tcp6) tcp
sync Run synchronously if multiple hosts are specified false
timeout Timeout for SSH connection to host 30s
key Content of SSH private key (e.g., raw content of ~/.ssh/id_rsa)
key_path Path to SSH private key
passphrase Passphrase for the SSH private key
fingerprint SHA256 fingerprint of the host public key
use_insecure_cipher Allow additional (less secure) ciphers false
cipher Allowed cipher algorithms. Uses sensible defaults if unspecified

🛠️ SSH Command Settings

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

Parameter Description Default
script Commands to execute remotely
script_path Path to a file in the repository containing commands to execute remotely
envs Environment variables to pass to the shell script
envs_format Flexible configuration for environment variable transfer
allenvs Pass all environment variables with GITHUB_ and INPUT_ prefixes to the script false
command_timeout Timeout for SSH command execution 10m
debug Enable debug mode false
request_pty Request a pseudo-terminal from the server false
curl_insecure Allow curl to connect to SSL sites without certificates false
version drone-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.

Parameter Description Default
proxy_host SSH proxy host
proxy_port SSH proxy port 22
proxy_username SSH proxy username
proxy_password SSH proxy password
proxy_passphrase SSH proxy key passphrase
proxy_protocol SSH proxy protocol version tcp
proxy_timeout Timeout for SSH connection to proxy host 30s
proxy_key Content of SSH proxy private key
proxy_key_path Path to SSH proxy private key
proxy_fingerprint SHA256 fingerprint of the proxy host public key
proxy_cipher Allowed cipher algorithms for the proxy
proxy_use_insecure_cipher Allow insecure ciphers for the proxy false

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


⚡ 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: linuxserver.io password: ${{ secrets.PASSWORD }} port: ${{ secrets.PORT }} script: whoami

Output:

======CMD====== whoami ======END====== linuxserver.io =============================================== ✅ 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.


🌐 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.