Perché Hugo e non Gatsby o Next.js#
Gatsby ha avuto il suo momento, ma l’ecosistema si è spostato. Il progetto è stato acquisito, la manutenzione è rallentata e la maggior parte dei team che lo usavano è migrata a Next.js o a un generatore statico. Hugo occupa una nicchia diversa: non dipende da React, non richiede Node.js e compila migliaia di pagine in meno di un secondo. Se stai costruendo un blog o un sito di documentazione, e non una vera applicazione React, Hugo è lo strumento giusto.
Questo blog gira su Hugo deployato su GitHub Pages tramite GitHub Actions. Tutto quello descritto qui è il setup reale, non un esempio teorico.
flowchart LR
A([Scrivere Markdown]) --> B[git push su main]
B --> C[Workflow GitHub Actions]
C --> D[hugo --minify]
D --> E[Deploy su GitHub Pages]
E --> F([Sito online])
Installare Hugo#
Hugo è distribuito come singolo binario. L’approccio consigliato su macOS è Homebrew:
brew install hugoSu Linux, scarica il binario extended dalla pagina delle release. La versione extended è richiesta dai temi che utilizzano SCSS.
hugo version
# hugo v0.155.3+extended darwin/arm64Installa sempre la variante extended. Molti temi dipendono dalla compilazione SCSS, disponibile solo nella build extended.
Creare un nuovo sito#
hugo new site my-blog
cd my-blogQuesto crea la struttura di directory di base:
my-blog/
archetypes/ # template per i contenuti
content/ # i tuoi file markdown vanno qui
layouts/ # override dei template
static/ # file copiati cos come sono (favicon, CSS, immagini)
themes/ # i submodule dei temi vanno qui
config.toml # configurazione del sitoAggiungere un tema come submodule Git#
I temi in Hugo vengono aggiunti come submodule git in modo da poterli aggiornare indipendentemente dai contenuti. Questo e il passaggio critico che le pipeline CI spesso sbagliano.
git init
git submodule add https://github.com/nunocoracao/blowfish.git themes/blowfishPoi fai riferimento al tema in config.toml:
baseURL = "https://tuonomeutente.github.io/"
theme = "blowfish"Il baseURL deve corrispondere esattamente all’URL di deployment. Un errore qui causa link rotti nella build di produzione.
Scrivere i post#
Struttura delle directory#
Tutti i post del blog si trovano in content/posts/. Un file Markdown per ogni post.
hugo new posts/il-mio-primo-post.mdFront matter#
Hugo legge i metadati dal blocco front matter in cima a ogni file:
---
title: "Il mio primo post"
date: 2025-06-01T10:00:00+01:00
draft: false
tags: ["go", "aws", "devops"]
---
Il tuo contenuto qui.Imposta draft: true mentre scrivi. I post in bozza vengono esclusi dalle build di produzione per default. Usa hugo server -D in locale per vedere le bozze.
Sviluppo locale#
hugo server -DQuesto avvia il server di sviluppo su http://localhost:1313 con live reload. Le modifiche a contenuti o template si riflettono nel browser istantaneamente senza bisogno di aggiornare la pagina.
Il workflow di GitHub Actions#
Questo e il workflow usato da questo blog. Si attiva al push su main, compila il sito con Hugo extended e fa il deploy su un repository GitHub Pages separato usando un personal access token.
name: Publish github page
on:
push:
branches:
- main
pull_request:
workflow_dispatch:
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: true # necessario per i submodule del tema
fetch-depth: 0 # necessario per .GitInfo e .Lastmod
- name: Setup Hugo
run: |
wget -q https://github.com/gohugoio/hugo/releases/download/v0.147.6/hugo_extended_0.147.6_linux-amd64.deb
sudo dpkg -i hugo_extended_0.147.6_linux-amd64.deb
- name: Build
run: hugo --minify
- name: Deploy
uses: peaceiris/actions-gh-pages@v4
if: github.ref == 'refs/heads/main'
with:
personal_token: ${{ secrets.PERSONAL_TOKEN }}
external_repository: tuonomeutente/tuonomeutente.github.io
publish_branch: main
publish_dir: ./publicAlcuni aspetti da tenere a mente.
submodules: true e obbligatorio. Senza di esso, la directory del tema e vuota e Hugo fallisce silenziosamente o con un errore confuso.
fetch-depth: 0 recupera l’intera storia git. Serve se il tuo tema usa .GitInfo o .Lastmod per mostrare le date di ultima modifica.
hugo --minify riduce le dimensioni dell’output HTML, CSS e JavaScript. Usalo sempre in produzione.
Deploy su un repository separato#
Il pattern sopra usa external_repository e personal_token. Torna utile quando il repository dei contenuti e privato o quando vuoi tenere gli artefatti di build separati dalla sorgente.
Deploy sul branch gh-pages dello stesso repository. Setup piu semplice, funziona bene per i siti di progetto.
- uses: peaceiris/actions-gh-pages@v4
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./publicDeploy su un repository separato tuonomeutente.github.io. Necessario per le GitHub user pages (che devono trovarsi in un repo col tuo nome utente).
- uses: peaceiris/actions-gh-pages@v4
with:
personal_token: ${{ secrets.PERSONAL_TOKEN }}
external_repository: tuonomeutente/tuonomeutente.github.io
publish_branch: main
publish_dir: ./publicCrea un personal access token con scope repo e salvalo come PERSONAL_TOKEN nei secrets del repository sorgente.
Dominio personalizzato#
Per usare un dominio personalizzato, crea un file CNAME in static/:
blog.tuodominio.comPoi configura il DNS con un record CNAME che punta blog.tuodominio.com a tuonomeutente.github.io. GitHub gestisce HTTPS automaticamente tramite Let’s Encrypt.
Configurazione di Hugo#
Un config.toml minimale per questo setup:
baseURL = "https://tuonomeutente.github.io/"
defaultContentLanguage = "en"
theme = "blowfish"
pagination.pagerSize = 10
enableRobotsTXT = true
buildDrafts = false
buildFuture = false
googleAnalytics = "G-XXXXXXXXXX"
[markup.goldmark.renderer]
unsafe = true
[markup.highlight]
noClasses = true
style = "nord"Dimenticare --minify nel passaggio di build
hugo dal workflow locale, deployerai asset non minificati. La differenza nelle dimensioni delle pagine puo essere significativa con molti post. Usa sempre hugo --minify in CI.Impostare il baseURL sbagliato
baseURL non corrisponde all’URL di deployment, tutti i link e gli asset saranno rotti in produzione. Il server locale non intercetta questo problema perche sovrascrive baseURL automaticamente. Verifica eseguendo hugo --minify in locale e controllando i link in public/index.html.Submodule mancante nel checkout CI
actions/checkout@v4 non recupera i submodule per default. Senza submodules: true, la directory themes/ e vuota. Hugo fallira o produrra un sito bianco a seconda della configurazione.Post in bozza deployati in produzione
draft: true vengono esclusi solo se non passi -D al comando di build. Il problema si presenta quando dimentichi di cambiare il flag prima di fare il push. Prendi l’abitudine di controllare hugo list drafts prima di fare merge su main.Non impostare fetch-depth: 0
actions/checkout esegue un clone superficiale. Questo fa si che .GitInfo e .Lastmod restituiscano valori vuoti nei template. Se il tuo tema mostra le date di ultima modifica, imposta fetch-depth: 0.Se vuoi approfondire uno qualsiasi di questi argomenti, offro sessioni di coaching 1:1 per ingegneri che lavorano su integrazione AI, architettura cloud e platform engineering. Prenota una sessione (50 EUR / 60 min) o scrivimi a manuel.fedele+website@gmail.com.