Setup CI/CD and host a Hugo website with Cloudflare
A personal website is a good fit for static hosting. Hugo turns configuration, templates, and Markdown into HTML, while Cloudflare Pages builds and serves the result at the edge. Connecting Pages to Git also provides a small CI/CD pipeline without maintaining a deployment server or workflow runner.
The finished workflow is straightforward:
- Develop and preview the Hugo site locally.
- Push a branch to GitHub or GitLab.
- Cloudflare builds a preview deployment for the branch or pull request.
- Merge into
mainto deploy the production site.
Prepare the Hugo site
Install Hugo on macOS with Homebrew:
brew install hugo
hugo version
Create a site if one does not already exist:
hugo new site my-site
cd my-site
Add a theme and configure it in hugo.toml. Set baseURL to the domain that will eventually serve the production site:
baseURL = "https://example.com/"
title = "My Personal Website"
theme = "<theme-name>"
Create a post with the site archetype:
hugo new content posts/hello-world.md
New posts are usually drafts. Change draft to false in the post front matter when the content is ready for production.
Preview the site, including drafts, at http://localhost:1313/:
hugo server --buildDrafts
Before pushing, run the same kind of production build that Cloudflare will run:
hugo --gc --minify --cleanDestinationDir
Hugo writes the generated site to public/. This directory is a build artifact and should not be edited manually or committed as the source of the website.
Push the source to Git
Cloudflare Pages supports GitHub and GitLab repositories. Create a repository, commit the Hugo source, and push the production branch:
git add .
git commit -m "Create Hugo site"
git branch -M main
git remote add origin <repository-url>
git push -u origin main
If the theme is a Git submodule, commit the .gitmodules file and the submodule reference. Cloudflare needs both to fetch the theme during a build.
Create the Cloudflare Pages project
In the Cloudflare dashboard:
- Open Workers & Pages.
- Select Create application.
- Select Pages, then Import an existing Git repository.
- Authorize the Git provider and select the website repository.
- Set the production branch to
main.
Use these build settings:
| Setting | Value |
|---|---|
| Framework preset | Hugo |
| Build command | hugo --gc --minify -b "$CF_PAGES_URL" |
| Build output directory | public |
| Root directory | / |
CF_PAGES_URL is supplied automatically by Cloudflare. Passing it to Hugo gives each preview deployment URLs that point at that preview instead of the production domain.
In Settings > Environment variables, add HUGO_VERSION and pin it to the version used locally. For this site, that value is:
HUGO_VERSION=0.164.0
Add the variable to both the production and preview environments. Pinning the version prevents an automatic Hugo upgrade from changing or breaking a build unexpectedly.
Select Save and Deploy. Cloudflare clones the repository, runs Hugo, uploads public/, and assigns the project a *.pages.dev address.
Understand the CI/CD flow
The Git integration is the pipeline:
- A push to a non-production branch creates a preview deployment.
- A pull request receives a unique preview URL for review.
- A push or merge to
maincreates a production deployment. - A failed Hugo command stops the deployment because it returns a non-zero exit code.
This keeps generated files out of Git. The repository stores the source, and every deployment is rebuilt from a specific commit. Cloudflare also retains deployment history, so an earlier successful deployment can be rolled back from the project dashboard.
Connect a custom domain
Open the Pages project, select Custom domains, and choose Set up a domain. Enter the domain or subdomain that should serve the site.
If the domain already uses Cloudflare DNS, Cloudflare can create the required DNS record automatically. Otherwise, follow the displayed DNS instructions at the current provider. Once validation completes, Cloudflare provisions HTTPS automatically.
After the custom domain is active, update baseURL in hugo.toml to that HTTPS URL if it still contains a placeholder. Push the change to main and verify the new production deployment.
A normal publishing cycle
Publishing a post now uses the same review and deployment path as any code change:
hugo new content posts/my-new-post.md
hugo server --buildDrafts
git switch -c post/my-new-post
git add content/posts/my-new-post.md
git commit -m "Add my new post"
git push -u origin post/my-new-post
Open a pull request, review the Cloudflare preview, mark the post as published, and merge it. Cloudflare rebuilds the site and promotes the result to production without an SSH session, Nginx configuration, or manual file copy.