August 19, 2020

Publishing a Hugo Site With Github Actions

Storing your Hugo site in a Github repository makes it easy to automate re-generating and uploading your site every time you create a new commit. I'll assume you have a Hugo site in a Github repository already & walk through the steps to configure Github Actions to generate the site & upload it using rsync over SSH, with key-based authentication:

  1. Create a Workflow

    In the root directory of your Hugo site, create a .github directory. Within that directory create a workflows directory. Finally, within the workflows directory create an empty file named main.yaml.

    At the top of the file, add a name, I used the name of the site:


    The next section – on: – describes when to invoke the Action. I've set mine to build/deploy on every push to the Git repository (with the push: section) and to offer manual deploys (workflow_dispatch:):

            - main

    The meat of the file is the jobs: configuration, which installs the necessary pre-requisites to check out the repository, generate the Hugo site, and use rsync to deploy it:

          runs-on: ubuntu-18.04
            - uses: actions/checkout@v2
                submodules: false  # Fetch Hugo themes (use true OR recursive to fetch)
                fetch-depth: 0    # Fetch all history for .GitInfo and .Lastmod
            - name: Setup Hugo
              uses: peaceiris/actions-hugo@v2
                hugo-version: '0.74.2'
                # extended: true
            - name: Build
              run: hugo --minify
            - name: Deploy
              id: deploy
              uses: Pendect/action-rsyncer@v1.1.0
                DEPLOY_KEY: ${{secrets.DEPLOY_KEY}}
                flags: '-vzcrlp --delete --chmod=Dgo+rx,Fgo+r'
                options: ''
                ssh_options: ''
                src: 'public/'
                dest: 'SSH_USERNAME@SSH_HOST:HOST_PATH'
            - name: Display status from deploy
              run: echo "${{ steps.deploy.outputs.status }}"

    Take particular note of the dest: line - this needs to contain the correct parameters the deployment target. The HOST_PATH must include a trailing /, as well. An example dest value is:

                dest: ''
  2. Generate Keys

    With the Workflow in place the Action to build the site will work, but when it attempts to rsync it will fail, because the Action doesn't have SSH access. To fix this, generate an SSH key just for the Action. Hit <ENTER> when prompted for a passphrase:

      $ ssh-keygen -f github-action
      Generating public/private rsa key pair.
      Enter passphrase (empty for no passphrase):
      Enter same passphrase again:
      Your identification has been saved in github-action.
      Your public key has been saved in
      The key fingerprint is:
      The key's randomart image is:
      +---[RSA 2048]----+
      |               .o|
      |              .oo|
      |              +oo|
      |       E .   . =.|
      |      o S o   +.*|
      |       + + + o +=|
      |      . o Boo.o= |
      |       .+=o+=.+ o|
      |      .o +=+.=++ |

    This generates a private key in the github-actions file, and a public key in Copy the contents of and add it to the .authorized_keys file on the server to which the site will be deployed.

  3. Give Github the Private Key

    The contents of github-actions (the private key) need to be added to Github. Go to the 'Settings' page for the site's Github repository and select 'Secrets':

    Before adding secrets

    Select 'New Secret'. Enter the 'Name' DEPLOY_KEY and paste the contents of the github-actions private key vile in the 'Value' box:

    Adding the DEPLOY_KEY secret

    After clicking 'Add secret' the key will appear in the list of secrets:

    After adding the secret

  4. Test the Deploy

    Navigate to the 'Actions' tab of the Github repository and select the workflow with the name configured in main.yaml. The 'Run workflow' button will present a menu to select the branch to use.

    The Actions page and menu

    If everything goes well the deploy will churn for a little bit and then display a check-box to indicate success.

A Note on rsync Options

The Action that builds the site checks everything out anew when it runs, which means that the mtimes of the files it generates will always be set to the generation time. rsync, by default, uses the mtime as a cheap way to determine if a file should be sync-ed. To prevent every file on the site from sync-ing every time this setup uses the -c option to indicate that rsync should use file checksums to determine if they should be sync-ed. It also uses the -p option, combined with --chmod=Dgo+rx,Fgo+r to ensure that all files and directories are readable by all users. These can be modified to suit any specific case.