Deploying with SSH

February 20, 2023

I’ve deployed my personal applications in a lot of different ways over the years. This is my favorite. It’s simple, reliable, and easy to automate.


I want to deploy with a simple git push. On pushes to the master branch, a GitHub Action SSHes to my VPS with a restricted command key (sometimes called a single command or single purpose key) for the associated site.

name: Deploy

    branches: [master]

    runs-on: ubuntu-latest
      - name: Deploy via SSH
        run: |
          eval `ssh-agent -s`
          ssh-add - <<< "$SSH_KEY"
          ssh -o StrictHostKeyChecking=no $SSH_USER@$SSH_HOST
          SSH_USER: ${{ secrets.DEPLOY_SSH_USER }}
          SSH_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
          SSH_HOST: ${{ secrets.DEPLOY_SSH_HOST }}

On my VPS, ~/.ssh/authorized_keys holds the following:

command="/apps/deploy/",no-port-forwarding,no-x11-forwarding,no-agent-forwarding <the public key>

Whenever the deployment SSH key is used to connect to my VPS, it will automatically trigger the Bash script at /apps/deploy/ This key is unable to do anything else, so I’m comfortable giving it to GitHub.


This blog is a static site served by nginx. However, it is an Astro app, and as such needs to be generated. I do this with Docker to avoid having to install Node on my VPS. You can see the Dockerfile here, but the only interesting part is the very last line: RUN tar -cf build.tar dist. This creates a tarball containing the generated site, which we can then easily copy to the VPS’s filesystem.

The first half of the above mentioned Bash script does exactly that and is the following:


set -e

cd /apps/repos/
git pull
docker build . -t

docker run --rm cat build.tar > build.tar


Lastly, we need to publish the new static site. All that’s required is putting the files in the right spot. The second half of the Bash script does this.

tar -xf build.tar
rm -f build.tar

rm -rf /apps/nginx-sites/
mv dist /apps/nginx-sites/

That’s it!