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 and chmod 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 is chmod 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.