Generating SSH Keys for GitHub Actions Deployment
These instructions were generated by a bot and reviewed, edited and executed by me, to reflect current best practices for using SSH keys from GitHub Actions to deploy to a Linux server. They replace password-based approaches like sshpass
with key-based auth, ensure correct file permissions, and add host-key verification for secure non‑interactive connections.
1) Create a dedicated deploy key (recommended: Ed25519)
Generate a fresh keypair on your workstation (or any secure machine you control). Use a distinct filename so you don’t overwrite your personal key:
ssh-keygen -t ed25519 -a 100 -C "deploy_app_to_server_github_actions" -f ~/.ssh/deploy_app_to_server_github_actions
This produces ~/.ssh/deploy_app_to_server_github_actions
(private key) and ~/.ssh/deploy_app_to_server_github_actions.pub
(public key). For CI, leave the passphrase empty. If you insist on using a passphrase, you’ll need a workflow that can supply it to ssh-add
; the common webfactory/ssh-agent
action expects an unencrypted key.
2) Authorize the key on the server
Add the public key to the current user’s ~/.ssh/authorized_keys
on your server and set strict permissions:
# Copy the public key to your local authorized_keys file
cat ~/.ssh/deploy_app_to_server_github_actions.pub >> ~/.ssh/authorized_keys
# Set strict permissions so SSH will accept the key
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
chown -R "$(whoami)":"$(whoami)" ~/.ssh
# (Optional) Remove duplicate keys
sort -u ~/.ssh/authorized_keys -o ~/.ssh/authorized_keys
3) Store the private key in GitHub Secrets
Copy the contents of ~/.ssh/deploy_app_to_server_github_actions
(the private key that begins with -----BEGIN OPENSSH PRIVATE KEY-----
) into a repository secret, e.g. LINUXHOST_PRIVATE_KEY
. GitHub supports multiline secrets—paste it exactly as-is.
5) Use the key in your workflow
Preferred: load the key into an agent; don’t write it to disk.
name: Deploy
on:
push:
branches: [ main ]
jobs:
deploy:
runs-on: ubuntu-latest
environment: github_environment_name # Replace with your environment name
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Set up .NET
uses: actions/setup-dotnet@v1
with:
dotnet-version: '9.0.x'
# Start ssh-agent and load the private key from secrets
- name: Use SSH key
uses: webfactory/ssh-agent@v0.9.0
with:
ssh-private-key: ${{ secrets.LINUXHOST_PRIVATE_KEY }}
# (Optional but safer than accept-new) add host key
- name: Add host key
run: |
mkdir -p ~/.ssh
ssh-keyscan -H ${{ secrets.LINUXHOST }} >> ~/.ssh/known_hosts
# Copy build artifacts
- name: Copy files
run: |
scp -r publish/* ${{ secrets.LINUXHOSTUSERNAME }}@${{ secrets.LINUXHOST }}:~/myapp
# Restart your service
- name: Restart service
run: |
ssh -o StrictHostKeyChecking=accept-new ${{ secrets.LINUXHOSTUSERNAME }}@${{ secrets.LINUXHOST }} 'sudo systemctl restart service-name.service'
6) Troubleshooting “Permission denied (publickey,password)”
- Confirm you’re connecting as the same user whose
authorized_keys
you edited. - Verify server permissions:
chmod 700 ~/.ssh
andchmod 600 ~/.ssh/authorized_keys
. - Make sure the private key in secrets matches the public key on the server (no stray whitespace; keep it multiline).
- If you wrote the key to disk, ensure the file is
chmod 600
and the directory ischmod 700
. - Use
-o StrictHostKeyChecking=accept-new
for first connection.
Notes & Security
- Prefer keys over
sshpass
. Password-based automation is discouraged and can leak credentials in process lists or logs. - Create a dedicated key just for CI. Rotate it by replacing the public key on the server and updating the secret.
- Consider disabling password auth on the server once key logins are working.
That’s it—GitHub Actions can now securely connect over SSH using your dedicated deploy key.