Deploy a Private Hugo Website to GitHub Pages with GitHub Actions and Deploy Keys
What You’ll Build
By the end of this tutorial, you will have:
- a private Hugo source repository;
- a public GitHub Pages repository;
- an automated GitHub Actions deployment workflow;
- an SSH deploy key for secure deployments;
- a fully automated Hugo website published on GitHub Pages.
For example:
https://username.github.io/my-awesome-site/
Official Documentation
This tutorial is based on the excellent documentation provided by Peaceiris.
The official documentation covers a variety of deployment scenarios, including:
- GitHub Pages;
- external repositories;
- deploy keys;
- GitHub tokens;
- custom domains.
It also includes screenshots and additional configuration examples that may be helpful if this is your first time setting up GitHub Pages.
If you’re looking for more advanced configurations, I highly recommend reading it alongside this tutorial.
https://github.com/peaceiris/actions-gh-pages
Why Another Tutorial?
The official documentation is comprehensive and supports many different deployment scenarios.
This article focuses on one specific setup that I use for my own Hugo websites:
- a private Hugo source repository;
- a public GitHub Pages repository;
- deployment using an SSH deploy key;
- GitHub Actions for fully automated publishing.
By focusing on a single deployment strategy, the setup becomes much easier to follow.
Why Use Two Repositories?
I prefer to keep my Hugo source code private. My private repository contains:
- all Markdown files;
- Hugo configuration files;
- themes and layouts;
- draft articles;
- GitHub Actions workflows.
The public repository only contains the generated website files.
The setup looks like this:
Private Hugo repository
│
▼
GitHub Actions
│
▼
Hugo build (public/)
│
▼
Deploy using an SSH deploy key
│
▼
Public GitHub Pages repository
│
▼
https://username.github.io/my-awesome-site/
Why Use a Deploy Key?
There are several ways to publish to another repository:
- Personal Access Token (PAT);
- GitHub Token;
- SSH Deploy Key.
For a GitHub Pages repository, I prefer using an SSH Deploy Key. The key only grants access to the public repository and does not require permissions for any of my other repositories.
Why Not Use the Official GitHub Pages Workflow?
GitHub’s official GitHub Pages workflow works great when the Hugo source code and the published website live in the same repository.
In this tutorial, we intentionally separate both repositories:
- the Hugo source code remains private;
- the published website lives in a public repository.
Deploy keys and the Peaceiris GitHub Actions make this workflow straightforward and easy to maintain.
Step 1 - Create the Public Repository
Create a new public GitHub repository that will host the generated Hugo website.
I recommend enabling the following option when creating the repository:
Add a README file
This ensures that the main branch exists immediately.
For this tutorial, we’ll assume the repository is named:
my-awesome-site
The final GitHub Pages URL will therefore become:
https://username.github.io/my-awesome-site/
Step 2 - Generate an SSH Deploy Key
Generate a new SSH key locally:
ssh-keygen -t ed25519 -C "github-pages-deploy" -f gh-pages -N ""
This command creates an SSH key pair without a passphrase.
The generated files are:
gh-pages
gh-pages.pub
The public key (gh-pages.pub) is safe to share with GitHub.
The private key (gh-pages) must never be shared publicly and will only be used as a GitHub Actions secret.
Note: The Peaceiris documentation uses an RSA key in its examples. In this tutorial, we use an ed25519 key instead because it is modern, compact and perfectly suited for GitHub deploy keys.
Step 3 - Add the Public Deploy Key
Navigate to your public repository:
Settings
→ Deploy keys
→ Add deploy key
Open the gh-pages.pub file in your favourite text editor. You will see a single line that looks similar to this:
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA.... github-pages-deploy
Copy the entire line and paste it into the Key field.
Do not remove:
- the
ssh-ed25519prefix; - the encoded key itself;
- the
github-pages-deploycomment.
The entire line is required.
Finally, make sure to enable:
Allow write access
Step 4 - Add the Private Deploy Key Secret
Navigate to your private Hugo repository:
Settings
→ Secrets and variables
→ Actions
→ New repository secret
Create a repository secret named:
ACTIONS_DEPLOY_KEY
Open the gh-pages file. Its contents will look similar to this:
-----BEGIN OPENSSH PRIVATE KEY-----
...
-----END OPENSSH PRIVATE KEY-----
Copy the entire contents of the file, including:
-----BEGIN OPENSSH PRIVATE KEY-----
and:
-----END OPENSSH PRIVATE KEY-----
Paste everything into the value field of the ACTIONS_DEPLOY_KEY secret.
Never share the contents of this file publicly.
Step 5 - (Optional) Configure Repository Variables
The deployment username, email address and public repository name are not secret information. GitHub Actions repository variables are therefore a good place to store them.
Navigate to:
Settings
→ Secrets and variables
→ Actions
→ Variables
Create the following variables:
DEPLOY_USER_NAME
DEPLOY_USER_EMAIL
PUBLIC_REPOSITORY
For example:
DEPLOY_USER_NAME
john-doe
DEPLOY_USER_EMAIL
john@example.com
PUBLIC_REPOSITORY
username/my-awesome-site
Step 6 - Create the GitHub Actions Workflow
Create the following file:
.github/workflows/deploy.yml
Add the following workflow:
name: Deploy Hugo site to GitHub Pages
on:
push:
branches:
- main
workflow_dispatch:
permissions:
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout source
uses: actions/checkout@v4
with:
submodules: recursive
fetch-depth: 0
- name: Setup Hugo
uses: peaceiris/actions-hugo@v3
with:
hugo-version: "0.148.2"
extended: true
- name: Build Hugo site
run: hugo --gc --minify
- name: Deploy website
uses: peaceiris/actions-gh-pages@v4
with:
deploy_key: ${{ secrets.ACTIONS_DEPLOY_KEY }}
external_repository: ${{ vars.PUBLIC_REPOSITORY }}
publish_branch: main
publish_dir: ./public
user_name: ${{ vars.DEPLOY_USER_NAME }}
user_email: ${{ vars.DEPLOY_USER_EMAIL }}
The deployment action uses peaceiris/actions-gh-pages@v4.
The separate peaceiris/actions-hugo@v3 action is only responsible for installing Hugo during the workflow.
If you prefer, you can replace the ${{ vars.* }} values with your own configuration directly in the workflow.
Step 7 - Configure the Hugo baseURL
If you are publishing your website as a GitHub Pages project site, make sure that the baseURL is configured correctly.
For example:
baseURL = "https://username.github.io/my-awesome-site/"
Without the correct baseURL, links, stylesheets and images may not work as expected.
Step 8 - Push Your First Commit
Commit and push your Hugo website to the main branch of your private repository.
Once the GitHub Actions workflow completes successfully, the generated website files should automatically appear in your public repository.
Step 9 - Verify the Public Repository Contents
Verify that the deployment was successful.
Your public repository should now contain files similar to:
index.html
404.html
sitemap.xml
robots.txt
posts/
css/
images/
The exact contents will depend on your Hugo theme and configuration.
Step 10 - Enable GitHub Pages
Now that the public repository contains the generated website files, you can enable GitHub Pages.
Navigate to:
Settings
→ Pages
Configure GitHub Pages as follows:
Source:
Deploy from a branch
Branch:
main
Folder:
/ (root)
GitHub Pages will now automatically publish the contents of your public repository.
Step 11 - Verify the Deployment
Finally, verify that:
- the GitHub Actions workflow completed successfully;
- the public repository contains the generated website files;
- GitHub Pages is enabled;
- your website is reachable.
For example:
https://username.github.io/my-awesome-site/
Conclusion
This setup has several advantages:
- your Hugo source code remains private;
- draft articles are never published;
- only the generated HTML, CSS and JavaScript files are publicly available;
- the SSH deploy key has minimal permissions;
- GitHub Pages deployment is fully automated;
- the generated site can be published as a GitHub Pages project site.
For a personal Hugo website, this is a simple and secure way to combine a private source repository with a public GitHub Pages repository.