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-ed25519 prefix;
  • the encoded key itself;
  • the github-pages-deploy comment.

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.