Я долго пользовался WordPress’ом для своего блога и каждый месяц что-то ломалось — то обновление плагина положило admin-панель, то PHP-FPM начал жрать память, то комментаторы прорвались через капчу. В итоге переехал на Hugo, и за полтора года ни одного инцидента. Расскажу, как поднять минимальный технический блог за вечер, без оверинженеринга.
Почему Hugo
- Один статический бинарь, никаких зависимостей
- Генерация быстрая — у меня 200 постов рендерится за 1.5 секунды
- Syntax highlighting через Chroma из коробки
- Темы простые, можно написать свою за день
- Деплой — это
rsyncсгенерированной папки на nginx, и всё
Альтернативы — Jekyll, Astro, Zola, Eleventy. Jekyll медленный, Astro — это уже JS-фреймворк со своими заморочками, Zola — нишевый, Eleventy — слишком гибкий, теряешься в выборе. Hugo — золотая середина для технических блогов.
Установка
Hugo распространяется как один бинарь:
HUGO_VERSION=0.140.0
wget https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.tar.gz
tar -xzf hugo_extended_${HUGO_VERSION}_linux-amd64.tar.gz
mv hugo /usr/local/bin/
hugo version
Берите именно hugo_extended — без него не работает SCSS/Sass, который нужен большинству тем.
На Debian/Ubuntu можно через apt install hugo, но там обычно версия с отставанием в полгода. Лучше с github.
Создание сайта
hugo new site myblog
cd myblog
git init
Структура:
myblog/
├── archetypes/
├── content/
│ └── posts/
├── data/
├── layouts/
├── static/
├── themes/
└── hugo.toml
Минимальный hugo.toml:
baseURL = "https://example.com/"
languageCode = "ru-ru"
title = "Мой блог"
theme = "minimal"
[params]
description = "Записки практикующего инженера"
author = "Anon"
[markup]
[markup.highlight]
style = "monokai"
lineNos = false
codeFences = true
guessSyntax = true
tabWidth = 4
[permalinks]
posts = "/:slug/"
style = "monokai" — стиль подсветки кода. Полный список — hugo gen chromastyles --help.
Тема
Можно взять готовую, можно написать свою. Готовых на themes.gohugo.io много, но они часто перегружены — таргетинг, аналитика, comments, share-buttons. Для технического блога нужен минимум.
Я написал свою на 4 шаблона. Структура themes/minimal/:
themes/minimal/
├── layouts/
│ ├── _default/
│ │ ├── baseof.html
│ │ ├── list.html
│ │ └── single.html
│ ├── index.html
│ └── partials/
│ ├── head.html
│ └── footer.html
├── static/
│ └── css/
│ └── main.css
└── theme.toml
Базовый шаблон (baseof.html):
<!DOCTYPE html>
<html lang="{{ .Site.LanguageCode }}">
<head>
{{ partial "head.html" . }}
</head>
<body>
<header>
<h1><a href="{{ .Site.BaseURL }}">{{ .Site.Title }}</a></h1>
</header>
<main>
{{ block "main" . }}{{ end }}
</main>
{{ partial "footer.html" . }}
</body>
</html>
Single-post (single.html):
{{ define "main" }}
<article>
<h1>{{ .Title }}</h1>
<time>{{ .Date.Format "2 January 2006" }}</time>
{{ .Content }}
<p>Теги: {{ range .Params.tags }}<a href="/tags/{{ . }}/">{{ . }}</a> {{ end }}</p>
</article>
{{ end }}
Главная (index.html):
{{ define "main" }}
<ul class="post-list">
{{ range first 20 (where .Site.RegularPages "Section" "posts") }}
<li>
<time>{{ .Date.Format "02.01.2006" }}</time>
<a href="{{ .Permalink }}">{{ .Title }}</a>
<p>{{ .Params.summary }}</p>
</li>
{{ end }}
</ul>
{{ end }}
Первый пост
Архетип для новых постов (archetypes/posts.md):
+++
title = "{{ replace .Name "-" " " | title }}"
date = "{{ .Date }}"
draft = false
tags = []
categories = []
summary = ""
+++
Создаём пост:
hugo new posts/first-post.md
Hugo использует архетип и создаёт файл с заполненным frontmatter. Пишем содержимое в Markdown, сохраняем.
Локальный preview
hugo server -D
-D — показывать drafts. Идём на http://localhost:1313/, видим блог. Hugo делает live-reload, при сохранении файла страница обновляется сама.
Билд и деплой
Билд:
hugo --minify
Сгенерированный сайт лежит в public/. Это просто статика — HTML, CSS, JS. Деплоим на любой статик-сервер. Простейший вариант — nginx:
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
root /var/www/blog;
index index.html;
location / {
try_files $uri $uri/ $uri.html =404;
}
location ~* \.(css|js|woff2)$ {
expires 30d;
add_header Cache-Control "public, immutable";
}
}
Деплой одной строкой:
hugo --minify && rsync -avz --delete public/ user@server:/var/www/blog/
Или через CI — github actions, gitlab pipelines, что угодно. Hugo не требует ничего особого, билдится в одну команду в любом раннере.
Подсветка кода
В блоках кода с language hint Chroma подсветит автоматом:
server {
listen 80;
}
Стили генерируются в CSS — Hugo может сгенерировать их в файл:
hugo gen chromastyles --style=monokai > static/css/syntax.css
И подключить в head.html:
<link rel="stylesheet" href="/css/syntax.css">
Если в hugo.toml указан style = "..." — Hugo инлайнит стили в HTML напрямую, без отдельного файла. Это удобнее, но раздувает страницу.
RSS
Hugo генерирует RSS-фид автоматом. Доступен по /index.xml или /posts/index.xml. Просто подключите ссылку в шапке:
<link rel="alternate" type="application/rss+xml"
title="{{ .Site.Title }}"
href="{{ .Site.BaseURL }}index.xml">
Что я бы НЕ делал
- Не вешайте Disqus или другие comments-системы. Они тащат трекеры, замедляют сайт и устаревают
- Не подключайте google analytics — есть plausible или umami, они легче
- Не делайте темы с тёмной/светлой схемой через JS-переключалку —
@media (prefers-color-scheme)в CSS решает то же без скриптов - Не пишите сразу 10 разных layout’ов — нужен один post-template и один list-template, остальное появится по мере надобности
Итог
Hugo поднимается за час даже без опыта. Бинарь, тема, пара постов, rsync на nginx — и блог в проде. Никакого PHP, никакой базы, никаких обновлений плагинов. Если что-то ломается — это вы сами сломали. Простота и стабильность.