Zola #2 - Mise en place

Cet article fait suite à Zola #1 - Comment j'ai choisi mon CMS/SSG en 2026 et décrit les configurations possibles pour une exploitation de Zola. Les étapes décrites dans ce document sont adaptées à GitLab et FreeBSD.

Mise en place de Zola

J'envisage 2 scénarios pour la mise en place de Zola:

  1. Scénario PULL: le serveur de production se tiens à jour périodiquement vis à vis des sources du site. Il possède une installation locale de Zola et peut lancer un build quand il détecte une mise à jour de contenu sur le site. Dans ce cas, le serveur "pull" les changements depuis GitLab puis build et publie directement les nouveaux contenus.

  2. Scénario PUSH: le serveur de production ne fait rien et ne possède pas Zola. Il possède uniquement un serveur web et autorise des connexions ssh/rsync depuis GitLab. Dans ce cas, il faut mettre en place une pipeline CI/CD sur GitLab pour lancer une image docker contenant Zola et faire un build CI/CD du site. Si le build est en succès il faut alors deployer le site généré sur le serveur de production via rsync.

Scénario PULL

Sur mon serveur, équipé en FreeBSD 15.0, je dois recompiler Zola dans sa version 0.23.2 car c'est la version que j'ai utilisée pour écrire le site et cette version n'est pas encore disponible sous FreeBSD.

Le plan de compilation

 # installer les outils de build
 $ pkg install rust git pkgconf

 # récupérer les sources à jour de Zola
 $ git clone https://github.com/getzola/zola.git
 
 # basculer sur la bonne version: 'master' correspond à 0.23.2 
 $ cd zola
 $ git checkout master
 
 # lancer le build 
 $ cargo build --release

Les première étapes déroulent sans le moindre souci:

$ pkg install rust git pkgconf
The following 14 package(s) will be affected (of 0 checked):

New packages to be INSTALLED:
        git: 2.54.0 [FreeBSD-ports]
        p5-Authen-SASL: 2.1900 [FreeBSD-ports]
        p5-Crypt-URandom: 0.54 [FreeBSD-ports]
        p5-Digest-HMAC: 1.05 [FreeBSD-ports]
        p5-Error: 0.17030 [FreeBSD-ports]
        p5-IO-Socket-SSL: 2.099 [FreeBSD-ports]
        p5-MIME-Base32: 1.303 [FreeBSD-ports]
        p5-MIME-Base64: 3.16 [FreeBSD-ports]
        p5-Mozilla-CA: 20250602 [FreeBSD-ports]
        p5-Net-SSLeay: 1.94 [FreeBSD-ports]
        p5-URI: 5.34 [FreeBSD-ports]
        pkgconf: 2.4.3_1,1 [FreeBSD-ports]
        python312: 3.12.13_3 [FreeBSD-ports]
        rust: 1.96.1 [FreeBSD-ports]

Number of packages to be installed: 14

The process will require 2 GiB more space.
228 MiB to be downloaded.

[...]
Checking integrity... done (0 conflicting)
[ 1/14] Installing p5-Crypt-URandom-0.54...
[ 1/14] Extracting p5-Crypt-URandom-0.54: 100%
[ 2/14] Installing p5-Digest-HMAC-1.05...
[ 2/14] Extracting p5-Digest-HMAC-1.05: 100%
[ 3/14] Installing p5-Authen-SASL-2.1900...
[ 3/14] Extracting p5-Authen-SASL-2.1900: 100%
[ 4/14] Installing p5-Error-0.17030...
[ 4/14] Extracting p5-Error-0.17030: 100%
[ 5/14] Installing p5-MIME-Base32-1.303...
[ 5/14] Extracting p5-MIME-Base32-1.303: 100%
[ 6/14] Installing p5-MIME-Base64-3.16...
[ 6/14] Extracting p5-MIME-Base64-3.16: 100%
[ 7/14] Installing p5-Mozilla-CA-20250602...
[ 7/14] Extracting p5-Mozilla-CA-20250602: 100%
[ 8/14] Installing p5-Net-SSLeay-1.94...
[ 8/14] Extracting p5-Net-SSLeay-1.94: 100%
[ 9/14] Installing p5-URI-5.34...
[ 9/14] Extracting p5-URI-5.34: 100%
[10/14] Installing p5-IO-Socket-SSL-2.099...
[10/14] Extracting p5-IO-Socket-SSL-2.099: 100%
[11/14] Installing pkgconf-2.4.3_1,1...
[11/14] Extracting pkgconf-2.4.3_1,1: 100%
[12/14] Installing python312-3.12.13_3...
[12/14] Extracting python312-3.12.13_3: 100%
[13/14] Installing git-2.54.0...
===> Creating groups
Creating group 'git_daemon' with gid '964'
===> Creating users
Creating user 'git_daemon' with uid '964'
[13/14] Extracting git-2.54.0: 100%
[14/14] Installing rust-1.96.1...
[14/14] Extracting rust-1.96.1: 100%
$ git clone https://github.com/getzola/zola.git
Cloning into 'zola'...
remote: Enumerating objects: 32900, done.
remote: Counting objects: 100% (485/485), done.
remote: Compressing objects: 100% (265/265), done.
remote: Total 32900 (delta 325), reused 220 (delta 220), pack-reused 32415 (from 3)
Receiving objects: 100% (32900/32900), 119.73 MiB | 11.22 MiB/s, done.
Resolving deltas: 100% (18702/18702), done.
$ cd zola
$ git switch master
Already on 'master'
Your branch is up to date with 'origin/master'.

Tout est prêt pour le build. Pourtant le build échouera avant la fin par manque RAM et de swap. En effet, Rust procède à un grand nombre d'optimisations très couteuses en mémoire lors de la phase finale du build. Mon modeste VPS avec 2Go de RAM et 1Go de swap est arrivé à saturation avant la fin du build.

Build adapté au capacités du VPS

Pour limiter le nombre de threads mais aussi les optimiseurs du linker (et d'autres), on préfère lancer la commande de build avec ces paramètres:

CARGO_PROFILE_RELEASE_LTO=false \
CARGO_PROFILE_RELEASE_CODEGEN_UNITS=16 \
cargo build --release -j 1
   Updating crates.io index
  Downloaded anstyle-query v1.1.5
  Downloaded ahash v0.8.12
  Downloaded equivalent v1.0.2
  Downloaded env_filter v2.0.0
  Downloaded block-buffer v0.10.4
    [...]
  Downloaded libc v0.2.189
  Downloaded giallo v0.5.2
  Downloaded encoding_rs v0.8.35
  Downloaded webp v0.3.1
  Downloaded rust-stemmers v1.2.0
  Downloaded libwebp-sys v0.9.6
  Downloaded 485 crates (66.9MiB) in 7.47s (largest was `aws-lc-sys` at 9.2MiB)
   Compiling proc-macro2 v1.0.107
   Compiling unicode-ident v1.0.24
   Compiling quote v1.0.47
   Compiling syn v2.0.119
   Compiling syn v3.0.3
   Compiling cfg-if v1.0.4
   Compiling libc v0.2.189
   [...]
   Compiling errors v0.23.2 (/home/freebsd/zola/components/errors)
   Compiling utils v0.23.2 (/home/freebsd/zola/components/utils)
   Compiling config v0.23.2 (/home/freebsd/zola/components/config)
   Compiling content v0.23.2 (/home/freebsd/zola/components/content)
   Compiling render v0.23.2 (/home/freebsd/zola/components/render)
   Compiling markdown v0.23.2 (/home/freebsd/zola/components/markdown)
   Compiling imageproc v0.23.2 (/home/freebsd/zola/components/imageproc)
   Compiling templates v0.23.2 (/home/freebsd/zola/components/templates)
   Compiling zola v0.23.2 (/home/freebsd/zola)
   Compiling search v0.23.2 (/home/freebsd/zola/components/search)
   Compiling link_checker v0.23.2 (/home/freebsd/zola/components/link_checker)
   Compiling site v0.23.2 (/home/freebsd/zola/components/site)
   Compiling axum v0.8.9
   Compiling notify-debouncer-full v0.7.0
   Compiling env_logger v0.11.11
   Compiling ctrlc v3.5.2
   Compiling open v5.4.1
   Compiling console v0.23.2 (/home/freebsd/zola/components/console)
   Finished `release` profile [optimized] target(s) in 46m 26s

On peut donc vérifier que la version du build est bien la 0.23.2 et ensuite l'installer sur le filesystem:

$ ./target/release/zola --version
zola 0.23.2

$ install -m 755 target/release/zola /usr/local/bin/zola

Il faut ensuite cloner la repository des sources du site depuis GitLab une première fois, s'assurer de rester sur la branche "main" qui est celle de publication, puis mettre en place un cron qui fait des update git à intervales régulier (par exemple toutes les nuits) et si un chamgement est détecté il faudra lancer lancer un build Zola.

Clonage de la repository git et switch sur "main":

freebsd@server:~ $ git clone https://gitlab.com/kosmology/website.git
Cloning into 'website'...
remote: Enumerating objects: 252, done.
remote: Counting objects: 100% (22/22), done.
remote: Compressing objects: 100% (20/20), done.
remote: Total 252 (delta 3), reused 0 (delta 0), pack-reused 230 (from 2)
Receiving objects: 100% (252/252), 6.16 MiB | 8.92 MiB/s, done.
Resolving deltas: 100% (9/9), done.

freebsd@server:~ $ cd website/
freebsd@server:~/website $ git switch main
Already on 'main'
Your branch is up to date with 'origin/main'.

Premier build du site avec zola:

freebsd@server:~/website$ zola build
Building site...
-> Creating 18 pages (0 orphan) and 2 sections
Done in 474ms.

On ajoute ensuite une entrée dans la crontab du user pour garantir la mise à jour du site toutes les nuits:

$ crontab -e

0 1 * * * cd /home/freebsd/website && /usr/local/bin/git pull --ff-only && /usr/local/bin/zola build >> /home/freebsd/website/cron-build.log 2>&1

Enfin une petite configuration Nginx pour l'hébergement du site:

server {
    listen 80;
    server_name my-website.fr www.my-website.fr;

    root /home/freebsd/website/public;
    index index.html;

    location / {
        try_files $uri $uri/ =404;
    }
}

Il faudra peaufiner les réglages Nginx et les droits sur les fichiers et répertoires, voir héberger les fichiers à un autre endroit sur le filesystem. Je n'ai fais que montrer le principe.

Voila l'essentiel pour faire fonctionner le scénario PULL.

Scénario PUSH

La partie CI/CD de Gitlab contiendra 3 phases:

  • image: construction de l'image docker avec la version de Zola 0.23.x et les outils système tel que rsync
  • build: phase de build classe qui utilise l'image docker avec zola pour récupérer les sources du site et faire le build
  • deploy: phase de déploiement consécutive à la phase de build. Déploiement sur le serveur cible.

Préparation de l'image

On construit une image docker à partir d'Alpine Linux et ajoutant la version de Zola qui convient. On installe dans l'Alpine Linux: bash, curl, tar, gzip, git, ssh et rsync.

Dockerfile.ci
FROM alpine:3.22

ARG ZOLA_VERSION=0.23.2

RUN apk add --no-cache \
    bash \
    curl \
    tar \
    gzip \
    git \
    openssh-client \
    rsync \
    ca-certificates

RUN curl -L \
    "https://github.com/getzola/zola/releases/download/v${ZOLA_VERSION}/zola-v${ZOLA_VERSION}-x86_64-unknown-linux-musl.tar.gz" \
    -o /tmp/zola.tar.gz \
    && tar -xzf /tmp/zola.tar.gz -C /usr/local/bin \
    && rm /tmp/zola.tar.gz \
    && chmod +x /usr/local/bin/zola

RUN zola --version

WORKDIR /build

Configuration GitLab CI

On définit le fichier YAML avec les 3 stages/phases qui nous intéressent.

.gitlab-ci.yml
stages:
  - image
  - build
  - deploy

variables:
  ZOLA_IMAGE: "$CI_REGISTRY_IMAGE/zola-ci:0.23.2"

#
# Construction de l'image CI Zola
#
build-ci-image:
  stage: image
  image: docker:latest

  services:
    - docker:dind

  before_script:
    - echo "$CI_REGISTRY_PASSWORD" |
      docker login "$CI_REGISTRY" -u "$CI_REGISTRY_USER" --password-stdin

  script:
    - docker build -f Dockerfile.ci -t "$ZOLA_IMAGE" .
    - docker push "$ZOLA_IMAGE"

  rules:
    - changes:
        - Dockerfile.ci


#
# Construction du site Zola
#
build-site:
  stage: build

  image:
    name: $ZOLA_IMAGE
    entrypoint: [""]

  script:
    - zola --version
    - zola build

  artifacts:
    name: "site-$CI_COMMIT_SHORT_SHA"
    paths:
      - public/
    expire_in: 1 day

#
# Déploiement sur le VPS FreeBSD
#
deploy-site:
  stage: deploy

  image:
    name: $ZOLA_IMAGE
    entrypoint: [""]

  needs:
    - job: build-site
      artifacts: true

  before_script:
    - mkdir -p ~/.ssh
    - chmod 700 ~/.ssh

    - cp "$SSH_PRIVATE_KEY" ~/.ssh/id_ed25519
    - chmod 600 ~/.ssh/id_ed25519

    - cp "$SSH_KNOWN_HOSTS" ~/.ssh/known_hosts
    - chmod 644 ~/.ssh/known_hosts

  script:
    - rsync -az --delete public/ "$DEPLOY_USER@$DEPLOY_HOST:$DEPLOY_PATH/"

  rules:
    - if: '$CI_COMMIT_BRANCH == "main"'

Pour que le deploy fonctionne il faut préparer le serveur destination: création d'un user de déploiement, création de clef ssh et réglages de base FreeBSD.

Configuration FreeBSD

On commence par créer le user "deploy". On définit un mot de passe et on prépare son SSH.

pw useradd deploy -m -s /bin/sh
passwd deploy

mkdir -p /home/deploy/.ssh
chmod 700 /home/deploy/.ssh

touch /home/deploy/.ssh/authorized_keys
chmod 600 /home/deploy/.ssh/authorized_keys

chown -R deploy:deploy /home/deploy/.ssh

On installe rsync:

pkg install rsync

Adaptation des droits sur le répertoire qui va servir les fichiers pour le hosting:

chown -R deploy:deploy /usr/local/www/apache24/data/kosmology

On génère ensuite un clef SSH pour le user deploy. J'évite de le faire directement sur le serveur car cela n'est pas nécessaire. On reprends ensuite la clef publique pour la placer dans les authorized_keys du serveur:

local$ ssh-keygen -t ed25519 -C "gitlab-deploy" -f gitlab-deploy
local$ cat gitlab-deploy.pub
> ssh-ed25519 AAAbzzzz****************AAAAAA gitlab-deploy

server$ echo "ssh-ed25519 AAAbzzzz****************AAAAAA gitlab-deploy" >> ~/.ssh/authorized_keys

La suite se déroule dans l'interface de GitLab. Dans l'onglet Settings - CI/CD - Variables. On ajoute 5 variables de type "file":

SSH_PRIVATE_KEY : on y joint la clef privée du user "deploy"
SSH_KNOWN_HOSTS : on y joint le résultat de 'ssh-keyscan monsite.tld'
DEPLOY_HOST : le nom du host à joindre pour le déploiement.
DEPLOY_USER : "deploy" (le user qui servira pour les déploiements)
DEPLOY_PATH : /usr/local/www/.... (le répertoire de déploiement pour le serveur web)

Le résultat de la saisie devrait ressemble à cet écran:

On teste une petite Merge Request de la branche "drafting" vers "main" pour lancer une pipeline intégrale de build et de déploiement.

Et dans le détail:

Après vérification les fichiers ont bien été publiés sur le serveur. Le serveur web prends le relai et partage les fichiers directement après le dépot par GitLab.

Parfait !

Cela conclut la mise en place de tout l'outillage pour procéder à des publications automatisées via Zola.