Deploying to FTP/SSH with GitHub Actions

Deploy a static site through GitHub Actions to a server

Because of an excellent article over at, it didn’t take me nearly as long to figure out how to deploy a static-generated site (Astro) to my hosting provider.


Below is the deploy.yml file that I put in the .github/workflows folder. Works like a charm!

0. Overview

We need to generate four secrets in our repository settings for the deploy script to work:

More detailed instructions in Zell’s blog above, below an abbreviated summary.

1. Generate a SSH key on your server

ssh-keygen -t rsa -b 4096 -C ""

2. Add the public key to authorized_keys

On your server, add the newly generated key to authorized_keys:

cat >> ~/.ssh/authorized_keys

3. Add the private key to your repository secrets

4. Find the IP address of your server

5. Find the name of your SSH user account

6. Create a secret for your publish directory

Perhaps mostly to keep the deploy script clean and reusable…


7. Add the deploy script to your repository

This will grab the built dist directory, and copy the contents to your specified folder.

If your publish folder is named something else than dist, change the last line of the script where it says ./dist/ to whatever your publish directory is named.

name: deploy changes

      - master
      - main
    - cron: '0 0 * * *' # Everyday at 12am

    runs-on: ubuntu-latest
    permissions: write-all
        node-version: [18]
      - name: Checkout
        uses: actions/checkout@v3
          fetch-depth: 0
      - name: Use pnpm
        uses: pnpm/action-setup@v2.2.4
          version: 7
      - name: Use Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v3
          node-version: ${{ matrix.node-version }}
          cache: pnpm
      - name: Install dependencies
        run: pnpm install
      - name: Build project
        run: pnpm run build

      - name: Install SSH Key
        uses: shimataro/ssh-key-action@v2
          key: ${{ secrets.SSH_PRIVATE_KEY }}
          known_hosts: unnecessary

      - name: Adding known_hosts
        run: ssh-keyscan -H ${{ secrets.SSH_HOST }}  >> ~/.ssh/known_hosts

      - name: Deploy with rsync
        run: rsync -avz ./dist/ ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }}:${{ secrets.PUB_DIR }}