How to Deploy a Site via SSH Using GitHub Actions

I love GitHub Actions. They are so simple and powerful, allowing you to have your code deployment and source code in one location.

I manage and deploy all of my sites using SSH (because it’s more secure), and over the years, I’ve adopted numerous deployment strategies. I adopted a Git strategy not too long ago where my server would pull down changes from Git, but it’s a flawed approach.

Here is an actual GitHub Actions build file I use for a project. It’s a mixture of Node.js and WordPress. If your needs are not as complicated, your file will resemble a fraction of this.

The first few parts, I think, are reasonably self-explanatory. Give our action file a name. Specify what triggers our build (in our case, a push to the master branch).

Inside of the jobs section, we get into the filling of our delicious GitHub Actions pie. We specify we want an Ubuntu Linux instance. I am managing my WordPress dependencies using Composer, so I am using the php-actions/composer action. I also defined I want Node.js version 14 installed (you can change this to suit your version needs).

You probably don’t need libnotify-bin I needed it for some scripts I was running which write console output.

Because I have Node dependencies inside of my WordPress theme, I cd into the directory and then run npm install — I have a couple of Aurelia applications inside of this theme which I then install the dependencies for and subsequently build (for deployment).

name: Deploy Prod Apps

on:
  push:
    branches:
      - master

jobs:
  deploy:
    name: Deploy apps to prod
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v2
      - uses: php-actions/composer@v6

      - name: Install Node.js
        uses: actions/setup-node@v1
        with:
          node-version: '14.x'

      - name: Install libnotify
        run: sudo apt-get install libnotify-bin

      - name: Install npm dependencies
        run: cd wp-content/themes/news-theme && npm install

      - name: Pinning Queue
        run: cd wp-content/themes/news-theme/platform-apps/pinning-queue && npm install && npm run build

      - name: Locality Selector
        run: cd wp-content/themes/news-theme/platform-apps/locality-selector && npm install && npm run build

      - name: Deploy
        uses: easingthemes/ssh-deploy@main
        env:
          SSH\_PRIVATE\_KEY: ${{ secrets.SSH\_PRIVATE\_KEY\_PROD }}
          ARGS: >-  
            -rltgoDzvO
            --exclude='.git'
            --exclude='node\_modules/'
          REMOTE\_HOST: ${{ secrets.REMOTE\_HOST\_PROD }}
          REMOTE\_USER: ${{ secrets.REMOTE\_USER\_PROD }}
          TARGET: /opt/bitnami/wordpress/

The SSH deployment step

This is the part you probably want to know about (not the other life story nonsense). We create a new build step called “Deploy” and use the easingthemes/ssh-deploy action. We then pass in some environment variables to this.

\- name: Deploy
  uses: easingthemes/ssh-deploy@main
  env:
    SSH\_PRIVATE\_KEY: ${{ secrets.SSH\_PRIVATE\_KEY\_PROD }}
    ARGS: >-  
      -rltgoDzvO
      --exclude='.git'
      --exclude='node\_modules/'
    REMOTE\_HOST: ${{ secrets.REMOTE\_HOST\_PROD }}
    REMOTE\_USER: ${{ secrets.REMOTE\_USER\_PROD }}
    TARGET: /opt/bitnami/wordpress/

The most important value here is SSH_PRIVATE_KEY this is your private key value, and you mustn’t publicly specify this in your file whatsoever. For actions, it is best practice to use secret values to pass into your GitHub Actions. I will assume you know how to generate SSH keys, register them and then copy the key. This section on the repo will point you in the right direction.

If you get stuck on this step, make sure you’re using your private key and not your public key. Most of the issues you’ll face are related to not providing the correct key value.

Secondly, you must exclude directories that you do not want to be copied. You don’t want to copy your .git directory, and you especially never want to copy a node_modules directory (have you ever seen how big this folder gets?).

The REMOTE_HOST value is usually the IP address of your site. The REMOTE_USER is the username you would use when tunnelling into our server via SSH. The TARGET is where your files will be copied. You can see I am using a Bitnami WordPress image, so I make sure my files are deployed into the WordPress directory.

Bonus points: Post deploy commands

Sadly, the ssh-deploy action does not support post deploy commands (the ability to run commands after a deploy is done). I have had to do this on a couple of occasions to fix file and folder permissions.

For post deploy commands, you can use the appleboy/ssh-action action to do this. It kind of looks similar to that of the above ssh-deploy.

- name: Post Deploy
        uses: appleboy/ssh-action@master
        with:
            host: ${{ secrets.REMOTE\_HOST\_PROD }}
            username: ${{ secrets.REMOTE\_USER\_PROD }}
            key: ${{ secrets.SSH\_PRIVATE\_KEY\_PROD }}
            port: 22
            script: | 
              sudo find /opt/bitnami/wordpress/wp-content/mu-plugins -type d -exec chmod 775 {} \\; 
              sudo find /opt/bitnami/wordpress/wp-content/plugins -type d -exec chmod 775 {} \\; 
              sudo find /opt/bitnami/wordpress/wp-content/themes -type d -exec chmod 775 {} \\; 
              sudo find /opt/bitnami/wordpress/wp-content/mu-plugins -type f -exec chmod 664 {} \\; 
              sudo find /opt/bitnami/wordpress/wp-content/plugins -type f -exec chmod 664 {} \\; 
              sudo find /opt/bitnami/wordpress/wp-content/themes -type f -exec chmod 664 {} \\; 
              sudo chmod 664 /opt/bitnami/wordpress/wp-config.php
              sudo chmod 775 /opt/bitnami/wordpress/wp-content/w3tc-config 
              sudo chmod 775 /opt/bitnami/wordpress/wp-content/cache

Here is a post-deploy build step that ensures all wp-content folders have 775 permission and the files inside are 664. The pipe on the first line allows us to run multiple script commands. If you have one, put it on a single line and don’t use a pipe.