Complete guide to setting up Hugo with Docker for your HBStack site. Covers Docker installation, configuration, and containerized development workflows.
Complete guide to setting up Hugo with Docker for your HBStack site. Covers Docker installation, configuration, and containerized development workflows.
Using Docker with Hugo simplifies development by providing a consistent environment across different systems.
Before setting up your Hugo project with Docker, you need to install Docker and Docker Compose on your system.
1# Update package index
2sudo apt-get update
3
4# Install packages to allow apt to use a repository over HTTPS
5sudo apt-get install -y \
6 apt-transport-https \
7 ca-certificates \
8 curl \
9 gnupg \
10 lsb-release
11
12# Add Docker's official GPG key
13sudo mkdir -p /etc/apt/keyrings
14curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
15
16# Set up the Docker repository
17echo \
18 "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
19 $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
20
21# Install Docker Engine
22sudo apt update
23sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
24
25
26# Add your user to the docker group to run Docker without sudo
27sudo usermod -aG docker $USER
28
29# check if group is present in groups
30groups
31groups $USER
32
33# You should see `docker` present in groups list.
34# If not, you may need to log out and back in for the group change to take effect.
35# Apply the new group membership (or log out and back in)
36newgrp docker
37
38# check if Docker is installed correctly
39docker run hello-world
Docker Compose comes built-in with recent Docker Desktop installations. For Linux, you might need to install it separately:
1# Download Docker Compose
2sudo curl -L "https://github.com/docker/compose/releases/download/v2.24.6/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
3
4# Apply executable permissions
5sudo chmod +x /usr/local/bin/docker-compose
6
7# Create a symbolic link to make it available in your PATH
8sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
Alternatively, you can use the Docker Compose V2 plugin:
1# Docker Compose V2 is now integrated with the Docker CLI as "docker compose"
2docker compose version
For our Hugo project using the HB Framework, we need to create a docker-compose.yml file that defines our services. Here’s the file structure:
1name: docker-with-hugo
2
3services:
4 server:
5 image: hugomods/hugo:exts-non-root
6 command: server -D --bind 0.0.0.0
7 volumes:
8 - ./:/src:rw
9 - ~/.cache/hugo_cache:/tmp/hugo_cache:rw
10 ports:
11 - 1313:1313
12 env_file:
13 - path: .env
14 required: false
15 restart: unless-stopped
16
17 hugo-mod-update:
18 image: hugomods/hugo:exts-non-root
19 entrypoint: /bin/sh
20 command: >
21 -c "cd /src && echo '📦 Current modules:' && hugo mod graph && echo '\n🔄 Updating modules...' && hugo mod get -u ./... && echo '\n✅ Updated modules:' && hugo mod graph"
22 volumes:
23 - ./:/src:rw
24 - ~/.cache/hugo_cache:/tmp/hugo_cache:rw
25 env_file:
26 - path: .env
27 required: false
28 restart: no
29
30 dev-memory:
31 image: hugomods/hugo:exts-non-root
32 command: server --gc -e development --buildDrafts --enableGitInfo --config config/development/hugo.yaml --renderToMemory -p 1233 --bind 0.0.0.0
33 volumes:
34 - ./:/src:rw
35 - ~/.cache/hugo_cache:/tmp/hugo_cache:rw
36 ports:
37 - 1233:1233
38 env_file:
39 - path: .env
40 required: false
41 restart: unless-stopped
42
43 dev-disk:
44 image: hugomods/hugo:exts-non-root
45 command: server --gc -e development --buildDrafts --logLevel=info --enableGitInfo --config config/development/hugo.yaml -p 1433 --bind 0.0.0.0
46 volumes:
47 - ./:/src:rw
48 - ~/.cache/hugo_cache:/tmp/hugo_cache:rw
49 ports:
50 - 1433:1433
51 env_file:
52 - path: .env
53 required: false
54 restart: unless-stopped
55
56 validate:
57 image: hugomods/hugo:exts-non-root
58 command: hugo --gc --panicOnWarning --renderToMemory --logLevel=info
59 volumes:
60 - ./:/src:rw
61 - ~/.cache/hugo_cache:/tmp/hugo_cache:rw
62 env_file:
63 - path: .env
64 required: false
Let’s break down the key components of this configuration:
Project Name
1name: docker-with-hugo
Defines the project name, which is used in container naming.
Service Definitions We define three services for different development scenarios:
server: Standard development serverdev-memory: Development server with rendering to memorydev-disk: Development server with rendering to diskDocker Image
1image: hugomods/hugo:exts-non-root
Uses the official HugoMods Docker image that includes:
Command Each service has a specific command:
1command: server -D --bind 0.0.0.0 # For the default server
This launches Hugo’s development server with drafts enabled and binds to all network interfaces.
1# For dev-memory
2command: server --gc -e development --buildDrafts --enableGitInfo --config config/development/hugo.yaml --renderToMemory -p 1233 --bind 0.0.0.0
The parameters mean:
--gc: Run garbage collection during the build-e development: Use development environment--buildDrafts: Include draft content--enableGitInfo: Get last Git revision for each page--config config/development/hugo.yaml: Use specific config file--renderToMemory: Store rendered pages in memory (faster performance)-p 1233: Use port 1233--bind 0.0.0.0: Bind to all network interfacesVolumes
1volumes:
2 - ./:/src:rw
3 - ~/.cache/hugo_cache:/tmp/hugo_cache:rw
Maps your local project directory to /src inside the container with read-write permissions, and uses a persistent cache to speed up builds.
Ports
1ports:
2 - 1313:1313 # For default server
3 - 1233:1233 # For development servers
Maps container ports to host ports so you can access your Hugo site in a browser.
Environment Variables
1env_file:
2 - path: .env
3 required: false
Allows for loading environment variables from a .env file.
Restart Policy
1restart: unless-stopped
Ensures the container restarts automatically unless explicitly stopped, providing better reliability.
You can start any of the predefined servers using Docker Compose:
1# Start the default server (accessible at http://localhost:1313)
2docker compose up server
3
4# Start the memory-based development server (accessible at http://localhost:1233)
5docker compose up dev-memory
6
7# Start the disk-based development server (accessible at http://localhost:1433)
8docker compose up dev-disk
Add the -d flag to run in detached mode:
1docker compose up -d server
To stop and remove the containers:
1docker compose down
To generate a production-ready site:
1docker compose run --rm server hugo --minify
This command:
hugo --minify command in the server service--rm flag removes the container after it completes--minify flag compresses HTML, CSS, JS, and XML filesHugo Modules provide a way to share and reuse components across Hugo sites. To update all modules:
1docker compose run hugo-mod-update
To add a new module, edit config/_default/module.yaml and then run the update command.
To remove any orphaned containers that are not defined in your docker-compose.yml file:
1docker compose <up or down> --remove-orphans
To remove all stopped containers, you can use the following command:
1docker container prune
This command will prompt you for confirmation before removing all stopped containers. If you want to skip the confirmation, you can add the -f flag.
When working with Docker for Hugo development, you might accumulate many stopped containers over time. If you found 10 stopped containers when running docker container prune, it’s a sign that you should establish a regular maintenance routine. Here are best practices for managing your Docker containers:
Here’s a recommended cleaning schedule based on usage patterns:
Daily Active Development: If you’re actively developing every day:
docker container prune at the end of each workday--rm flag with docker run commands to automatically remove containers after they exitOccasional Development: If you work on your Hugo site a few times a week:
docker container prune once a weekProject-Based Cleanup: If you switch between multiple projects:
docker container prune when switching between projectsHere’s an effective workflow to minimize container clutter:
Start your day with:
1# Check what containers exist before you begin
2docker ps -a
3
4# Remove any dangling containers from previous work
5docker container prune -f
During development:
1# Always use the --rm flag for one-off commands
2docker run --rm -v "$(pwd)":/src -w /src hugomods/hugo:exts-non-root hugo version
3
4# Use named containers for longer-running processes
5docker compose up -d --name hugo-dev server
End your day with:
1# Stop all running containers for this project
2docker compose down
3
4# Clean up any leftover containers
5docker container prune -f
Here are some useful commands for container maintenance:
1# List all containers (running and stopped)
2docker ps -a
3
4# See how much disk space Docker is using
5docker system df
6
7# Get detailed information about a specific container
8docker inspect <container_id>
9
10# Remove containers with a specific status (like 'exited')
11docker rm $(docker ps -q -f status=exited)
12
13# Stop and remove all containers
14docker stop $(docker ps -a -q) && docker rm $(docker ps -a -q)
15
16# Complete system pruning (containers, images, networks, and volumes)
17docker system prune -a
To effectively manage your Docker environment, it’s important to regularly monitor resource usage:
1# Check overall Docker disk usage
2docker system df
3
4# Get detailed breakdown of space usage
5docker system df -v
6
7# Monitor real-time resource usage of running containers
8docker stats
9
10# Check logs for a specific container
11docker logs <container_id>
12
13# Follow logs in real-time
14docker logs -f <container_id>
For more thorough cleanup beyond just containers:
1# Remove unused images (not just dangling ones)
2docker image prune -a
3
4# Remove all unused volumes
5docker volume prune
6
7# Remove all unused networks
8docker network prune
9
10# Complete system cleanup (containers, networks, images, and build cache)
11docker system prune -a --volumes
Create a file named docker-maintenance.sh in your project root:
1#!/bin/bash
2
3# Comprehensive Docker maintenance script
4
5echo "📊 Current Docker status:"
6echo "========================="
7docker ps
8echo ""
9echo "Disk usage before cleanup:"
10docker system df
11echo ""
12
13echo "🛑 Stopping all running containers..."
14docker stop $(docker ps -q) 2>/dev/null || echo "No running containers to stop."
15echo ""
16
17echo "🧹 Cleaning up Docker resources..."
18echo "Removing all stopped containers..."
19docker container prune -f
20echo ""
21
22echo "Removing unused images..."
23docker image prune -a -f
24echo ""
25
26echo "Removing unused volumes..."
27docker volume prune -f
28echo ""
29
30echo "Removing unused networks..."
31docker network prune -f
32echo ""
33
34echo "✅ Cleanup complete!"
35echo ""
36echo "Disk usage after cleanup:"
37docker system df
38echo ""
39
40echo "🚀 Starting essential services..."
41cd /path/to/your/hugo/project
42docker compose up -d server
43echo "Done! Your Hugo server should be running at http://localhost:1313"
Make it executable:
1chmod +x docker-maintenance.sh
To automate cleanup, add a cron job:
1crontab -e
Add one of these lines depending on your preferred schedule:
1# Run daily at midnight
20 0 * * * /path/to/your/project/docker-maintenance.sh >> /path/to/your/project/logs/docker-maintenance.log 2>&1
3
4# Run weekly on Sunday at midnight
50 0 * * 0 /path/to/your/project/docker-maintenance.sh >> /path/to/your/project/logs/docker-maintenance.log 2>&1
6
7# Run at system startup
8@reboot sleep 60 && /path/to/your/project/docker-maintenance.sh >> /path/to/your/project/logs/docker-maintenance.log 2>&1
If you’re using npm/yarn in your project, add these scripts to your package.json:
1"scripts": {
2 "dev": "docker compose up server",
3 "dev:memory": "docker compose up dev-memory",
4 "dev:disk": "docker compose up dev-disk",
5 "build": "docker compose run --rm server hugo --minify",
6 "docker:status": "docker ps && echo '\\nResource usage:' && docker stats --no-stream",
7 "docker:cleanup": "docker system prune -a -f --volumes",
8 "docker:restart": "docker compose down && docker compose up -d server"
9}
Then you can run them with:
1npm run docker:status
2npm run docker:cleanup
Add this service to your docker-compose.yml:
1services:
2 # ... your existing services ...
3
4 maintenance:
5 image: docker:cli
6 volumes:
7 - /var/run/docker.sock:/var/run/docker.sock
8 command: >
9 sh -c "
10 echo 'Running Docker maintenance...' &&
11 docker system prune -a -f --volumes &&
12 echo 'Maintenance complete!'
13 "
14 profiles:
15 - maintenance
Run it with:
1docker compose --profile maintenance up maintenance
To prevent Docker containers from consuming too many resources, add limits to your services in docker-compose.yml:
1services:
2 server:
3 # ... existing configuration ...
4 deploy:
5 resources:
6 limits:
7 cpus: '0.5'
8 memory: 512M
9 reservations:
10 cpus: '0.25'
11 memory: 256M
Add health checks to ensure your services are running properly:
1services:
2 server:
3 # ... existing configuration ...
4 healthcheck:
5 test: ['CMD', 'curl', '-f', 'http://localhost:1313']
6 interval: 1m
7 timeout: 10s
8 retries: 3
9 start_period: 30s
For better isolation and security:
1services:
2 server:
3 # ... existing configuration ...
4 networks:
5 - hugo_network
6
7networks:
8 hugo_network:
9 driver: bridge
For keeping your Docker images up-to-date:
1docker run -d \
2 --name watchtower \
3 -v /var/run/docker.sock:/var/run/docker.sock \
4 container/watchtower \
5 --interval 86400 \
6 --cleanup
This will check for updates once a day and clean up old images.
You can customize your Hugo environment by creating a .env file in your project root. This file is optional but useful for development.
Example .env file:
1# Hugo environment: development or production
2HUGO_ENVIRONMENT=development
3
4# For direct module downloads without proxy
5HUGO_MODULE_PROXY=direct
6
7# Set cache directory (already configured in docker-compose.yml)
8# HUGO_CACHEDIR=/tmp/hugo_cache
9
10# Enable verbose output for debugging
11# HUGO_VERBOSE=true
12
13# Custom parameters for your theme
14# HUGO_PARAMS_THEME_VARIANT=dark
To see all environment variables available:
1docker compose run server env
Image used in this setup is hugomods/hugo:exts-non-root, which includes:
Learn more about this image at docker.hugomods.com
Using Docker with Hugo offers several advantages:
Consistent Environment: Everyone working on the project gets the exact same Hugo version, dependencies, and configuration.
No Local Dependencies: You don’t need to install Hugo, Go, Node.js, PostCSS, or any other dependencies directly on your system.
Version Control: You can specify exact versions of Hugo to use, preventing compatibility issues when Hugo updates.
Cross-platform: Works the same way across different operating systems (Linux, macOS, Windows).
Isolation: Prevents conflicts with other software on your system.
Easy Setup: New team members can start contributing quickly without complex setup.
For more convenient operation, you can use VS Code tasks to run Docker commands. Create a .vscode/tasks.json file with the following content:
1{
2 "version": "2.0.0",
3 "tasks": [
4 {
5 "label": "Hugo: Start Server (Default)",
6 "type": "shell",
7 "command": "docker compose up server",
8 "problemMatcher": [],
9 "group": {
10 "kind": "build",
11 "isDefault": true
12 }
13 },
14 {
15 "label": "Hugo: Start Dev Server (Memory)",
16 "type": "shell",
17 "command": "docker compose up dev-memory",
18 "problemMatcher": []
19 },
20 {
21 "label": "Hugo: Start Dev Server (Disk)",
22 "type": "shell",
23 "command": "docker compose up dev-disk",
24 "problemMatcher": []
25 },
26 {
27 "label": "Hugo: Stop All Containers",
28 "type": "shell",
29 "command": "docker compose down",
30 "problemMatcher": []
31 },
32 {
33 "label": "Hugo: Build Production Site",
34 "type": "shell",
35 "command": "docker compose run --rm server hugo --minify",
36 "problemMatcher": []
37 },
38 {
39 "label": "Hugo: Update Modules",
40 "type": "shell",
41 "command": "docker compose run hugo-mod-update",
42 "problemMatcher": []
43 },
44 {
45 "label": "Docker: Show Status",
46 "type": "shell",
47 "command": "docker ps && echo '\\nResource usage:' && docker stats --no-stream",
48 "problemMatcher": []
49 },
50 {
51 "label": "Docker: Clean Up Resources",
52 "type": "shell",
53 "command": "docker system prune -a -f --volumes",
54 "problemMatcher": []
55 }
56 ]
57}
You can then run these tasks from VS Code by pressing Ctrl+Shift+P and typing “Tasks: Run Task”.