SSH Remote Commands
ActionsTags
(2)- 🚀 SSH for GitHub Actions
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.
This action provides flexible SSH command execution with a rich set of configuration options.
For full details, see action.yml.
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 |
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 |
| capture_stdout | Capture standard output from commands as action output | false |
| version | drone-ssh binary version. If not specified, the latest version will be used. |
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_stopoption, addset -eat the top of your shell script.
This action provides the following outputs that you can use in subsequent steps:
| Output | Description |
|---|---|
| stdout | Standard output of the executed commands (requires capture_stdout: true) |
Run remote SSH commands in your workflow with minimal configuration:
name: Remote SSH Commandon: [push]jobs: build: name: Buildruns-on: ubuntu-lateststeps: - name: Execute remote SSH commands using passworduses: appleboy/ssh-action@v1with: host: ${{secrets.HOST }}username: ${{secrets.USERNAME }}password: ${{secrets.PASSWORD }}port: ${{secrets.PORT }}script: whoamiOutput:
======CMD====== whoami ======END====== out: your_username =============================================== ✅ Successfully executed commands to all hosts. ===============================================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:
ssh-keygen -t rsa -b 4096 -C "[email protected]"ssh-keygen -t ed25519 -a 200 -C "[email protected]"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_rsaTip: Copy from
-----BEGIN OPENSSH PRIVATE KEY-----to-----END OPENSSH PRIVATE KEY-----(inclusive).
For ED25519:
# macOS pbcopy <~/.ssh/id_ed25519 # Ubuntu xclip <~/.ssh/id_ed25519See 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
.sshpermissions to 700- Set
.ssh/authorized_keys2permissions to 640
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 "[email protected]"This section covers common and advanced usage patterns, including multi-host, proxy, and environment variable passing.
- name: Execute remote SSH commands using passworduses: appleboy/ssh-action@v1with: host: ${{secrets.HOST }}username: ${{secrets.USERNAME }}password: ${{secrets.PASSWORD }}port: ${{secrets.PORT }}script: whoami- name: Execute remote SSH commands using SSH keyuses: appleboy/ssh-action@v1with: host: ${{secrets.HOST }}username: ${{secrets.USERNAME }}key: ${{secrets.KEY }}port: ${{secrets.PORT }}script: whoami- name: Multiple commandsuses: appleboy/ssh-action@v1with: host: ${{secrets.HOST }}username: ${{secrets.USERNAME }}key: ${{secrets.KEY }}port: ${{secrets.PORT }}script: | whoami ls -al- name: File commandsuses: appleboy/ssh-action@v1with: host: ${{secrets.HOST }}username: ${{secrets.USERNAME }}key: ${{secrets.KEY }}port: ${{secrets.PORT }}script_path: scripts/script.sh - 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 -alDefault port is 22.
- 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 - 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 - 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
envobject must be strings. Using integers or other types may cause unexpected results.
You can capture the standard output of remote commands and use it in subsequent steps:
- name: Execute and capture outputid: sshuses: appleboy/ssh-action@v1with: host: ${{secrets.HOST }}username: ${{secrets.USERNAME }}key: ${{secrets.KEY }}port: ${{secrets.PORT }}capture_stdout: truescript: | echo "Hello World" hostname- name: Use captured outputrun: echo "SSH output was ${{steps.ssh.outputs.stdout }}"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 -alA 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 -alVerifying 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 '' -f2Update 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 -alIf 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" ] &&returnComment out this line or use absolute paths for your commands.
Contributions are welcome! Please submit a pull request to help improve appleboy/ssh-action.
This project is licensed under the MIT License.
SSH Remote Commands is not certified by GitHub. It is provided by a third-party and is governed by separate terms of service, privacy policy, and support documentation.

