Héberger un site Hugo sur Raspberry Pi avec Docker, Traefik et GitHub Actions
Cet article fournit un guide complet pour créer et déployer un site web sur une instance serveur locale. En utilisant GitHub Actions, vous apprendrez comment déployer automatiquement de nouvelles versions chaque fois que des ressources sont ajoutées. J’expliquerai également comment configurer Traefik, un proxy inverse qui—avec la configuration DNS et routeur—rendra votre site web accessible au public.
Prérequis
Il y a quelques prérequis qui ne seront pas détaillés dans cet article. D’abord, vous avez besoin d’un réseau local avec une instance serveur fonctionnelle, et un accès à cette instance (soit directement, soit via SSH). Dans mon cas, c’est un simple Raspberry Pi.
Vous devriez aussi posséder un nom de domaine et avoir accès à ses enregistrements DNS pour rediriger le trafic entrant vers votre routeur. De plus, vous avez besoin d’accéder à la configuration de votre routeur pour ajouter des règles de redirection de port (port forwarding). Votre fournisseur internet doit vous avoir assigné une adresse IP publique statique/fixe (sinon, regardez du côté des services de DNS dynamique).
Vous avez besoin d’un compte GitHub existant configuré pour cloner, commiter et pusher vers vos dépôts. Enfin, vous avez besoin de Docker installé et tournant à la fois sur votre ordinateur principal et votre serveur.
Créer et pusher votre site web
Je couvrirai cette partie brièvement puisque j’ai déjà écrit un article détaillé sur ce sujet, accessible ici. Pour ce tutoriel, j’ai choisi l’exemple d’un blog de cuisine utilisant le thème cuisine-book par Kien Nguyen-Tuan, qui correspond parfaitement à mes besoins. J’ai créé un dépôt GitHub (privé) nommé “cooking” et l’ai cloné sur ma machine de développement.
Dépôt GitHub pour le site web “cooking”
Ensuite, j’ai créé un nouveau site Hugo, ajouté ce thème, et l’ai lancé localement :
git clone https://github.com/letrome/cooking.git
cd cooking
hugo new site cooking
git submodule add https://github.com/ntk148v/hugo-cuisine-book themes/cuisine-book
hugo server --minify --theme cuisine-book
Note : J’ai testé cela sur macOS et Ubuntu via WSL2, et ça a marché comme un charme ! Cependant, vous pouvez rencontrer l’erreur suivante :
[...]
executing "partials/head.html" at <css>: can't evaluate field Sass in type interface {}
[...]
Ceci est dû à un changement cassant (breaking change) introduit dans les versions récentes de Hugo. Selon la fraîcheur de votre template, vous pourriez avoir besoin d’éditer le thème pour le corriger. Si cela arrive, ouvrez le fichier ./themes/cuisine-book/layouts/partials/head.html et remplacez css.Sass par resources.ToCSS. Pour vérifier que votre site web tourne correctement, visitez l’URL montrée après avoir exécuté la dernière commande, typiquement http://localhost:1313.
Le site web, accessible à localhost:1313
Vous pouvez maintenant commiter et pusher vos changements vers votre dépôt GitHub.
Dockeriser cette app
D’abord, créons un fichier nommé Dockerfile à la racine du projet. Voici le contenu :
FROM hugomods/hugo AS builder
COPY . /site
WORKDIR /site
RUN hugo --minify --theme cuisine-book --destination /public
FROM nginx:alpine
COPY --from=builder /public /usr/share/nginx/html
EXPOSE 80
Explorons brièvement ce que fait ce Dockerfile. Le premier bloc gère la génération du site web. Nous utilisons hugomods/hugo, une image Docker communautaire pour construire des sites Hugo (ligne 1). Nous copions tous les fichiers nécessaires dans un dossier nommé site (ligne 2). Depuis ce dossier (ligne 3), nous lançons la génération des fichiers statiques (ligne 4).
Le second bloc sert le site web en utilisant Nginx. En partant d’une image Nginx basée sur Alpine (ligne 1), nous copions les fichiers générés précédemment vers /usr/share/nginx/html (ligne 2)—le répertoire par défaut où Nginx expose les ressources. Enfin, nous spécifions que le port 80 du conteneur Docker sera exposé.
Ajouter Docker Compose
Pour simplifier le processus de déploiement et éviter de taper de longues commandes, créons un fichier docker-compose.yaml à la racine de votre dépôt :
services:
cooking-app:
build: .
image: cooking:latest
container_name: cooking-container
ports:
- "81:80"
restart: always
Ici j’ai lié le port 81 de l’hôte au port 80 du conteneur. Cela anticipe notre configuration finale où Traefik écoutera sur les ports 80 et 443.
Maintenant, vous pouvez construire et lancer votre site web avec une seule commande : docker compose up -d
Ouvrez votre navigateur web pour tester que votre site web est accessible à http://localhost:81.
Le site web, accessible à localhost:81
Automatiser le build et le déploiement avec GitHub Actions
Cette section montre comment utiliser GitHub Actions pour déployer automatiquement les mises à jour vers votre serveur local (le Raspberry Pi). Le processus implique trois étapes : configurer un runner auto-hébergé, configurer le workflow de votre dépôt, et vérifier l’installation.
Ajouter le runner auto-hébergé
Sur la page de votre dépôt sur github.com, allez dans Settings. Sous la catégorie Actions > Runners dans la barre latérale gauche, cliquez sur le bouton vert New self-hosted runner.
La section “Runners” sur GitHub
Sélectionnez l’image et l’architecture du runner qui correspondent à votre serveur (typiquement Linux et ARM64 pour un Raspberry Pi). Copiez et collez les instructions fournies dans le terminal de votre serveur.
La page “Add new self-hosted runner”
Lors de l’exécution du script config.sh, appuyez sur entrée à chaque invite pour utiliser les valeurs par défaut. Une fois fait, vous devriez avoir un runner fonctionnel.
De retour dans les paramètres de votre projet GitHub, sous Actions > Runners, vous devriez maintenant voir votre runner avec le statut Idle.
Le runner avec le statut “Idle”
Note : Par défaut, ce runner ne démarre pas automatiquement au boot. Pour résoudre cela, je recommande de l’installer comme un service en utilisant systemd. Consultez cet article pour les détails.
Configurer le workflow GitHub Action
Nous devons définir le workflow. Quand un commit est pushé sur la branche master, le Dockerfile s’exécute, crée une image, et la déploie sur l’instance du runner.
Créez le fichier .github/workflows/deploy.yml. Grâce à notre fichier docker-compose.yaml, cette étape est maintenant très propre :
name: Deploy Hugo site to Docker
on:
push:
branches: ["master"]
workflow_dispatch:
concurrency:
group: "docker-deployment"
cancel-in-progress: false
defaults:
run:
shell: bash
jobs:
build-and-deploy:
runs-on: self-hosted
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: recursive
fetch-depth: 0
- name: Deploy with Docker Compose
run: |
docker compose up -d --build --force-recreate
L’étape Checkout repository utilise submodules: recursive pour assurer que les fichiers du thème sont récupérés. L’étape finale demande simplement à Docker Compose de reconstruire l’image et recréer le conteneur si des changements sont détectés.
Tester la configuration
Commitez le fichier YAML de déploiement et pushez-le vers la branche master. Dans votre dépôt GitHub, cliquez sur l’onglet Actions. Vous devriez voir un workflow en cours d’exécution.
Le workflow GitHub Actions en cours d’exécution
Une fois qu’il réussit, ouvrez l’URL correspondant à l’IP de votre instance (ex. http://192.168.1.7:81). Votre site web devrait apparaître.
DNS et Routage
DNS
D’abord, identifiez l’adresse IP publique de votre routeur (en utilisant un site comme nordvpn.com/what-is-my-ip). Puis, ajoutez un enregistrement A (A record) à votre zone DNS :
-
Nom : Le sous-domaine (ex.
cookingpourcooking.votredomaine.com). -
Type d’enregistrement :
A -
Valeur : L’IP publique de votre routeur.
-
TTL :
14400(ou défaut).
Visiter l’URL devrait maintenant retourner ERR_CONNECTION_REFUSED au lieu de ERR_NAME_NOT_RESOLVED. C’est un bon progrès.
Configuration du Routeur
Accédez à l’interface de votre routeur (souvent 192.168.1.1 ou 192.168.1.254).
-
IP Statique : Assignez une IP locale statique à votre serveur (Raspberry Pi) dans les paramètres DHCP.
-
Redirection de Port (Port Forwarding) : Redirigez les ports externes 80 et 443 (TCP) vers l’IP statique de votre serveur sur les ports 80 et 443 respectivement.
Installation et Configuration de Traefik
Traefik est un proxy inverse puissant et cloud-native. Nous l’utiliserons pour router le trafic externe vers votre conteneur de manière sécurisée.
Créez un fichier docker-compose.yaml spécifique pour Traefik (ou ajoutez-le à votre stack existante) :
services:
traefik:
restart: always
image: traefik:latest
container_name: traefik
ports:
- "443:443"
- "80:80"
volumes:
- ./traefik.yaml:/etc/traefik/traefik.yaml
- /var/log/traefik.log:/var/log/traefik.log
- ./acme.json:/etc/traefik/acme.json
- ./conf:/etc/traefik/conf
traefik.yaml
Créez le fichier de configuration principal traefik.yaml :
entryPoints:
http:
address: ":80"
https:
address: ":443"
asDefault: true
log:
level: INFO
filePath: /var/log/traefik.log
format: json
providers:
file:
directory: /etc/traefik/conf
certificatesResolvers:
letsencrypt:
acme:
email: votreemail@votredomaine.com
storage: /etc/traefik/acme.json
httpChallenge:
entryPoint: http
Configuration des fichiers
Lancez les commandes suivantes pour créer les fichiers nécessaires et définir les permissions :
sudo touch /var/log/traefik.log
touch acme.json
sudo chown $USER:$USER acme.json
sudo chmod 600 acme.json
mkdir conf
Note : Le chmod 600 sur acme.json est critique ; Traefik échouera au démarrage si les permissions sont trop ouvertes.
cooking.yaml (Configuration Dynamique)
Créez conf/cooking.yaml pour dire à Traefik comment router le trafic vers votre site spécifique :
http:
routers:
cooking:
rule: "Host(`cooking.votredomaine.com`)"
entryPoints:
- "https"
service: service-cooking
tls:
certResolver: letsencrypt
services:
service-cooking:
loadBalancer:
servers:
- url: http://192.168.1.7:81
Assurez-vous de mettre à jour le nom de domaine et l’adresse IP dans le champ url.
Conclusion
Lancez docker compose up -d pour démarrer Traefik. Attendez quelques instants que le certificat se génère, et votre site devrait être en ligne et sécurisé avec HTTPS !