This is the multi-page printable view of this section. .

Return to the regular view of this page.

Deployment

Build Oink once, publish its static output, and verify the result.

OINK deployment has two separate stages: Hugo builds a complete public/ directory, then a static host publishes that directory. Keep build verification and hosted verification distinct so a successful local command is not mistaken for a completed production release.

Production build

Run the pinned Hugo Extended version from the site root:

hugo --gc --minify --cleanDestinationDir

--gc removes unused cached resources, --minify produces production assets, and --cleanDestinationDir removes files left by an earlier build. Review the command before using the last option when publishDir points anywhere other than the site’s dedicated output directory.

The build must finish without warnings that hide missing content, endpoints, or resources. Inspect public/ locally before uploading it.

Local preview

For editing:

hugo server --disableFastRender

Hugo’s development server proves that the source can render. It is not a production host and its live-reload behavior is not part of the generated site. Always run a clean production build before release.

Build environments and indexing

The plain hugo command uses the production environment. Oink marks HTML from production builds as indexable and uses optimized, fingerprinted assets. For a public preview that search engines must not index, build with another environment:

hugo --environment preview --baseURL "https://preview.example.com/"

Oink emits noindex, nofollow outside production. A hosting-level X-Robots-Tag header is useful defense in depth, especially for non-HTML files. Rebuild the reviewed source in production before publishing it at the canonical URL; a preview artifact is not a production artifact.

Static hosting

Any host that can serve directories and files can publish OINK:

  • object storage and a CDN;
  • GitHub Pages, GitLab Pages, or similar Git-driven static hosting;
  • Netlify, Cloudflare Pages, or another build-and-publish platform;
  • an Nginx, Caddy, Apache, or internal file server.

Set baseURL to the canonical production URL. If the site is published below a path such as https://example.com/manual/, include that path and test it; OINK’s local assets and component URLs are designed to preserve subpath deployments.

Cloudflare Pages

Connect Pages directly to the source branch. OINK does not require a GitHub Actions workflow that prebuilds and pushes an orphan Pages branch.

Use these settings for an Oink site:

Setting Value
Production branch main, or the reviewed source branch
Root directory the standalone site directory
Build command hugo --gc --minify
Build output directory public
HUGO_VERSION 0.164.0
SKIP_DEPENDENCY_INSTALL 1

As of 2026-08-08, Cloudflare Pages’ v3 build image documents Hugo 0.147.7 as its default, below OINK’s minimum 0.160.1. Set HUGO_VERSION explicitly for both Production and Preview rather than relying on the moving platform default. SKIP_DEPENDENCY_INSTALL=1 prevents the platform’s generic dependency installer from adding a frontend installation step that the site does not need.

For previews that need the generated Pages URL as their canonical build URL:

hugo --gc --minify --baseURL "$CF_PAGES_URL"

Cloudflare documents public as Hugo’s standard output directory, the HUGO_VERSION override, and the CF_PAGES_URL base-URL pattern. Recheck the platform documentation when changing the build image or pinned Hugo version.

See the Cloudflare Hugo guide and Cloudflare build image reference.

Air-gap deployment

For a disconnected environment, transfer both the site source and a verified theme archive rather than depending on an initial Hugo Module download:

  1. verify the theme archive’s sidecar SHA-256 file;
  2. install a supported Hugo Extended binary inside the environment;
  3. extract the theme into the site’s themes/oink/ directory;
  4. set theme: oink and run hugo --gc --minify in the site;
  5. publish public/ to the internal static server.

Keep PlantUML and Diagrams.net disabled unless a reachable internal endpoint is configured. External links and embeds remain the content author’s responsibility.

Headers and caching

Fingerprint-bearing CSS and JavaScript can use long-lived immutable caching. HTML, search indexes, feeds, and sitemaps should use shorter caching or revalidation so a new deployment becomes visible promptly.

Hosts that recognize the _headers convention can use a site-owned static/_headers file. This is not a portable standard; review security headers against the site’s actual inline content and integrations.

Preview and production URLs

Canonical, hreflang, Open Graph, feeds, and absolute links depend on baseURL. A production build should use the production URL; a preview may use its temporary URL when link validation or social metadata needs to be accurate.

Do not publish a preview build to production without rebuilding against the canonical URL. Conversely, do not reject a preview merely because it contains the intentionally supplied preview host.

Deployment acceptance

Verify each layer independently:

Source and configuration

  • the expected commit and pinned theme version are present;
  • baseURL, languages, menus, repository metadata, and optional endpoints are correct;
  • no unpublished draft or secret enters the public content tree.

Build artifact

  • a clean production build succeeds with the pinned Hugo Extended version;
  • English, Chinese, feeds, sitemaps, search indexes, and 404.html are present;
  • local assets resolve under both root and configured subpath;
  • the artifact contains required license and attribution surfaces.

Hosted site

  • the production URL returns the new artifact;
  • canonical and language-alternate URLs use the production origin;
  • navigation, search, language switching, dark mode, print, and representative components work in a real browser;
  • redirects, custom headers, cache policy, and 404 handling behave as configured;
  • an air-gap claim is backed by a browser network audit.

A green build log completes only the artifact stage. Deployment is complete after the hosted checks pass.

Rollback

Keep the previous known-good static artifact or hosting deployment identifier. If a new release fails hosted validation, restore that artifact first, then diagnose source or platform behavior. Rebuilding an old source commit with a new unpinned toolchain is not equivalent to restoring the original artifact.

1 - Serving your site locally

Preview content locally with Hugo’s development server.

Depending on your deployment choice you may want to serve your site locally during development to preview content changes. To serve your site locally:

  1. Ensure you have an up to date local copy of your site files cloned from your repo.

  2. Ensure Hugo Extended and any source-fetch tools required by your chosen installation method are installed, as described in Prerequisites and installation. Node.js and PostCSS are not site-build prerequisites.

  3. Run the hugo server command in your site root. By default your site will be available at http://localhost:1313.

Now that you’re serving your site locally, Hugo will watch for changes to the content and automatically refresh your site. If you have more than one local git branch, when you switch between git branches the local website reflects the files in the current branch.

2 - Deployment on GitHub Pages

Build and publish an Oink site with GitHub Actions and Pages.

If your source is hosted on GitHub, GitHub Pages can build and publish the site with a single Actions workflow. The consuming site needs Hugo Extended but does not need Node.js, npm, PostCSS, or a generated deployment branch.

Project sites use a URL such as https://<OWNER>.github.io/<REPOSITORY>/; user and organization sites use https://<OWNER>.github.io/. Custom domains are also supported.

Prepare the repository

Push the complete site source to GitHub and confirm that this command succeeds from the repository root:

hugo --gc --minify

Set the site’s baseURL to its production URL, or pass the Pages URL with Hugo’s --baseURL option in the workflow. A project site must include the repository path; otherwise CSS, JavaScript, and other resources will resolve from the wrong location.

Add the Pages workflow

Create .github/workflows/pages.yml with the following contents. Keep HUGO_VERSION aligned with a version validated by the theme.

name: Deploy Hugo site to Pages

on:
  push:
    branches: [main]
  workflow_dispatch:

permissions:
  contents: read
  pages: write
  id-token: write

concurrency:
  group: pages
  cancel-in-progress: false

env:
  GO_VERSION: 1.25.5
  HUGO_VERSION: 0.164.0

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
        with:
          fetch-depth: 0
          submodules: recursive
      - uses: actions/setup-go@v6
        with:
          go-version: ${{ env.GO_VERSION }}
      - name: Install Hugo Extended
        run: |
          curl -L -o hugo.deb \
            "https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.deb"
          sudo dpkg -i hugo.deb
      - uses: actions/configure-pages@v6
        id: pages
      - name: Build
        run: >-
          hugo --gc --minify --baseURL "${{ steps.pages.outputs.base_url }}/"
      - uses: actions/upload-pages-artifact@v5
        with:
          path: public

  deploy:
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    runs-on: ubuntu-latest
    needs: build
    steps:
      - name: Deploy
        id: deployment
        uses: actions/deploy-pages@v5

If the theme is installed as a Git submodule, submodules: recursive checks it out before Hugo runs. A complete offline archive can instead commit or restore the site-owned themes/oink/ directory as part of the repository or build input.

Enable GitHub Pages

In the repository settings, open Pages. Under Build and deployment, set Source to GitHub Actions. Push the workflow to main, then follow its first run in the repository’s Actions tab.

The workflow uploads only the generated public/ directory and publishes it through the Pages deployment API. It does not maintain a gh-pages branch.

For other authentication, domain, and permission options, see GitHub’s Pages documentation and Hugo’s GitHub hosting guide.

3 - Deployment on Cloudflare Pages

Build and publish an Oink site with Cloudflare Pages.

Cloudflare Pages can build an Oink site from a connected GitHub or GitLab repository and create preview deployments for reviewed branches. The consumer build runs Hugo Extended directly and does not need a frontend package install.

Configure the project

Import the repository in Workers & Pages, select the production branch, and use these settings:

Setting Value
Production branch main, or the reviewed source branch
Build command hugo --gc --minify
Build output directory public
HUGO_VERSION 0.164.0
SKIP_DEPENDENCY_INSTALL 1

Set HUGO_VERSION in both the Production and Preview environments. Cloudflare Pages’ v3 build image currently defaults to Hugo 0.147.7, which is below Oink’s minimum 0.160.1. Pinning the validated version prevents a build-image update from silently changing the toolchain. SKIP_DEPENDENCY_INSTALL=1 disables the generic dependency installation step that an Oink consumer does not use.

If the Hugo site lives below the repository root, set Root directory to that directory. The output value is relative to the selected root.

Set the base URL

Production builds should use the site’s canonical custom domain in baseURL. For a preview that needs its generated Pages URL in canonical and absolute links, use Cloudflare’s deployment URL:

hugo --gc --minify --baseURL "$CF_PAGES_URL"

Do not publish that preview artifact as production without rebuilding it for the canonical origin.

Deploy and verify

Save the configuration and inspect the first build log. A normal Oink consumer build should run Hugo without npm, PostCSS, Autoprefixer, or theme-owned CDN downloads. After deployment, verify:

  • the *.pages.dev preview or custom domain serves the expected commit;
  • English and translated routes use the intended canonical origin;
  • search, language switching, dark mode, print, and representative components work;
  • redirects, headers, custom domains, and 404 behavior match the Pages project configuration.

Cloudflare Git integration and Direct Upload are different project modes. Check the current Pages documentation before choosing a mode that must later support an external deployment pipeline.

4 - Deployment on Netlify

Deploying your OINK site on Netlify with Hugo alone.

Netlify can build a site from GitHub, GitLab, or Bitbucket and publish a preview for each pull request. An OINK consumer build runs Hugo Extended directly; it does not install Node.js packages or invoke PostCSS.

Configure the site

Push the complete source to your Git provider, import the repository in Netlify, and use these build settings:

Setting Value
Build command hugo --gc --minify
Publish directory public
HUGO_VERSION 0.164.0 or another theme-validated version

If Netlify detects package manifests that exist only for theme-maintainer tooling, disable automatic dependency installation for the site. They are not part of the consumer build contract.

For a theme installed as a Git submodule, enable recursive submodule checkout. For a Hugo module, Netlify also needs the normal Git and Go access required to download the pinned module on a clean build. A complete offline distribution uses the site-owned themes/oink/ directory and avoids that first-build download.

Keep configuration in the repository

The same settings can be committed as netlify.toml:

[build]
command = "hugo --gc --minify"
publish = "public"

[build.environment]
HUGO_VERSION = "0.164.0"

Keep production and deploy-preview contexts on the same Hugo version unless a preview is intentionally testing an upgrade. If preview builds need their generated URL as the base URL, add Netlify’s deploy URL to the Hugo command for that context.

To prevent a non-production deployment from being indexed, use a non-production Hugo environment as described in Build environments and indexing.

After saving the settings, trigger a deploy and inspect the build log. A normal consumer build should show one Hugo command and no npm, PostCSS, Autoprefixer, CDN download, or build-time remote-resource step.

5 - Deployment with Amazon S3 and CloudFront

Publish Oink output with Amazon S3 and CloudFront.

There are several options for publishing your web site using Amazon Web Services. This section describes the most basic option, deploying your site using an S3 bucket and activating the CloudFront CDN (content delivery network) to speed up the delivery of your deployed contents.

  1. After your registration at AWS, create your S3 bucket, connect it with your domain, and add it to the CloudFront CDN. This blog post has all the details and provides easy to follow step-by-step instructions for the whole procedure.

  2. Download and install the latest version 2 of the AWS Command Line Interface (CLI). Then configure your CLI instance by issuing the command aws configure (make sure you have your AWS Access Key ID and your AWS Secret Access Key at hand):

    $ aws configure
    AWS Access Key ID [None]: AKIAIOSFODNN7EXAMPLE
    AWS Secret Access Key [None]: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
    Default region name [None]: eu-central-1
    Default output format [None]:
    
  3. Check the proper configuration of your AWS CLI by issuing the command aws s3 ls, this should output a list of your S3 bucket(s).

  1. Inside your hugo.toml/hugo.yaml/hugo.json, add a [deployment] section like this one:

    [deployment]
    [[deployment.targets]]
    name = "aws"
    URL = "s3://www.your-domain.tld"
    cloudFrontDistributionID = "E9RZ8T1EXAMPLEID"
    deployment:
      targets:
        - name: aws
          URL: 's3://www.your-domain.tld'
          cloudFrontDistributionID: E9RZ8T1EXAMPLEID
    {
      "deployment": {
        "targets": [
          {
            "name": "aws",
            "URL": "s3://www.your-domain.tld",
            "cloudFrontDistributionID": "E9RZ8T1EXAMPLEID"
          }
        ]
      }
    }
  1. Run the command hugo --gc --minify to render the site’s assets into the public/ directory of your Hugo build environment.

  2. Use Hugo’s built-in deploy command to deploy the site to S3:

    hugo deploy
    Deploying to target "aws" (www.your-domain.tld)
    Identified 77 file(s) to upload, totaling 5.3 MB, and 0 file(s) to delete.
    Success!
    Invalidating CloudFront CDN...
    Success!
    

    As you can see, issuing the hugo deploy command automatically invalidates your CloudFront CDN cache.

  3. That’s all you need to do! From now on, you can easily deploy to your S3 bucket using Hugo’s built-in deploy command!

For more information about the Hugo deploy command, including command line options, see this synopsis. In particular, you may find the --maxDeletes int option or the --force option (which forces upload of all files) useful.

If S3 does not meet your needs, consider AWS Amplify Console. This is a more advanced continuous deployment (CD) platform with built-in support for the Hugo static site generator. A starter can be found in Hugo’s official docs.