Hugo Static Site Generation: From Development to Production

Jun 8, 2025

Complete guide to building fast, secure static websites with Hugo. Covers installation, custom themes, content management, and deployment automation.

Hugo Static Site Generation: From Development to Production

Hugo: Modern Static Site Generation

Hugo stands as one of the fastest static site generators, built with Go for speed and efficiency. It transforms Markdown content into lightning-fast websites with minimal configuration and maximum flexibility.

Why Hugo?

Performance Benefits

  • Build Speed: Sites with thousands of pages build in seconds
  • Runtime Performance: Static files serve instantly
  • Memory Efficient: Minimal resource usage during builds
  • SEO Optimized: Clean HTML and fast loading times

Developer Experience

  • Live Reload: Instant preview of changes
  • Hot Reload: CSS and JS updates without page refresh
  • Flexible Templating: Go templates with logical structure
  • Content Management: Markdown with frontmatter metadata

Installation and Setup

macOS Installation

# Method 1: Homebrew (Recommended)
brew install hugo

# Method 2: Extended version with Sass/SCSS support
brew install hugo --HEAD

# Verify installation
hugo version

Alternative Installation Methods

# Method 3: Direct binary download
curl -L https://github.com/gohugoio/hugo/releases/latest/download/hugo_extended_0.111.3_macOS-universal.tar.gz | tar -xz
sudo mv hugo /usr/local/bin/

# Method 4: Go installation
go install github.com/gohugoio/hugo@latest

Project Initialization

Creating a New Site

# Create new Hugo site
hugo new site samcloud-portfolio
cd samcloud-portfolio

# Initialize Git repository
git init
git add .
git commit -m "Initial Hugo site"

# Site structure overview
tree -L 2

Directory Structure

samcloud-portfolio/
├── archetypes/          # Content templates
├── assets/              # Source files for processing
├── config/              # Configuration files
├── content/             # Site content (Markdown)
├── data/                # Data files (JSON, YAML, TOML)
├── layouts/             # Template files
├── public/              # Generated static site
├── resources/           # Cached processed files
├── static/              # Static assets
└── themes/              # Theme files

Configuration

Main Configuration (config.toml)

baseURL = 'https://samcloud.dev'
languageCode = 'en-us'
title = 'SamCloud Portfolio'
theme = 'samcloud-theme'

# Site parameters
[params]
  author = "Sam Cloud"
  description = "Creative & Technical Portfolio"
  keywords = ["photography", "development", "infrastructure"]
  social_linkedin = "https://linkedin.com/in/sam-cloud"
  social_github = "https://github.com/samcloud"

# Menu configuration
[[menu.main]]
  name = "Portfolio"
  url = "/portfolio/"
  weight = 10

[[menu.main]]
  name = "Production"
  url = "/production/"
  weight = 20

[[menu.main]]
  name = ".SamCloud"
  url = "/samcloud/"
  weight = 30

[[menu.main]]
  name = "Services"
  url = "/services/"
  weight = 40

# Markup configuration
[markup]
  [markup.goldmark]
    [markup.goldmark.renderer]
      unsafe = true
  [markup.highlight]
    style = 'github'
    lineNos = true
    lineNumbersInTable = false

# Build configuration
[build]
  writeStats = true
  
[[build.cachebusters]]
  source = "assets/.*\\.(js|ts|jsx|tsx)"
  target = "js"

[[build.cachebusters]]
  source = "assets/.*\\.(.*)$"
  target = "$1"

Environment-Specific Configuration

# config/development/config.toml
baseURL = 'http://localhost:1313'
buildDrafts = true
buildFuture = true

# config/production/config.toml
baseURL = 'https://samcloud.dev'
minify = true

Custom Theme Development

Theme Structure

# Create custom theme
hugo new theme samcloud-theme

# Theme directory structure
themes/samcloud-theme/
├── archetypes/
├── layouts/
│   ├── _default/
│   ├── partials/
│   ├── portfolio/
│   └── index.html
├── static/
│   ├── css/
│   ├── js/
│   └── images/
└── theme.toml

Base Layout Template

<!-- themes/samcloud-theme/layouts/_default/baseof.html -->
<!DOCTYPE html>
<html lang="{{ .Site.LanguageCode }}">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>{{ block "title" . }}{{ .Site.Title }}{{ end }}</title>
    
    <!-- SEO Meta Tags -->
    <meta name="description" content="{{ with .Description }}{{ . }}{{ else }}{{ .Site.Params.description }}{{ end }}">
    <meta name="keywords" content="{{ delimit .Site.Params.keywords ", " }}">
    
    <!-- Open Graph -->
    <meta property="og:title" content="{{ .Title }}">
    <meta property="og:description" content="{{ with .Description }}{{ . }}{{ else }}{{ .Site.Params.description }}{{ end }}">
    <meta property="og:type" content="website">
    <meta property="og:url" content="{{ .Permalink }}">
    
    <!-- Stylesheets -->
    {{ $style := resources.Get "scss/main.scss" | resources.ToCSS | resources.Minify | resources.Fingerprint }}
    <link rel="stylesheet" href="{{ $style.Permalink }}" integrity="{{ $style.Data.Integrity }}">
    
    {{ block "head" . }}{{ end }}
</head>
<body>
    {{ block "header" . }}{{ partial "header.html" . }}{{ end }}
    
    <main>
        {{ block "main" . }}{{ end }}
    </main>
    
    {{ block "footer" . }}{{ partial "footer.html" . }}{{ end }}
    
    <!-- JavaScript -->
    {{ $js := resources.Get "js/main.js" | resources.Minify | resources.Fingerprint }}
    <script src="{{ $js.Permalink }}" integrity="{{ $js.Data.Integrity }}"></script>
    
    {{ block "scripts" . }}{{ end }}
</body>
</html>
<!-- themes/samcloud-theme/layouts/portfolio/single.html -->
{{ define "main" }}
<article class="portfolio-article">
    <header class="portfolio-header">
        <h1>{{ .Title }}</h1>
        <div class="portfolio-meta">
            <time datetime="{{ .Date.Format "2006-01-02" }}">{{ .Date.Format "January 2, 2006" }}</time>
            {{ with .Params.tags }}
            <div class="tags">
                {{ range . }}
                <span class="tag">{{ . }}</span>
                {{ end }}
            </div>
            {{ end }}
        </div>
    </header>
    
    <div class="portfolio-content">
        {{ .Content }}
    </div>
    
    {{ with .Params.gallery }}
    <div class="portfolio-gallery">
        {{ range . }}
        <div class="gallery-item">
            <img src="{{ . }}" alt="Portfolio image" loading="lazy">
        </div>
        {{ end }}
    </div>
    {{ end }}
</article>
{{ end }}

Content Management

Content Organization

content/
├── _index.md                 # Homepage
├── portfolio/
│   ├── _index.md            # Portfolio listing
│   ├── yellow-mellow-clawz/
│   │   └── index.md
│   └── maternity-shoot/
│       └── index.md
├── samcloud/
│   ├── _index.md            # Tech blog listing
│   ├── hugo-setup.md
│   └── wireguard-vpn.md
└── about/
    └── index.md

Content Templates (Archetypes)

# archetypes/portfolio.md
---
title: "{{ replace .Name "-" " " | title }}"
date: {{ .Date }}
draft: false
description: ""
tags: []
categories: ["Portfolio"]
gallery: []
featured_image: ""
---

Gallery description and project details go here.
# archetypes/samcloud.md
---
title: "{{ replace .Name "-" " " | title }}"
date: {{ .Date }}
draft: false
description: ""
tags: []
categories: ["Technical"]
---

Technical article content starts here.

Creating Content

# Create new portfolio entry
hugo new portfolio/new-photoshoot/index.md

# Create new technical article
hugo new samcloud/kubernetes-setup.md

# Create content with specific archetype
hugo new --kind samcloud samcloud/docker-optimization.md

Asset Processing

SCSS/Sass Processing

// assets/scss/main.scss
@import "variables";
@import "mixins";
@import "base";
@import "components/gallery";
@import "components/navigation";

// Variables
$primary-color: #3b82f6;
$secondary-color: #1e293b;
$accent-color: #fbbf24;

// Responsive mixins
@mixin mobile {
  @media (max-width: 768px) {
    @content;
  }
}

@mixin desktop {
  @media (min-width: 769px) {
    @content;
  }
}

JavaScript Bundling

// assets/js/main.js
import { initGallery } from './components/gallery.js';
import { initNavigation } from './components/navigation.js';

document.addEventListener('DOMContentLoaded', () => {
    initGallery();
    initNavigation();
});

Image Processing

<!-- Image processing in templates -->
{{ $image := .Resources.GetMatch "featured.jpg" }}
{{ $thumbnail := $image.Resize "300x200" }}
{{ $large := $image.Resize "1200x800" }}

<picture>
    <source media="(max-width: 768px)" srcset="{{ $thumbnail.Permalink }}">
    <img src="{{ $large.Permalink }}" alt="{{ .Title }}" loading="lazy">
</picture>

Development Workflow

Local Development

# Start development server
hugo server -D --disableFastRender

# Start with specific configuration
hugo server --environment development

# Build for specific environment
hugo --environment production --minify

# Watch for changes with polling (useful for networked drives)
hugo server --poll 1s

Content Management Workflow

# Create content branch
git checkout -b content/new-portfolio-entry

# Add content and assets
hugo new portfolio/beach-photoshoot/index.md
# Add images to content/portfolio/beach-photoshoot/

# Preview changes
hugo server -D

# Commit and push
git add .
git commit -m "Add beach photoshoot portfolio entry"
git push origin content/new-portfolio-entry

Performance Optimization

Build Optimization

# config.toml
[minify]
  disableCSS = false
  disableHTML = false
  disableJS = false
  disableJSON = false
  disableSVG = false
  disableXML = false

[caches]
  [caches.getcsv]
    dir = ":cacheDir/:project"
    maxAge = "10s"
  [caches.getjson]
    dir = ":cacheDir/:project"
    maxAge = "10s"
  [caches.images]
    dir = ":resourceDir/_gen"
    maxAge = "720h"

Image Optimization

# Batch optimize images
find static/images -name "*.jpg" -exec mogrify -strip -interlace Plane -gaussian-blur 0.05 -quality 85% {} \;

# WebP conversion
find static/images -name "*.jpg" -exec cwebp -q 80 {} -o {}.webp \;

Performance Monitoring

// Monitor Core Web Vitals
import {getCLS, getFID, getFCP, getLCP, getTTFB} from 'web-vitals';

getCLS(console.log);
getFID(console.log);
getFCP(console.log);
getLCP(console.log);
getTTFB(console.log);

Deployment Automation

GitHub Actions Workflow

# .github/workflows/hugo.yml
name: Deploy Hugo Site

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
      with:
        submodules: recursive
        
    - name: Setup Hugo
      uses: peaceiris/actions-hugo@v2
      with:
        hugo-version: 'latest'
        extended: true
        
    - name: Build
      run: hugo --minify
      
    - name: Deploy to GitHub Pages
      uses: peaceiris/actions-gh-pages@v3
      if: github.ref == 'refs/heads/main'
      with:
        github_token: ${{ secrets.GITHUB_TOKEN }}
        publish_dir: ./public

Netlify Deployment

# netlify.toml
[build]
  publish = "public"
  command = "hugo --gc --minify"

[context.production.environment]
  HUGO_VERSION = "0.111.3"
  HUGO_ENV = "production"
  HUGO_ENABLEGITINFO = "true"

[[headers]]
  for = "/*"
  [headers.values]
    X-Frame-Options = "DENY"
    X-XSS-Protection = "1; mode=block"

Advanced Features

Multilingual Support

# config.toml
defaultContentLanguage = "en"

[languages]
  [languages.en]
    title = "SamCloud Portfolio"
    weight = 1
  [languages.es]
    title = "Portafolio SamCloud"
    weight = 2

Custom Shortcodes

<!-- layouts/shortcodes/gallery.html -->
<div class="gallery" data-gallery="{{ .Get "name" }}">
  {{ range .Params }}
  <div class="gallery-item">
    <img src="{{ . }}" alt="Gallery image" loading="lazy">
  </div>
  {{ end }}
</div>

Usage in content:














  

  

  

  

  

  

  

  

  

  

  

  

  

  


Gallery image
Gallery image
Gallery image
Gallery image
Gallery image
Gallery image
Gallery image
Gallery image
Gallery image
Gallery image
Gallery image
Gallery image
Gallery image
Gallery image

Data-Driven Content

# data/portfolio.yaml
categories:
  - name: "Fashion Photography"
    slug: "fashion"
    description: "Editorial and commercial fashion work"
  - name: "Lifestyle"
    slug: "lifestyle"
    description: "Candid lifestyle and portrait photography"

Troubleshooting

Common Issues

# Clear Hugo cache
hugo mod clean --all

# Rebuild assets
rm -rf resources/_gen/

# Check for template errors
hugo server --debug --verbose

# Validate configuration
hugo config

Performance Debugging

# Analyze build performance
hugo --templateMetrics --templateMetricsHints

# Check for unused CSS
hugo server --printUnusedTemplates

Conclusion

Hugo provides a powerful, flexible foundation for building fast, maintainable websites. Its speed, extensive theming capabilities, and robust asset processing make it ideal for portfolios, blogs, and documentation sites.

The combination of Markdown content, Go templates, and modern build tools creates a development experience that scales from simple blogs to complex multi-language sites.


Next: Ollama & Open WebUI: Private AI Infrastructure