This guide walks you through using GitHub Actions to build a Hugo static site and deploy it to a remote server whenever you push to a branch. Hugo turns Markdown (and other content) into static HTML; GitHub Actions runs the build on push and syncs the public/ output to your server via SSH and rsync.
GitHub Actions in brief
GitHub Actions automates parts of your workflow (testing, building, deploying) in response to events such as push, pull request, or merge. A workflow is a YAML file (under .github/workflows/) that defines one or more jobs, each made up of steps. For a static site, a typical job checks out the repo, installs Hugo, builds the site, then deploys the build artifact (e.g. with rsync over SSH).
Prerequisites
- A Hugo site in a GitHub repository (see Blog with Hugo static site generator if you need a starting point).
- A destination server reachable by SSH (VPS, cloud VM, or your own host).
- SSH key pair: the public key on the server (
~/.ssh/authorized_keys), the private key stored as a GitHub secret.
Store SSH credentials in GitHub Secrets
You need the GitHub Actions runner to SSH into your server. Use a dedicated deploy key (or a key pair used only for this):
- Generate an SSH key pair (if you don’t have one):
ssh-keygen -t ed25519 -C "github-actions-deploy" -f deploy_key -N "" - Add the public key to the server: append
deploy_key.pubto~/.ssh/authorized_keyson the deploy user’s account. - In the GitHub repo: Settings → Secrets and variables → Actions → New repository secret.
- Add these secrets (use uppercase with underscores; names are referenced in the workflow):
- SSH_PRIVATE_KEY – contents of the private key file (the whole file, including
-----BEGIN ... KEY-----and-----END ... KEY-----). - SSH_USER – SSH username on the server (e.g.
deployorubuntu). - SSH_HOST – server hostname or IP (e.g.
example.comor203.0.113.10).
- SSH_PRIVATE_KEY – contents of the private key file (the whole file, including
The workflow will use these to run rsync over SSH.
Example workflow: build Hugo and deploy with rsync
Create .github/workflows/hugo-deploy.yml in your repo (adjust the branches and deploy path to match your setup):
| |
What each part does
- on.push.branches – runs the workflow when you push to
master(change tomainif that’s your default branch). - Checkout – clones the repo and submodules (needed for Hugo themes as submodules);
fetch-depth: 0helps Hugo’s.Lastmodetc. - Setup Hugo – installs Hugo so the
hugocommand is available. - Clean public – removes any existing
public/so the build is fresh. - Build – runs
hugo -D(drafts included; drop-Dfor production). - Install SSH key – loads
SSH_PRIVATE_KEYinto the runner’s~/.ssh. Theknown_hostsvalue in the action can be a placeholder if you add the host in the next step. - Add known hosts – runs
ssh-keyscan -H ${{ secrets.SSH_HOST }}and appends the result to~/.ssh/known_hostsso the first SSH/rsync connection doesn’t prompt for host key verification. - Deploy – rsyncs
./public/to the server.--deleteremoves files on the server that no longer exist inpublic/. Change/var/www/citizix/to your actual web root.
If you prefer not to use the “Add known hosts” step, you can precompute the host key and store it in a secret (e.g. SSH_KNOWN_HOSTS) and write it to ~/.ssh/known_hosts in the workflow; using ssh-keyscan in the job is simpler and keeps the host key in sync with the server.
Verifying
- After a push to the configured branch, open the repo’s Actions tab and confirm the workflow run succeeds.
- On the server, check that
/var/www/citizix/(or your path) contains the updated static files and that the site loads correctly in a browser.
Summary
You set up a GitHub Actions workflow that builds a Hugo site and deploys it with rsync over SSH. You stored the private key, SSH user, and host in GitHub Secrets, and used the Install SSH key action plus ssh-keyscan so the runner can connect without host key prompts. Adjust branches, deploy path, and hugo flags to match your environment. For other deployment targets (e.g. GitHub Pages or GKE), you can reuse the same checkout-and-build steps and swap the deploy step for the appropriate action or CLI.