Héberger un site statique dans un conteneur

created: mercredi, déc. 10, 2025

La plupart de nos sites hébergés sont rendus statiquement, construits avec des outils comme Hugo, Zola ou Jekyll. En général, tous ces générateurs de site prennent en entrée un format simplifié (généralement Markdown) et génèrent du HTML bien défini en sortie. Reste alors la question : comment puis-je héberger un tel site ?

Il existe des fournisseurs spécialisés proposant de l’hébergement pour sites statiques, mais comme vous le savez, nous avons suivi une voie différente lors de la construction de notre infrastructure principale. Pour notre cloud, le dénominateur commun pour le déploiement est un conteneur. Et avec cela vient la question : comment passer d’une build de site statique à un conteneur qui héberge le site web ?

Commençons donc de zéro en générant une page très simple “hello world” avec Hugo.

hugo new site hello
cd hello
git init
git submodule add https://github.com/theNewDynamic/gohugo-theme-ananke.git themes/ananke
echo "theme = 'ananke'" >> hugo.toml

Pour vérifier la build, nous pouvons démarrer le serveur localement :

hugo serve

Puis notre propre page “Hello World” apparaît à l’adresse http://localhost:1313

Jusque-là tout va bien. Maintenant, envisageons de mettre cela dans un conteneur.

Two Approaches to Containerization

Techniquement, nous pouvons emprunter deux chemins différents ici :

  1. Mettre tout le code source dans un conteneur et exécuter le processus de build à l’intérieur
  2. Construire localement et ne copier que les sorties dans le conteneur

Pour rendre notre exemple plus reproductible et moins dépendant des environnements locaux, nous choisissons l’option 1 pour ce scénario. Cela rend également les pipelines CI/CD plus propres puisque l’environnement de build est entièrement défini dans le Dockerfile.

Building the Container Image

Une image de conteneur commence toujours par une image de base. Pour cela, nous utilisons Alpine Linux car elle est légère (environ 5Mo) et fournit assez d’outils pour notre projet.

Nous utiliserons un build multi-étapes, une technique supportée par les outils modernes de construction d’images qui nous permet d’utiliser une image pour la construction et une autre pour l’exécution. Cela garde notre image finale petite en excluant les outils de build dont nous n’avons pas besoin à l’exécution.

Stage 1: Build

FROM alpine AS build
RUN apk add --no-cache hugo
WORKDIR /src
ADD . .
RUN hugo --minify

Dans cette étape, nous :

Le site généré se retrouve dans /src/public/.

Stage 2: Runtime

FROM alpine AS runner
RUN apk add --no-cache lighttpd
COPY --from=build /src/public /var/www/localhost/htdocs
EXPOSE 80
CMD ["lighttpd", "-D", "-f", "/etc/lighttpd/lighttpd.conf"]

Dans cette étape, nous :

The Complete Dockerfile

Voici le Dockerfile complet combinant les deux étapes :

# Stage 1: Build the static site
FROM alpine AS build
RUN apk add --no-cache hugo
WORKDIR /src
ADD . .
RUN hugo --minify

# Stage 2: Serve with lighttpd
FROM alpine AS runner
RUN apk add --no-cache lighttpd
COPY --from=build /src/public /var/www/localhost/htdocs
EXPOSE 80
CMD ["lighttpd", "-D", "-f", "/etc/lighttpd/lighttpd.conf"]

Enregistrez ceci en tant que Dockerfile à la racine de votre projet Hugo.

Building and Running Locally

Vous pouvez utiliser n’importe quel outil de conteneur compatible OCI comme Podman ou Docker. Les exemples ci-dessous utilisent docker, mais podman fonctionne comme un remplaçant direct.

Pour construire l’image :

docker build -t my-website .

Pour l’exécuter localement :

docker run -p 8080:80 --rm my-website

Votre site est maintenant disponible à l’adresse http://localhost:8080

Le flag -p 8080:80 mappe le port 8080 de votre machine au port 80 à l’intérieur du conteneur. Le flag --rm supprime automatiquement le conteneur lorsqu’il s’arrête.

Why This Approach Works Well

Cette configuration présente plusieurs avantages :

  1. Taille d’image réduite : L’image finale contient uniquement Alpine (~5Mo) + lighttpd (~1Mo) + vos fichiers HTML. Pas de Node.js, pas de Ruby, pas d’outils de build alourdissant votre image de production.

  2. Builds reproductibles : La même version exacte de Hugo s’exécute en CI comme en local, éliminant les problèmes du type “ça marche sur ma machine”.

  3. Démarrage rapide : lighttpd démarre en quelques millisecondes, ce qui rend cette solution parfaite pour les déploiements scale-to-zero sur DTZ.

  4. Sécurité : Le conteneur de production a une surface d’attaque minimale — juste un serveur de fichiers statiques sans runtime dynamique.

Architecture Considerations

Si vous construisez votre image sur un Mac Apple Silicon (ARM64) ou une autre architecture non standard, rappelez-vous que les serveurs tournent généralement sur AMD64 (x86_64). Pour vous assurer que votre conteneur s’exécute correctement sur DTZ (et la plupart des autres fournisseurs cloud), vous devriez spécifier explicitement la plateforme cible lors de la build.

Changez votre commande de build pour :

docker build --platform linux/amd64 -t my-website .

Cela indique à Docker de cross-compiler l’image pour les serveurs Linux standards, garantissant la compatibilité quel que soit la machine sur laquelle vous construisez.

Deploying to DownToZero

Une fois votre image construite, vous pouvez la pousser vers un registre de conteneurs et la déployer sur DTZ. Si vous utilisez notre registre de conteneurs :

# Tag for DTZ registry
docker tag my-website YOUR_CONTEXT_ID.cr.dtz.dev/my-website:latest

# Login and push
docker login YOUR_CONTEXT_ID.cr.dtz.dev -u apikey
docker push YOUR_CONTEXT_ID.cr.dtz.dev/my-website:latest

Créez ensuite un service de conteneur dans le tableau de bord DTZ pointant vers votre image. Le service gérera automatiquement les certificats TLS, la mise à l’échelle et le routage.

Pour des déploiements automatisés à chaque commit, consultez notre GitHub Action for seamless deployments.

Adapting for Other Static Site Generators

Le même schéma fonctionne pour d’autres générateurs. Voici les changements clés :

For Zola:

FROM alpine AS build
RUN apk add --no-cache zola
WORKDIR /src
ADD . .
RUN zola build

For Jekyll:

FROM ruby:alpine AS build
RUN apk add --no-cache build-base
RUN gem install bundler jekyll
WORKDIR /src
ADD . .
RUN bundle install
RUN bundle exec jekyll build

L’étape runtime reste la même — il suffit de copier depuis /src/public (Zola) ou /src/_site (Jekyll) vers la racine de documents de lighttpd.

Wrapping Up

Containeriser des sites statiques est simple une fois que vous comprenez le schéma : construire dans une étape, servir depuis une autre. Le résultat est un conteneur minuscule, rapide et sécurisé, parfait pour les déploiements cloud modernes.

Cette approche s’aligne bien avec notre philosophie chez DTZ — utilisation minimale des ressources, démarrages à froid rapides et infrastructure qui se met à l’échelle jusqu’à zéro lorsqu’elle n’est pas utilisée. Un site statique dans un conteneur d’environ 10Mo qui démarre instantanément est à peu près aussi efficace que peut l’être l’hébergement web.