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?
- 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>
Portfolio Gallery Layout
<!-- 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
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 \;
// 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:
×
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
# 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