Deploy a Hugo site
Publish a Hugo site on your own domain, from your computer or from GitHub Actions.
| Build command | hugo build --gc --minify |
| Output folder | public |
| Settings you need | baseURL set to your domain |
| Routing | Hugo’s default pretty URLs (/about/ → about/index.html) work as they are |
Before you begin
Section titled “Before you begin”You need:
- A Hugo site that builds on your computer with
hugo build. - An Evolving Edge account that can deploy workloads. See Getting Started.
- A project in the portal, and its project ID (it starts with
proj_). - A deploy token from the portal, under Workload → Deploy Tokens, with
workload:write. Scope it to the project. See Deploy tokens. - The domain you want to serve the site on, for example
www.example.com, and access to its DNS settings. - Node.js 22.12 or later, on Windows (x64), macOS, or Linux (x64 or arm64), to deploy from your computer. The deploy tool is an npm package; Hugo itself doesn’t need Node.js, and the GitHub Action below brings its own.
1. Set your base URL
Section titled “1. Set your base URL”Hugo writes absolute URLs into your sitemap, RSS feeds and canonical links from baseURL, so set it to your real domain, with a trailing slash:
baseURL = "https://www.example.com/"Your site is served from the root of the domain. If you’re moving from a GitHub Pages project site (https://you.github.io/repo/), drop the /repo/ path.
2. Claim your domain
Section titled “2. Claim your domain”Only the project that claimed a domain can deploy to it, so claim it before your first deploy. In the portal, or from the command line:
export EE_CDN_TOKEN=<your deploy token>npx -p @evolving-edge/ee-cdn-cli ee-domain claim www.example.com --project proj_abc123 --waitIt prints a TXT record and a CNAME record to add at your DNS provider, then waits until the domain is active. See Custom domains for the portal steps, root domains and Cloudflare.
3. Deploy from your computer
Section titled “3. Deploy from your computer”Build the site, then deploy the public folder:
hugo build --gc --minifynpx @evolving-edge/ee-cdn-cli public --domain www.example.com --project proj_abc123 --trailing-slash--trailing-slashmakes/aboutredirect to/about/, where Hugo put the page. Hugo’s own links already end in a slash; this catches links typed or copied without one.ee-deployfindspublicby itself if you leave the folder out.- Each deploy replaces the whole site on that domain. Allow up to about 90 seconds for a new deploy to reach every node.
4. Deploy from GitHub Actions
Section titled “4. Deploy from GitHub Actions”In your repository settings, add:
- a secret
EE_CDN_TOKENholding your deploy token - variables
EE_DOMAIN(for examplewww.example.com) andEE_CDN_PROJECT_ID
Then create .github/workflows/deploy.yml:
name: Deploy to Evolving Edge
on: push: branches: [main] workflow_dispatch:
permissions: contents: read
concurrency: group: ee-deploy cancel-in-progress: false
jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: submodules: recursive # if your theme is a Git submodule fetch-depth: 0 # for .GitInfo and .Lastmod
- uses: peaceiris/actions-hugo@v3 with: hugo-version: '0.167.0' extended: true
- name: Build run: hugo build --gc --minify --baseURL "https://${{ vars.EE_DOMAIN }}/"
- name: Deploy uses: evolving-edge/ee-cdn-cli@v0.4.0 with: directory: public domain: ${{ vars.EE_DOMAIN }} project: ${{ vars.EE_CDN_PROJECT_ID }} token: ${{ secrets.EE_CDN_TOKEN }} trailing-slash: trueThe Action brings its own Node.js, so the workflow needs no setup-node step. If your site uses Dart Sass or npm packages, add those steps before Build, as in Hugo’s own GitHub Pages workflow. To keep a new release of the packaging tool from changing your deploys, pin it with builder-version; see The GitHub Action.
Redirects, 404 page and caching
Section titled “Redirects, 404 page and caching”Redirects. Put a _redirects file in static/; Hugo copies it to the site root:
/old-post/ /posts/new-post/ 301/blog/* /posts/:splat 301Rules can be 301 or 302, and * and :splat wildcards work. Redirects apply to public (Level 0) sites. Hugo’s own aliases keep working too, as HTML redirect pages.
404 page. A layouts/404.html template builds public/404.html. Evolving Edge serves it, with a 404 status, for any address that doesn’t exist. Most themes include one. See Custom 404 pages.
Caching. HTML pages are checked on every request, so a new deploy shows as soon as it reaches the nodes. Other files are cached for an hour. Hugo mixes fingerprinted files with the files static/ copies, so no folder is safe to cache for a year by default. To cache fingerprinted files for a year, keep them in a folder of their own and add the _headers rule in Caching hashed assets for a year.
What doesn’t carry over
Section titled “What doesn’t carry over”- Subfolder deploys. A site owns its whole domain; there’s no equivalent of a GitHub Pages project path.
hugo deploy. Its targets are cloud storage buckets. Useee-deployor the Action instead.- Deploy previews for each pull request. To preview, deploy to a second domain.
Troubleshooting
Section titled “Troubleshooting”| What you see | Why | What to do |
|---|---|---|
/about is a 404 but /about/ works |
The trailing-slash redirect isn’t on | Deploy with --trailing-slash (or trailing-slash: true in the Action) |
Sitemap or RSS links point at localhost or example.org |
baseURL wasn’t set for the production build |
Set it in hugo.toml, or pass --baseURL as in the workflow |
| Pages load without styles after moving from GitHub Pages | baseURL still includes the old /repo/ path |
Set baseURL to the domain root |
| The deploy is refused for your domain | The domain isn’t claimed for this project | Claim it first |
No deploy token |
EE_CDN_TOKEN isn’t set |
Export it, or add the repository secret |
| An authorisation error on upload | The token is scoped to a project and no project was given | Pass --project (or set EE_CDN_PROJECT_ID) |
More in the command line troubleshooting.
