Знакомство с Hugo

В этой статье мы познакомимся с генератором статичных сайтов Hugo. Установим Hugo в Docker и сгенерируем сайт с главной страничкой и статьёй из Markdown-файлов.

Что такое Hugo

Hugo — статический генератор сайтов, написанный на Go. Он превращает Markdown-файлы в готовый HTML-сайт. Вы пишете контент в текстовых файлах (.md), а Hugo генерирует из них готовый сайт.

Многие знакомы с динамическими сайтами, например на WordPress. Там сайт обычно работает так:

  1. Пользователь открывает страницу.
  2. Сервер запускает PHP или другой код.
  3. Код обращается к базе данных.
  4. Из базы достаются статьи, настройки, комментарии.
  5. Сервер собирает HTML-страницу и отдаёт её браузеру.

У статического сайта подход другой. Страницы заранее собираются в готовые HTML-файлы. Когда пользователь открывает сайт, сервер просто отдаёт уже готовый файл. То есть:

  • не нужна база данных;
  • не нужен PHP, Python или другой серверный язык для отображения страниц;
  • сайт можно раздавать даже с обычного файлового хостинга или из CDN;
  • страницы загружаются очень быстро.

Именно так создан данный сайт. Я пишу статьи в Obsidian → С помощью Hugo генерирую из .md файлов готовый статичный сайт → публикую сайт на хостинге.

Пример статьи в Hugo:

---
title: "Привет, мир!"
date: 2026-04-02
draft: false
tags: ["hugo", "тест"]
categories: ["блог"]
---

Это моя первая статья.
  • Блок между --- называется front matter. Это служебные данные страницы: заголовок, дата, теги, черновик, описание и другие параметры страницы.
  • Всё, что находится ниже, уже является содержимым страницы.

С помощью front matter Hugo может:

  • подставлять заголовок в шаблон страницы;
  • сортировать статьи по дате;
  • скрывать черновики;
  • показывать теги и категории;
  • формировать списки статей;
  • создавать RSS, sitemap и другие служебные страницы.

Типичный сайт на Hugo имеет примерно такую структуру:

.
├── archetypes/   # шаблоны для новых страниц
├── assets/       # CSS, JS и другие ассеты
├── content/      # статьи и страницы сайта
├── data/         # дополнительные данные
├── layouts/      # собственные шаблоны
├── public/       # в этот каталог собирается готовый сайт (html-страницы)
├── static/       # статические файлы: картинки, favicon и т.д.
├── themes/       # темы оформления
└── hugo.toml     # основной конфигурационный файл

Рассмотрим основные каталоги.

  • content/ Здесь хранится контент сайта. При генерации, файлы из этого каталога превращаются в страницы сайта (в каталоге public/). Например:
content/
├── _index.md
└── posts/
    ├── hello-world.md
    └── second-post.md
  • public/ При генерации статического сайта он помещается в этот каталог, после генерации именно его нужно публиковать на хостинге. Например:
public/
├── 404.html
├── CNAME
├── css/
├── posts/
       ├── hello-world/
       ├── second-post/
├── favicons/
├── images/
├── index.html
├── index.xml
├── js/
├── robots.txt
├── scss/
├── sitemap.xml
  • static/ Сюда кладут файлы, которые должны попасть на сайт как есть: картинки, favicon.ico, robots.txt, произвольные файлы для скачивания. Например:
static/
└── images/
    └── logo.png

После сборки файл будет доступен как:

public/images/logo.png
  • layouts/ Здесь находятся шаблоны страниц. Если тема вас почти устраивает, но вы хотите изменить какой-то блок, обычно не нужно править саму тему. Можно скопировать нужный шаблон темы в этот каталог и изменить его там.

  • themes/ Здесь лежат темы оформления. Тема содержит готовые шаблоны, стили и другие файлы, которые определяют внешний вид сайта. Ниже мы будем использовать тему Ananke.

  • archetypes/ Это заготовки для новых страниц. Например, можно создать шаблон статьи, чтобы новые посты автоматически получали нужный набор полей: заголовок, дату, теги, описание.

  • hugo.toml Основной файл настроек сайта. Здесь задаются: адрес сайта, язык, название сайта используемая тема, дополнительные параметры. Например:

baseURL = 'http://localhost:1313/'
languageCode = 'ru'
title = 'Мой тестовый Hugo-сайт'
theme = 'ananke'

[params]
  description = 'Тестовый сайт на Hugo + Docker'

Hugo хорошо подходит для блогов, документации, персональных сайтов и небольших проектов.

  • Он написан на Go и очень быстро генерирует сайт. Даже большие сайты могут собираться за секунды.
  • Статьи можно писать в Markdown. Это удобно, потому что не нужно отвлекаться на оформление. Вы пишете текст, а Hugo сам превратит его в HTML.
  • Весь сайт можно хранить как набор текстовых файлов. Это хорошо сочетается с Git.
  • Не нужна база данных, которую нужно обслуживать, резервировать и защищать от типичных уязвимостей.
  • Готовый сайт можно выложить практически на любой статический хостинг.
  • Во время разработки можно использовать локальный hugo server. Он запускает локальный веб-сервер и автоматически пересобирает сайт при изменении файлов.

Установка Hugo в Docker и генерация сайта

В этой статье мы развернём Hugo в Docker-контейнере, чтобы не засорять систему зависимостями. Кстати, если у вас ещё не установлены, то нужно установить:

Создаём каталог проекта и сразу переходим внутрь — дальше все файлы будут создаваться относительно этой директории.

mkdir hugo-test/ && cd hugo-test

Создадим docker-compose.yml:

cat << 'EOT' > docker-compose.yml
services:
  hugo:
    image: hugomods/hugo:0.160.0
    container_name: hugo
    restart: unless-stopped

    ports:
      - "1313:1313"

    volumes:
      - ./site:/src

    working_dir: /src

    command:
      - hugo
      - server
      - --bind=0.0.0.0
      - --baseURL=http://localhost:1313/
      - --buildDrafts
      - --buildFuture
      - --disableFastRender
EOT

Разберём построчно:

  • image: hugomods/hugo:0.160.0 Официальный образ Hugo конкретной версии. Фиксируем версию, чтобы сборка была воспроизводимой.
  • container_name: hugo Задаём понятное имя контейнера.
  • restart: unless-stopped Контейнер автоматически перезапустится после перезагрузки хоста или сбоя, пока мы сами его не остановим.
  • ports: "1313:1313" Пробрасываем порт 1313 из контейнера на хост — по нему будет доступен сайт.
  • volumes: ./site:/src Монтируем локальный каталог hugo-test/site внутрь контейнера как /src. Все файлы сайта живут на хосте, контейнер их только читает и собирает.
  • working_dir: /src Hugo запускается именно в /src, где лежит наш сайт.
  • command - Переопределяем команду по умолчанию: вместо разовой сборки запускаем живой сервер с автоперезагрузкой при изменении файлов.

Флаги сервера:

  • --bind=0.0.0.0 — слушаем на всех интерфейсах (иначе сервер доступен только внутри контейнера);
  • --baseURL — базовый URL, по которому мы открываем сайт в браузере;
  • --buildDrafts — показывать черновики (draft: true);
  • --buildFuture — показывать посты с датой в будущем;
  • --disableFastRender — полная пересборка при каждом изменении (медленнее, но надёжнее на этапе разработки).

Генерируем каркас нового сайта:

docker compose run --rm hugo new site /src
  • run --rm запускает одноразовый контейнер (после завершения он удалится);
  • переопределяем command из compose-файла. Вместо сервера выполняется hugo new site /src — команда создаёт пустую структуру проекта в примонтированном каталоге ./site.

В результате получаем:

site/
├── archetypes/   # шаблоны для новых постов
├── assets/       # CSS/JS, которые обрабатывает Hugo
├── content/      # весь контент сайта (статьи, страницы)
├── data/         # доп. данные в YAML/JSON/TOML
├── hugo.toml     # главный конфиг сайта
├── i18n/         # файлы локализации
├── layouts/      # собственные шаблоны (переопределяют тему)
├── static/       # статика: картинки, favicon, robots.txt
└── themes/       # сюда кладём темы оформления

Создаём каталог для постов:

sudo mkdir -p site/content/posts
  • Hugo строит дерево страниц из структуры каталогов внутри content/. Каталог posts станет разделом «Статьи» — каждый .md-файл внутри него будет отдельной записью.

Настраиваем hugo.toml:

cat << 'EOT' | sudo tee site/hugo.toml
baseURL = 'http://localhost:1313/'
languageCode = 'ru'
title = 'Мой тестовый Hugo-сайт'
theme = 'ananke'

[params]
  description = 'Тестовый сайт на Hugo + Docker'
EOT
  • baseURL Корневой адрес сайта. При деплое на реальный домен меняем на свой.
  • languageCode Язык контента — влияет на теги <html lang="ru">.
  • title Название сайта, отображается в шапке и в <title>.
  • theme Имя каталога с темой внутри themes/.
  • [params] description Произвольные параметры, которые тема использует в своих шаблонах.

Подключаем тему Ananke:

cd site && sudo git init && \
  sudo git submodule add https://github.com/gohugo-ananke/ananke.git themes/ananke
  • Тема оформляется как git-подмодуль: она лежит в themes/ananke/, но хранится в отдельном репозитории. Так её легко обновлять (git submodule update --remote) и не смешивать со своим кодом.

Возвращаемся в hugo-test/:

cd ..

Создаём главную страницу:

cat << 'EOT' | sudo tee site/content/_index.md
---
title: "Добро пожаловать"
date: 2026-04-02
draft: false
---

# Привет!

Это мой тестовый сайт на **Hugo + Docker + Ananke**.

[Перейти к статьям →](/posts/)
EOT
  • Файл _index.md в корне content/ — это главная страница сайта. Блок между --- называется front matter (про него я уже писал), ниже — сам контент.

Пишем первый пост:

cat << 'EOT' | sudo tee site/content/posts/hello-world.md
---
title: "Привет, мир!"
date: 2026-04-02
draft: false
description: "Моя первая статья на hugo"
tags: ["hugo", "docker", "тест"]
categories: ["hugo"]
---

## Введение

Это моя **первая статья** на Hugo!

Спасибо, что читаете!
EOT
  • Любой .md в content/posts/ автоматически становится записью в разделе «Статьи». tags и categories из front matter позволяют теме группировать посты и строить навигацию.

Запускаем сайт:

docker compose up -d && \
  docker compose ps
  • up -d поднимает контейнер в фоне.
  • ps - проверяем, что контейнер работает.

Если что-то пошло не так — смотрим логи:

docker compose logs -f hugo
  • -f (follow) выводит лог в реальном времени, как tail -f. Успешный запуск выглядит так:
hugo  | Web Server is available at http://localhost:1313/ (bind address 0.0.0.0) 
hugo  | Press Ctrl+C to stop

Проверяем в браузере:

http://localhost:1313/

Видим главную страницу и пост:

При любом изменении .md файлов сервер автоматически пересоберёт сайт и обновит вкладку браузера.

Итог

В этой статье мы прошли путь с нуля: создали каталог проекта, настроили Docker-контейнер с Hugo, подключили тему Ananke и написали первую статью. Сайт уже работает, доступен в браузере и автоматически обновляется при изменении файлов.

Не нужно запоминать все команды. Важно понять концептуально:

  1. Существует такой генератор — Hugo. Он превращает Markdown-файлы в готовый HTML-сайт. Это удобно для блогов и документации.
  2. Hugo можно запускать через Docker. Тогда в вашей системе не появится лишних пакетов, а сам контейнер легко удалить или пересоздать.
  3. Контент живёт отдельно от кода. Все статьи лежат в content/, тема — в themes/, настройки — в hugo.toml. Это разделение позволяет спокойно обновлять тему или Hugo, не ломая свои материалы.
  4. Все файлы — обычный текст. Их удобно хранить в Git, делать резервные копии, переносить между компьютерами.

Остальное — это просто синтаксис, к которому привыкаешь со временем. А пока можно держать эту статью как шпаргалку и возвращаться к ней по мере необходимости.

Шпаргалка основных команд⬇

Запуск и остановка сайта:

docker compose up -d          # запустить сайт в фоне
docker compose down           # остановить и удалить контейнер
docker compose restart hugo   # перезапустить контейнер
docker compose logs -f hugo   # посмотреть логи в реальном времени

Сгенерировать сайт для публикации на хостинг:

docker compose run --rm hugo --gc --minify --baseURL=https://example.com/ # без запуска сервера соберёт сайт в каталоге public/
  • Эта команда нужна, когда вы хотите получить готовые файлы сайта для размещения на хостинге, а не смотреть его локально через http://localhost:1313/.
  • Не забудьте заменить URL на свой.

Создание нового контента:

# Вариант 1 — вручную создать файл в site/content/posts/

# Вариант 2 — через Hugo (используя архетипы темы):
docker compose run --rm hugo new posts/my-new-post.md

На практике я редко создаю и правлю файлы напрямую через консоль. Лично я пишу статьи в Obsidian (это очень удобный редактор Markdown), а для публикации использую небольшой Python-скрипт, который берёт рутину на себя:

  1. Скрипт копирует готовую статью из хранилища Obsidian прямо на сервер Hugo в site/content/posts/.
  2. Запускается сборка «боевой» версии сайта со всеми оптимизациями:
docker compose run --rm hugo --gc --minify --baseURL=https://example.com/
  1. Каталог site/public/ у меня сразу опубликован на локальном web-сервере. Поэтому я могу открыть его в браузере и посмотреть, как статья будет выглядеть именно в продакшене — без hugo server.
  2. После финальной проверки на тестовом сервере я просто синхронизирую каталог public/ с основным хостингом.

Такой подход позволяет писать статьи в комфортной среде, а деплой сводится к нажатию одной кнопки.