Skip to content

Deploying Hugo

Hugo is an incredibly fast, flexible, and modern static site generator written in Go. It delivers websites in milliseconds and supports a powerful templating system, content organization, theme ecosystem, and advanced features like multilingual support, taxonomies, and custom outputs. Hugo is ideal for building blogs, documentation sites, portfolios, marketing websites, knowledge bases, and any content-driven project where performance and developer experience are critical. With Hugo, you get lightning-fast build times, excellent developer workflow, and production-ready websites with minimal configuration.

This comprehensive guide walks through deploying a Hugo site to Klutch.sh using either Nixpacks (automatic zero-configuration deployment) or a Dockerfile (manual container control). You’ll learn how to scaffold a Hugo project, install and configure themes, organize content, create custom templates, optimize images, set up environment variables, implement security best practices, set up monitoring, configure custom domains, and troubleshoot common issues. By the end of this guide, you’ll have a production-ready Hugo site running on Klutch.sh’s global infrastructure with automatic HTTPS, blazing-fast performance, and reliable hosting.

Prerequisites

  • Hugo (version 0.120+) – Installation Guide
  • Go (optional, only if building Hugo from source) – Download Go
  • Git installed locally and a GitHub account (Klutch.sh uses GitHub as the only git source)
  • Klutch.sh account with access to the dashboard at klutch.sh/app
  • Basic knowledge of Markdown, YAML, and static site generation concepts

Getting Started: Create a Hugo Site

1. Install Hugo

Install Hugo using your package manager or download a binary:

Terminal window
# macOS with Homebrew
brew install hugo
# Ubuntu/Debian with apt
sudo apt-get install hugo
# Windows with Chocolatey
choco install hugo
# Or download directly from releases
# https://github.com/gohugoio/hugo/releases

Verify the installation:

Terminal window
hugo version

2. Create a New Hugo Site

Create a new Hugo site:

Terminal window
hugo new site my-hugo-site
cd my-hugo-site

3. Project Structure

A typical Hugo site structure looks like:

my-hugo-site/
├── archetypes/
│ └── default.md
├── assets/
│ ├── css/
│ │ └── custom.css
│ └── images/
│ └── logo.png
├── content/
│ ├── _index.md
│ ├── about.md
│ ├── blog/
│ │ ├── _index.md
│ │ ├── first-post.md
│ │ └── second-post.md
│ └── projects/
│ ├── _index.md
│ └── my-project.md
├── layouts/
│ ├── _default/
│ │ ├── baseof.html
│ │ ├── home.html
│ │ ├── list.html
│ │ └── single.html
│ ├── partials/
│ │ ├── header.html
│ │ ├── footer.html
│ │ └── nav.html
│ └── shortcodes/
│ └── custom.html
├── static/
│ ├── robots.txt
│ └── favicon.ico
├── themes/
│ └── [theme-name]
├── hugo.toml
├── hugo.yaml
├── hugo.json
└── Dockerfile

4. Install and Configure a Theme

Install a theme. Hugo has hundreds of community themes. Let’s use the popular “Ananke” theme:

Terminal window
git init
git submodule add https://github.com/theNewDynamic/gohugo-theme-ananke.git themes/ananke

5. Configure Your Site

Update your hugo.toml (or hugo.yaml / hugo.json):

baseURL = "https://example-app.klutch.sh"
languageCode = "en-us"
title = "My Hugo Site"
theme = "ananke"
[params]
sitename = "My Hugo Site"
description = "A blazing-fast static site powered by Hugo"
author = "Your Name"
[menu]
[[menu.main]]
name = "Home"
url = "/"
weight = 1
[[menu.main]]
name = "About"
url = "/about/"
weight = 2
[[menu.main]]
name = "Blog"
url = "/blog/"
weight = 3

Or using YAML (hugo.yaml):

baseURL: https://example-app.klutch.sh
languageCode: en-us
title: My Hugo Site
theme: ananke
params:
sitename: My Hugo Site
description: A blazing-fast static site powered by Hugo
author: Your Name
menu:
main:
- name: Home
url: /
weight: 1
- name: About
url: /about/
weight: 2
- name: Blog
url: /blog/
weight: 3

6. Create Content

Create your first blog post:

Terminal window
hugo new blog/hello-klutch.md

Edit the file at content/blog/hello-klutch.md:

---
title: "Hello Klutch.sh"
description: "My first post on Hugo deployed to Klutch.sh"
date: 2024-01-15T10:00:00Z
categories: ["Hugo", "Deployment"]
tags: ["static-site", "klutch"]
draft: false
---
# Welcome to Hugo on Klutch.sh!
This is my first blog post using Hugo. Hugo is incredibly fast and flexible.
## Why Hugo?
- **Speed**: Hugo builds sites in milliseconds
- **Simplicity**: No database required, just Markdown files
- **Flexibility**: Powerful templating system
- **Performance**: Optimized static HTML output
Enjoy!

Create an about page:

Terminal window
hugo new about.md

Edit content/about.md:

---
title: "About"
description: "Learn more about my site"
---
# About Me
Welcome to my site built with Hugo and deployed on Klutch.sh!
I'm using Hugo because it's:
- **Fast**: Builds in milliseconds
- **Simple**: Markdown-based content
- **Reliable**: Perfect for production
Learn more at [gohugo.io](https://gohugo.io).

7. Run the Development Server

Start the Hugo development server:

Terminal window
hugo server

Or for drafts:

Terminal window
hugo server -D

Your site will be available at http://localhost:1313. Hugo automatically reloads on changes.

8. Create Custom Layouts

Create a custom layout at layouts/_default/single.html:

{{ define "main" }}
<article>
<header>
<h1>{{ .Title }}</h1>
<p class="meta">
Published on {{ .Date.Format "January 2, 2006" }}
{{ if .Params.author }}by {{ .Params.author }}{{ end }}
</p>
</header>
<div class="content">
{{ .Content }}
</div>
{{ if .Params.tags }}
<footer>
<ul class="tags">
{{ range .Params.tags }}
<li><a href="{{ "/tags/" | relLangURL }}{{ . | urlize }}">{{ . }}</a></li>
{{ end }}
</ul>
</footer>
{{ end }}
</article>
{{ end }}

Create a partial at layouts/partials/header.html:

<header class="site-header">
<nav>
<a href="{{ "/" | relLangURL }}" class="logo">{{ .Site.Title }}</a>
<ul>
{{ range .Site.Menus.main }}
<li><a href="{{ .URL | relLangURL }}">{{ .Name }}</a></li>
{{ end }}
</ul>
</nav>
</header>

9. Configure Image Processing

Hugo has built-in image processing. Add this to hugo.toml:

[imaging]
quality = 85
resampleFilter = "lanczos"
anchor = "smart"
[output]
home = ["HTML", "JSON"]
section = ["HTML", "JSON"]
page = ["HTML"]
[outputFormats.JSON]
isPlainText = true
mediaType = "application/json"

Use images in your content with Hugo’s image processing:

![My Image](images/photo.jpg)

10. Set Up Environment-Specific Configuration

Create environment-specific config files:

Terminal window
# Production config
# hugo.toml (or rename to hugo/config.toml in a config directory)

Create hugo.production.toml for production-specific settings:

# Production overrides
minify = true
enableRobotsTXT = true
[params]
environment = "production"
analyticsID = "your-analytics-id"
[outputs]
home = ["HTML", "RSS", "JSON"]
section = ["HTML", "JSON"]

11. Build for Production

Build your Hugo site:

Terminal window
hugo --minify

The built site will be in the public/ directory.

12. Test Locally

Test the production build by serving the public directory:

Terminal window
hugo --minify
cd public
python3 -m http.server 8080
# or
npx http-server

Visit http://localhost:8080 to verify the production build.


Local Production Build Test

Before deploying, thoroughly test the production build:

Terminal window
hugo --minify --buildFuture

Verify that:

  • All pages and sections load correctly
  • Navigation works as expected
  • Images load properly
  • No broken links exist
  • Performance is excellent

The production build is in the public/ directory.


Deploying with Nixpacks

Nixpacks automatically detects your Hugo site and configures build and runtime environments without requiring a Dockerfile. This is the simplest deployment method for Hugo.

Prerequisites for Nixpacks Deployment

  • Your Hugo site pushed to a GitHub repository
  • Valid hugo.toml/hugo.yaml/hugo.json configuration
  • No Dockerfile in the repository root (if one exists, Klutch.sh will use Docker instead)

Steps to Deploy with Nixpacks

  1. Push Your Hugo Site to GitHub

    Initialize and push your project to GitHub:

    Terminal window
    git init
    git add .
    git commit -m "Initial Hugo site"
    git branch -M main
    git remote add origin git@github.com:YOUR_USERNAME/YOUR_REPO.git
    git push -u origin main
  2. Log In to Klutch.sh Dashboard

    Go to klutch.sh/app and sign in with your GitHub account.

  3. Create a Project

    Navigate to the Projects section and create a new project for your Hugo site.

  4. Create an App

    Click “Create App” and select your GitHub repository.

  5. Select the Branch

    Choose the branch you want to deploy (typically main).

  6. Configure Traffic Type

    Select HTTP as the traffic type for Hugo (a static site generator).

  7. Set the Internal Port

    Set the internal port to 1313 – this is the default port where Hugo serves sites.

  8. Add Environment Variables (Optional)

    Add any environment variables your Hugo site requires:

    HUGO_ENV=production
    HUGO_ENABLEGITINFO=true
    HUGO_BUILDDIR=public

    If you need to customize the Nixpacks build or start command, use these environment variables:

    • BUILD_COMMAND: Override the default build command (e.g., hugo --minify)
    • START_COMMAND: Override the default start command (e.g., hugo server --bind 0.0.0.0)
  9. Configure Compute Resources

    Select your region, compute size, and number of instances based on expected traffic.

  10. Deploy

    Click “Create” to start the deployment. Nixpacks will automatically build and deploy your Hugo site. Your site will be available at a URL like https://example-app.klutch.sh.


Deploying with Docker

For more control over your deployment environment, you can use a Dockerfile. Klutch.sh automatically detects a Dockerfile in your repository root and uses it for deployment.

Creating a Dockerfile for Hugo

Create a Dockerfile in the root of your Hugo site:

# === Build stage ===
FROM klakegg/hugo:0.124.1-ext-alpine AS builder
WORKDIR /src
COPY . .
RUN hugo --minify
# === Runtime stage ===
FROM nginx:alpine
COPY --from=builder /src/public /usr/share/nginx/html
# Configure Nginx for SPA/static site routing
RUN echo 'server { \
listen 80; \
server_name _; \
root /usr/share/nginx/html; \
index index.html; \
location / { \
try_files $uri $uri/ =404; \
} \
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { \
expires 1y; \
add_header Cache-Control "public, immutable"; \
} \
}' > /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

Alternative Dockerfile Using http-server

For a lightweight alternative using Node.js http-server:

# === Build stage ===
FROM klakegg/hugo:0.124.1-ext-alpine AS builder
WORKDIR /src
COPY . .
RUN hugo --minify
# === Runtime stage ===
FROM node:18-alpine
WORKDIR /app
RUN npm install -g http-server
COPY --from=builder /src/public ./public
ENV PORT=1313
EXPOSE 1313
CMD ["http-server", "public", "-p", "1313", "--gzip"]

Dockerfile Notes

  • Builder stage: Uses the official Hugo Docker image to build your site.
  • Runtime stage: Uses Nginx (recommended) or http-server to serve your static files.
  • Port: The PORT environment variable is set to 1313 for http-server or 80 for Nginx.
  • Multi-stage build: Reduces final image size by excluding build tools from the runtime container.
  • Caching headers: Nginx configuration includes browser caching for optimal performance.

Steps to Deploy with Docker

  1. Create a Dockerfile

    Add the Dockerfile (shown above) to the root of your Hugo repository.

  2. Test Locally (Optional)

    Build and test the Docker image locally:

    Terminal window
    docker build -t hugo-site:latest .
    docker run -p 1313:1313 hugo-site:latest

    Visit http://localhost:1313 to verify.

  3. Push to GitHub

    Commit and push the Dockerfile and your code:

    Terminal window
    git add Dockerfile
    git commit -m "Add Dockerfile for production deployment"
    git push origin main
  4. Create an App in Klutch.sh

    Go to klutch.sh/app, navigate to “Create App”, and select your repository.

  5. Configure the App
    • Traffic Type: Select HTTP
    • Internal Port: Set to 1313 (or 80 if using the Nginx Dockerfile)
    • Environment Variables: Add any required runtime variables
  6. Deploy

    Klutch.sh automatically detects the Dockerfile and uses it to build and deploy your site. Your site will be available at https://example-app.klutch.sh.


Environment Variables

Define environment variables in the Klutch.sh dashboard for production configuration:

HUGO_ENV=production
HUGO_ENABLEGITINFO=true
HUGO_MINIFY=true
SITE_URL=https://example-app.klutch.sh
ANALYTICS_ID=your-analytics-id

Accessing Environment Variables in Hugo

Access environment variables in your templates:

{{ .Site.Params.analyticsID }}

Or in hugo.toml:

[params]
analyticsID = "{{ env "ANALYTICS_ID" }}"
environment = "{{ env "HUGO_ENV" }}"

In templates:

{{ if eq .Site.Params.environment "production" }}
<!-- Production only content -->
<script>
// Analytics script
var analyticsID = "{{ .Site.Params.analyticsID }}";
</script>
{{ end }}

Persistent Storage

If your Hugo site generates files, caches data, or needs to store user-generated content, you can use persistent volumes in Klutch.sh.

Adding Persistent Volumes

  1. In the Klutch.sh dashboard, go to your app’s Volumes section.
  2. Click Add Volume.
  3. Set the mount path (e.g., /cache, /data, /uploads).
  4. Set the size (e.g., 1 GiB, 5 GiB).
  5. Save and redeploy your app.

Example: Caching Generated Content

If you’re generating dynamic content or caching API responses:

Terminal window
# In your Dockerfile, create a volume mount point
RUN mkdir -p /var/cache/hugo
VOLUME /var/cache/hugo

Security Best Practices

1. HTTPS/SSL Enforcement

Klutch.sh automatically provides HTTPS for all deployed sites. All traffic is encrypted and secure.

2. Security Headers

Add security headers via Nginx configuration in your Dockerfile:

RUN echo 'server { \
listen 80; \
add_header X-Content-Type-Options "nosniff" always; \
add_header X-Frame-Options "DENY" always; \
add_header X-XSS-Protection "1; mode=block" always; \
add_header Referrer-Policy "strict-origin-when-cross-origin" always; \
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always; \
# ... rest of config
}' > /etc/nginx/conf.d/default.conf

3. Content Security Policy

Implement CSP headers to protect against XSS attacks:

<!-- In your layout template -->
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'">

4. Disable Directory Listing

Ensure Nginx doesn’t list directories:

RUN echo 'server { \
listen 80; \
autoindex off; \
# ... rest of config
}' > /etc/nginx/conf.d/default.conf

5. Protect Sensitive Files

Use .gitignore to prevent accidental commits of sensitive files:

config.local.toml
.env
.env.local
secrets/
private/

6. Dependency Security

Keep Hugo updated to the latest version for security patches:

Terminal window
hugo version
# Update Hugo regularly

7. Input Validation for Forms

If your site includes forms, validate input on the server:

{{ if eq .Method "POST" }}
{{ if and (ne .Param "name" "") (ne .Param "email" "") }}
<!-- Process form -->
{{ else }}
<!-- Show validation error -->
{{ end }}
{{ end }}

Monitoring and Logging

Health Check

Hugo sites are static and reliable. Monitor deployment health through the Klutch.sh dashboard.

Performance Monitoring

Use tools to monitor your site performance:

<!-- Add to your layout template -->
{{ if eq .Site.Params.environment "production" }}
<script>
// Web Vitals monitoring
web.dev/vitals monitoring code
</script>
{{ end }}

Error Tracking

Use a service like Sentry for JavaScript error tracking:

<!-- In your layout template -->
{{ if eq .Site.Params.environment "production" }}
<script src="https://browser.sentry-cdn.com/7.x.x/bundle.min.js"></script>
<script>
Sentry.init({ dsn: "{{ .Site.Params.sentryDSN }}" });
</script>
{{ end }}

Structured Logging

Monitor build logs in the Klutch.sh dashboard for any deployment issues.


Custom Domains

To use a custom domain with your Klutch.sh-deployed Hugo site:

1. Add the Domain in Klutch.sh

In the Klutch.sh dashboard, go to your app’s settings and add your custom domain (e.g., blog.example.com).

2. Update Your DNS Provider

Update your DNS records with the CNAME provided by Klutch.sh:

CNAME: blog.example.com → example-app.klutch.sh

3. Configure Hugo for Your Domain

Update hugo.toml with your custom domain:

baseURL = "https://blog.example.com"

4. Wait for DNS Propagation

DNS changes can take up to 48 hours to propagate. Verify with:

Terminal window
nslookup blog.example.com
# or
dig blog.example.com CNAME

Once propagated, your Hugo site will be accessible at your custom domain with automatic HTTPS.


Troubleshooting

Issue 1: Build Fails with “Theme Not Found”

Error: failed to find a theme to import

Solutions:

  • Verify theme is in themes/ directory: ls -la themes/
  • Check that theme name in hugo.toml matches directory name
  • If using a git submodule: git submodule update --init --recursive
  • Verify themes/ is tracked in git: git add themes/

Issue 2: Pages Not Appearing

Error: Content pages don’t show on the site

Solutions:

  • Ensure files have draft: false in frontmatter
  • Check the content file is in the correct directory structure
  • Verify baseURL is correct in hugo.toml
  • Check for typos in section names (content/blog/ vs content/posts/)
  • Run hugo --buildDrafts during development

Issue 3: Assets Not Loading (404 Errors)

Error: CSS, images, or JavaScript files return 404

Solutions:

  • Verify asset paths are relative: {{ .Site.BaseURL }}/css/style.css
  • Check files are in static/ or assets/ directory
  • Use Hugo’s absURL or relURL functions in templates
  • Rebuild the site: hugo --cleanDestinationDir

Issue 4: Slow Build Times

Error: Hugo takes more than a minute to build

Solutions:

  • Profile the build: hugo --logFile /tmp/hugo.log -v
  • Remove unused themes
  • Optimize images in source content
  • Reduce the number of custom shortcodes
  • Consider splitting large content sections

Issue 5: Syntax Highlighting Not Working

Error: Code blocks don’t have syntax highlighting

Solutions:

  • Install Chroma for syntax highlighting: included by default in Hugo
  • Verify Chroma is enabled in hugo.toml: pygmentsUseClasses = true
  • Use proper code fence syntax: ```language with language specified
  • Check for custom CSS overriding Chroma styles

Issue 6: URL Rewriting Issues

Error: URLs aren’t formatted correctly or links are broken

Solutions:

  • Check baseURL in hugo.toml includes trailing slash
  • Use {{ .Permalink }} instead of {{ .URL }} for absolute URLs
  • Verify URL frontmatter in content files
  • Check for custom outputs configuration that might affect URLs

Best Practices

1. Organize Content Logically

Create a clear content structure:

content/
├── blog/
│ ├── category-1/
│ └── category-2/
├── docs/
├── projects/
└── pages/

2. Use Front Matter Consistently

Define consistent front matter for all content:

---
title: "Article Title"
description: "Short description for SEO"
date: 2024-01-15
lastmod: 2024-01-20
categories: ["Category"]
tags: ["tag1", "tag2"]
draft: false
featured_image: /images/featured.jpg
---

3. Optimize Images

Use Hugo’s image processing:

{{ $image := .Resources.GetMatch "images/photo.jpg" }}
{{ $resized := $image.Fit "600x400" }}
<img src="{{ $resized.RelPermalink }}" alt="Description">

4. Implement Proper Navigation

Use templates for consistent navigation:

{{ define "partials/nav.html" }}
<nav>
{{ range .Site.Menus.main }}
<a href="{{ .URL }}">{{ .Name }}</a>
{{ end }}
</nav>
{{ end }}

5. Use Partials for Reusable Components

Break templates into small, reusable partials:

layouts/partials/
├── header.html
├── footer.html
├── nav.html
├── post-card.html
└── sidebar.html

6. Implement Breadcrumb Navigation

Help users understand site structure:

{{ define "partials/breadcrumb.html" }}
<nav class="breadcrumb">
<a href="{{ .Site.BaseURL }}">Home</a>
{{ range .Ancestors }}
/ <a href="{{ .Permalink }}">{{ .Title }}</a>
{{ end }}
/ {{ .Title }}
</nav>
{{ end }}

7. Configure Taxonomies

Use categories and tags effectively:

[taxonomies]
category = "categories"
tag = "tags"
author = "authors"

8. Optimize for SEO

Add meta tags for SEO:

{{ define "partials/head-meta.html" }}
<meta name="description" content="{{ .Description }}">
<meta property="og:title" content="{{ .Title }}">
<meta property="og:description" content="{{ .Description }}">
<meta property="og:image" content="{{ .Params.featured_image }}">
{{ end }}

9. Implement Pagination

Use Hugo’s built-in pagination:

{{ range (.Paginate .Pages).Pages }}
{{ .Render "list-item" }}
{{ end }}
{{ template "_internal/pagination.html" . }}

10. Keep Configuration DRY

Use configuration variables in templates:

[params]
siteTitle = "My Hugo Site"
siteDescription = "A fast static site"
author = "Your Name"
copyright = "2024"

Verifying Your Deployment

After deployment completes:

  1. Check the App URL: Visit your site at https://example-app.klutch.sh or your custom domain.
  2. Verify All Pages Load: Navigate through different sections and pages.
  3. Check Performance: Use Google PageSpeed Insights to verify performance.
  4. Review SEO: Check with SEO checkers.
  5. Test Responsiveness: Verify mobile, tablet, and desktop layouts.
  6. Check Browser Console: Open F12 and verify no errors.
  7. Review Klutch.sh Logs: Check the Klutch.sh dashboard logs for issues.

If your site doesn’t work as expected, review the troubleshooting section and check the Klutch.sh dashboard logs.


External Resources


Deploying a Hugo site to Klutch.sh is straightforward with Nixpacks for automatic deployment or Docker for custom environments. By following this guide, you’ve learned how to scaffold a Hugo project, install and configure themes, organize content, create custom templates, optimize images, configure environment variables, implement security best practices, set up monitoring, and troubleshoot common issues. Your Hugo site is now running on Klutch.sh’s global infrastructure with automatic HTTPS, blazing-fast performance, and reliable hosting. For additional help or questions, consult the official Hugo documentation or contact Klutch.sh support.