Simple zero downtime deployment with github actions

Posted on Sep 14, 2023

Github actions is a powerful tool. Today I built a simple deployment configuration for a client.

It is uploading repository content to server via rsync.


We need to create ssh keys. ssh-keygen -t rsa -b 4096 -C "" generate your keys. You will have private and public keys.

Add generated public key to your servers .ssh/authorized_keys file. Add generated private key to github repositories secrets as PKEY

How is working?

We will use shimataro/ssh-key-action@v2 action to configure ssh key in runner. Save SSH_HOST,SSH_USER and APP_ROOT folders in github repository secrets.

Then we will use ssh-keyscan command to update runners know_hosts file.

I use commit sha as folder name. And running folder create command on server.

Then i am sending files to that folder. After that i am updating symlink to current folder and removing all folders. I am keeping 5 most updated folders for rollbacks. Then restarting the server.

I’m adding full actions file in here as reference. Hope i find time next time to give more details about that.

have fun.

name: Deployment
run-name: Deployment
      - deploy-debug
    runs-on: ubuntu-latest
      - name: Checkout
        uses: actions/checkout@v3
          ref: deploy-debug
      - name: Install SSH Key
        uses: shimataro/ssh-key-action@v2
          key: ${{ secrets.PKEY }}
          known_hosts: 'just-a-placeholder-so-we-dont-get-errors'
      - name: Adding Known Hosts
        run: ssh-keyscan -H ${{ secrets.SSH_HOST }} >> ~/.ssh/known_hosts
      - name: Create a folder
        run: ssh ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} 'mkdir ${{github.sha}}'
      - name: Deploy with rsync 
        run: rsync --exclude '.git' --exclude '.github' --exclude '.gitignore' -avz ./ ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }}:${{ secrets.APP_ROOT }}
      - name: Update link
        run: ssh ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} 'ln -s  ${{ secrets.APP_ROOT }}/${{github.sha}} /var/www/current'
      - name: Remove older versions
        run: ssh ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} 'cd ${{ secrets.APP_ROOT }} && folders=($(find . -maxdepth 1 -type d -exec stat -f "%m %N" {} \; | sort -n | cut -d' ' -f2-)) && num_folders="${#folders[@]}" && keep_count=5 && [ "$num_folders" -gt "$keep_count" ] && delete_count=$((num_folders - keep_count)) && for ((i = 0; i < delete_count; i++)); do rm -rf "${folders[i]}"; done && echo "Removed old versions" || echo "Nothing to remove."'
      - name: Reload the Apache
        run: ssh ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} 'service apache2 reload'