8.1 KiB
Setup SSH Client
A composite GitHub Action that configures SSH client for running remote commands securely.
Features
- 🔐 Secure SSH key management
- 🔧 Automatic SSH configuration
- 🔑 Known hosts setup with ssh-keyscan fallback
- ✅ Connection validation
- 🎯 Simple, reusable configuration
- 🚀 Custom shell wrapper for seamless remote command execution
Usage
Basic Example with Custom Shell
name: Deploy Application
on: [push]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup SSH
uses: your-username/setup-ssh-client@v1
with:
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
ssh-host: ${{ secrets.SSH_HOST }}
ssh-user: ${{ secrets.SSH_USER }}
# Optional: enable debug mode to print the temp script and its
# contents on the runner for troubleshooting "no output" cases.
debug-shell-wrapper: 'true'
- name: Run remote commands with custom shell
shell: ssh-remote {0}
run: |
cd /var/www
git pull origin main
npm install
systemctl restart myapp
- name: Or use direct SSH commands
run: |
ssh github-action-host "uptime"
ssh github-action-host "df -h"
Basic Example (Direct SSH)
name: Deploy Application
on: [push]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup SSH
uses: your-username/setup-ssh-client@v1
with:
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
ssh-host: ${{ secrets.SSH_HOST }}
ssh-user: ${{ secrets.SSH_USER }}
- name: Run remote commands
run: |
ssh github-action-host "cd /var/www && git pull"
ssh github-action-host "systemctl restart myapp"
Advanced Example
- name: Setup SSH with custom configuration
uses: your-username/setup-ssh-client@v1
with:
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
ssh-host: example.com
ssh-user: deploy
ssh-port: '2222'
strict-host-key-checking: 'yes'
ssh-known-hosts: |
example.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC...
- name: Deploy application
run: |
ssh github-action-host "cd /var/www/app && ./deploy.sh"
Using Different Remote Shells
- name: Setup SSH with zsh
uses: your-username/setup-ssh-client@v1
with:
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
ssh-host: example.com
ssh-user: deploy
remote-shell: 'zsh'
- name: Run commands in remote zsh
shell: ssh-remote
run: |
source ~/.zshrc
echo "Running in zsh: $SHELL"
Disable Shell Wrapper (SSH Direct Only)
- name: Setup SSH without shell wrapper
uses: your-username/setup-ssh-client@v1
with:
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
ssh-host: example.com
ssh-user: deploy
use-shell-wrapper: 'false'
- name: Use direct SSH commands
run: |
ssh github-action-host "command1"
ssh github-action-host "command2"
Inputs
| Input | Description | Required | Default |
|---|---|---|---|
ssh-private-key |
SSH private key for authentication | Yes | - |
ssh-host |
SSH host to connect to | Yes | - |
ssh-user |
SSH username | Yes | - |
ssh-port |
SSH port | No | 22 |
ssh-known-hosts |
Known hosts content (uses ssh-keyscan if not provided) | No | '' |
strict-host-key-checking |
Enable strict host key checking (yes/no/accept-new) |
No | accept-new |
use-shell-wrapper |
Create shell wrapper for remote execution (enables shell: ssh-remote {0}) |
No | true |
remote-shell |
Shell to use on remote server (bash, sh, zsh, etc.) |
No | bash |
debug-shell-wrapper |
Print script path and contents before executing remote commands (for debugging only) | No | false |
Outputs
| Output | Description |
|---|---|
ssh-config-path |
Path to the SSH config file |
ssh-key-path |
Path to the SSH private key |
shell-wrapper-path |
Path to the SSH remote shell wrapper script |
Remote Command Execution
This action provides two ways to execute commands remotely:
1. Custom Shell Wrapper (Recommended)
Use shell: ssh-remote {0} in any step to execute the entire script on the remote server:
- name: Deploy application
shell: ssh-remote {0}
run: |
cd /var/www/myapp
git pull origin main
npm install --production
pm2 restart myapp
Benefits:
- Natural multi-line script syntax shell: ssh-remote {0}
- Works like a local shell
- No need to wrap commands in SSH
⚠️ Note: Enabling debug-shell-wrapper: 'true' will print the contents of the temporary script and the exit code to the job logs. This can help diagnose "no output" runs, but may leak secrets or other sensitive data — only enable on trusted runs.
2. SSH Host Alias (Direct)
- name: Run single commands
run: |
ssh github-action-host "uptime"
ssh github-action-host "df -h"
This eliminates the need to specify the host, user, port, and key path in every SSH command.
Generating SSH Keys
# Generate a dedicated SSH key pair for GitHub Actions
ssh-keygen -t ed25519 -C "github-actions" -f github_actions_key
shell: ssh-remote {0}
Setting up Secrets
-
Copy your private key content:
cat github_actions_key -
Add it to GitHub Secrets:
- Go to your repository → Settings → Secrets and variables → Actions
- Click "New repository secret"
- Name:
SSH_PRIVATE_KEY - Value: Paste the entire private key content
-
Add the public key to your server:
cat github_actions_key.pub >> ~/.ssh/authorized_keys
Getting Known Hosts
To pre-populate known hosts (recommended for security): shell: ssh-remote {0} ssh-keyscan -H your-server.com
Add the output to a GitHub secret named `SSH_KNOWN_HOSTS`.
## Troubleshooting
### Debugging no output from custom shell
If a step using `shell: ssh-remote {0}` shows no output but reports success:
- Enable `debug-shell-wrapper: 'true'` in the `Setup SSH` step. This will tell the wrapper to print the temp script path and the first 200 lines of the script it executes. Example:
```yaml
- name: Setup SSH
uses: your-username/setup-ssh-client@v1
with:
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
ssh-host: ${{ secrets.SSH_HOST }}
ssh-user: ${{ secrets.SSH_USER }}
debug-shell-wrapper: 'true'
- Re-run the job — the logs from subsequent steps will include the script path and contents; confirm the commands you expect are present.
- If the script looks correct, try a direct SSH command from a
run:step to verify remote side:
- name: Direct SSH smoke test
run: ssh github-action-host "echo hello; whoami; pwd"
- If the direct test shows output but the wrapper step still shows nothing, examine whether your remote shell sets PATH or environment differently for non-interactive shells — you may need to force a login shell or source shell rc files with
shell: ssh-remote {0}andrun: | source ~/.bashrc; your commandsor setremote-shell: 'bash -l'.
Note: Don't forget to disable debug-shell-wrapper when done; it prints the script contents into logs which may reveal secrets.
Connection Timeout
If the connection test fails with a timeout:
- Verify the host and port are correct
- Check firewall rules allow connections from GitHub Actions IPs
- Ensure SSH service is running on the remote server
Permission Denied
If you get "Permission denied":
- Verify the public key is in
~/.ssh/authorized_keyson the remote server - Check the private key format is correct (should include header/footer)
- Ensure the SSH user has appropriate permissions
Host Key Verification Failed
If you encounter host key verification issues:
- Set
strict-host-key-checking: 'no'(not recommended for production) - Or provide
ssh-known-hostsinput with the correct host key
License
MIT License - see LICENSE file for details
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.