November 18, 2020 - 4 min read

Deploy Hugo websites with Github Actions

A short how-to.


Photo by JJ Ying on Unsplash

How to deploy a static website generated with Hugo to a webserver? In this post, I will share the steps that I followed in order to be able to deploy this very blog using Github Actions to the shared hosting that I have.

Use case: I have a Hugo website, code hosted on GitHub, how do I deploy it to a location of my choice?

The assumed starting point is:

  • A configured working hosting server that we will call here THE_SERVER with an ssh access (e.g.
  • a website built with Hugo.
  • Basic knowledge of ssh and Hugo.

Let’s start!

Configuring the secure connection

The first thing is to generate a first pair of private/public ssh keys. In a terminal (local, distant, your taste):

 ssh-keygen -t rsa -b 4096

Or better, if your server supports it, adjusting filenames accordingly:

 ssh-keygen -a 100 -t ed25519

You will be prompted for a location - specify a location that you consider secure and a name. Do not type in any password. I will here assume that it is saved under the name id_rsa for the private key with the corresponding that should be automatically generated.

Go to next to, adapting to your situation:[GITHUB_USERNAME]/[GITHUB_REPOSITORY]/settings/secrets/actions

On this page, copy the private key - id_rsa - into a secret variable called DEPLOY_KEY. In plain english, this means, open the private key file in a text editor of you choice, copy everything in it:


and paste it in the Value text field. You should not have any blank lines above, below or in the middle of the text, depending on how the key was generated you might have blank lines at the end or in between the keys and the header/footer. Save the key.

Now, connect to THE_SERVER through an ssh connexion with something like ssh THE_SERVER_USERNAME@THE_SERVER and type your password. Copy the public key content to the ~/.ssh/authorized_keys file on THE_SERVER. If it is not empty, just add a new line or create the file if it did not exist. Just in case, make sure the file rights are Ok with chmod 600 ~/.ssh/authorized_keys.

Next, still in the ssh session, you will need to generate a new pair of keys with the following:

 ssh-keygen -t rsa -b 4096

Or better, if your server supports it, adjusting filenames accordingly:

 ssh-keygen -a 100 -t ed25519

Use a different name, here I will assume that the two files are in/called: ~/.ssh/id_rsa_gh / ~/.ssh/ Do not type in any password.

Make sure that the proper rights are applied:

chmod 400 ~/.ssh/id_rsa_gh
chmod 600 ~/.ssh/

This is important as Github expect certain permissions on these files.

We still have a little something to do, create a new file, I use vim, with: vim ~/.ssh/config. Type in the following:

    User git
    IdentityFile ~/.ssh/id_rsa_gh
    IdentitiesOnly yes

And again:

chmod 600 ~/.ssh/config

Now copy the public key present in the ~/.ssh/ file present on THE_SERVER and copy/paste into a deploy key here:[GITHUB_USERNAME]/[GITHUB_REPOSITORY]/settings/keys

Finally, go back to the terminal where you have an open connection to THE_SERVER and type in: ssh -T It should ask for your confirmation, type yes and you can close the ssh session if you see the following:

 Hi [GITHUB_USERNAME]/[GITHUB_REPOSITORY]! You\ve successfully authenticated, but GitHub does not provide shell access.

This handles the configuration of the secured connection.

Configure the Github Action pipeline

The big advantage of static generators is that you can focus on the content, writing in markdown and let the pipeline automatically handle the deployment.

The general logic of the pipeline is that it will use GitHub Actions to get the source code (theme included), build the static pages and transfer them using rsync. In the following configuration, we will build and deploy every time a commit or a pull request is pushed on the master branch. For this, you will need to add a pipeline file in your repository under .github/worflows/main.yml and configure some variables:

name: hugo rsync
    branches: [ master ]
    branches: [ master ]

    runs-on: ubuntu-latest
      - uses: actions/checkout@v2
          submodules: true  # Fetch Hugo themes (true OR recursive)
          fetch-depth: 0    # Fetch all history for .GitInfo and .Lastmod

      - name: Setup Hugo
        uses: peaceiris/actions-hugo@v2
          hugo-version: '0.74.3'

      - name: Build
        run: hugo --minify

      - name: Deployment rsync
        uses: burnett01/rsync-deployments@4.1
          switches: -avzr --delete
          path: public/
          remote_path: WWW/PATH/ON/THE/DISTANT/SERVER
          remote_host: ${{ secrets.DEPLOY_HOSTNAME }}
          remote_port: ${{ secrets.DEPLOY_PORT }}
          remote_user: ${{ secrets.DEPLOY_USERNAME }}
          remote_key: ${{ secrets.DEPLOY_KEY}}

All that is left to do, is to add a few keys in the repository’s secrets page to assign the DEPLOY_HOSTNAME, DEPLOY_PORT (often 22) and DEPLOY_USERNAME variables that matches THE_SERVER configuration.

That’s all! I never used Github Actions before this and I lost a couple of half-hours on some pain points without finding this use case described fully somewhere. This is corrected now. Please, contact me if something is missing or suggestions.

This is a very basic pipeline but it is a working starting point.


Suggestion by Jan Reilink, for better security:

So if your server supports it, replace the key generation with the more recent ed25519 encryption!

 ssh-keygen -a 100 -t ed25519

Copyright 2021 - Mikael Koutero. All rights reserved.

Privacy Statement