Skip to content

Installer un wiki MkDocs Material en conteneur LXC sur Proxmox

Guide d'installation manuelle, étape par étape, d'un wiki MkDocs Material en conteneur LXC Proxmox, avec mise à jour automatique sur push GitLab via un petit serveur webhook.

git push (repo contenu) → GitLab → webhook → LXC → git pull → mkdocs build → nginx

Le contenu du wiki (docs/ + mkdocs.yml) vit dans un repo Git séparé du conteneur lui-même — ce qui permet de faire évoluer le contenu indépendamment de l'infrastructure qui l'héberge.


Prérequis

  • Un hôte Proxmox VE avec accès pct
  • Un repo Git contenant (ou destiné à contenir) docs/ et mkdocs.yml
  • Accès internet depuis le conteneur (clone du repo, installation des paquets Python)

Valeurs d'exemple utilisées dans ce guide :

Paramètre Valeur d'exemple
ID du conteneur 207
IP fixe 192.168.1.207/24
Port nginx interne 8080
Port externe (NAT) 9320
Port webhook 9900
Repo contenu git@gitlab.com:groupe/wiki.git

1⃣ Création du conteneur LXC

pveam update
pveam available | grep debian-12-standard
pveam download local debian-12-standard_12.7-1_amd64.tar.zst

pct create 207 local:vztmpl/debian-12-standard_12.7-1_amd64.tar.zst \
    --hostname wiki.votre-domaine.tld \
    --memory 512 \
    --swap 256 \
    --cores 1 \
    --rootfs local-lvm:8 \
    --net0 name=eth0,bridge=vmbr0,ip=192.168.1.207/24,gw=192.168.1.1 \
    --unprivileged 1 \
    --onboot 1

pct start 207

2⃣ Installation des paquets et de MkDocs Material

pct exec 207 -- apt-get update
pct exec 207 -- apt-get install -y --no-install-recommends \
    python3 python3-pip python3-venv git nginx curl openssh-client

Installer MkDocs Material dans un environnement virtuel isolé (évite les conflits avec les paquets Python système) :

pct exec 207 -- bash -c "
    python3 -m venv /opt/mkdocs-venv &&
    /opt/mkdocs-venv/bin/pip install --quiet --upgrade pip &&
    /opt/mkdocs-venv/bin/pip install --quiet mkdocs mkdocs-material mkdocs-material-extensions pymdown-extensions
"
pct exec 207 -- ln -sf /opt/mkdocs-venv/bin/mkdocs /usr/local/bin/mkdocs
pct exec 207 -- bash -c "
    mkdir -p /var/www/wiki /srv/wiki /etc/wiki /usr/local/lib/wiki /var/log/wiki &&
    chown www-data:www-data /var/www/wiki
"

3⃣ Clé SSH de déploiement et clone du repo contenu

pct exec 207 -- bash -c "mkdir -p /root/.ssh && chmod 700 /root/.ssh"
pct exec 207 -- ssh-keygen -t ed25519 -f /root/.ssh/id_ed25519 -N '' -q
pct exec 207 -- ssh-keyscan gitlab.com >> /root/.ssh/known_hosts
pct exec 207 -- cat /root/.ssh/id_ed25519.pub

Ajouter cette clé publique sur GitLab : repo wiki → Settings → Repository → Deploy keys (lecture seule suffit).

Cloner et construire une première fois :

pct exec 207 -- bash -c "
    git clone --branch main git@gitlab.com:groupe/wiki.git /srv/wiki &&
    cd /srv/wiki && /opt/mkdocs-venv/bin/mkdocs build -d /var/www/wiki --quiet &&
    chown -R www-data:www-data /var/www/wiki
"

4⃣ Script de déploiement

Créer /usr/local/bin/wiki-deploy.sh, appelé à chaque push :

pct exec 207 -- bash -c "cat > /usr/local/bin/wiki-deploy.sh" << 'EOF'
#!/bin/bash
set -e

WIKI_DIR=/srv/wiki
WIKI_BUILD_DIR=/var/www/wiki
WIKI_BRANCH=main
LOG=/var/log/wiki/deploy.log

log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG"; }

log "Déploiement déclenché"
cd "$WIKI_DIR"
git fetch origin
git reset --hard "origin/${WIKI_BRANCH}"
log "Git pull OK ($(git rev-parse --short HEAD))"

/opt/mkdocs-venv/bin/mkdocs build -d "$WIKI_BUILD_DIR" --quiet
chown -R www-data:www-data "$WIKI_BUILD_DIR"
log "Build OK → $WIKI_BUILD_DIR"
EOF

pct exec 207 -- chmod +x /usr/local/bin/wiki-deploy.sh

5⃣ Serveur webhook

Un secret partagé authentifie les requêtes entrantes :

pct exec 207 -- bash -c "cat > /etc/wiki/webhook.env" << 'EOF'
WEBHOOK_SECRET=changez-moi
WEBHOOK_PORT=9900
DEPLOY_SCRIPT=/usr/local/bin/wiki-deploy.sh
EOF
pct exec 207 -- chmod 600 /etc/wiki/webhook.env

Créer /usr/local/lib/wiki/webhook.py :

pct exec 207 -- bash -c "cat > /usr/local/lib/wiki/webhook.py" << 'EOF'
#!/usr/bin/env python3
"""Webhook HTTP — reçoit les push Git et déclenche le déploiement du wiki."""

import hmac
import os
import subprocess
import threading
from http.server import BaseHTTPRequestHandler, HTTPServer

SECRET = os.environ.get("WEBHOOK_SECRET", "")
DEPLOY_SCRIPT = os.environ.get("DEPLOY_SCRIPT", "/usr/local/bin/wiki-deploy.sh")
PORT = int(os.environ.get("WEBHOOK_PORT", "9900"))


class WebhookHandler(BaseHTTPRequestHandler):
    def do_POST(self):
        if self.path != "/deploy":
            self._respond(404, b"Not Found")
            return

        token = self.headers.get("X-Gitlab-Token", "")
        if not SECRET or not hmac.compare_digest(token, SECRET):
            self._respond(403, b"Forbidden")
            return

        self._respond(200, b"OK")
        threading.Thread(target=self._deploy, daemon=True).start()

    def _deploy(self):
        try:
            subprocess.run([DEPLOY_SCRIPT], check=True, timeout=120)
        except Exception as e:
            print(f"[webhook] deploy error: {e}")

    def _respond(self, code, body):
        self.send_response(code)
        self.send_header("Content-Length", str(len(body)))
        self.end_headers()
        self.wfile.write(body)

    def log_message(self, fmt, *args):
        print(f"[webhook] {self.address_string()} {fmt % args}")


if __name__ == "__main__":
    server = HTTPServer(("0.0.0.0", PORT), WebhookHandler)
    print(f"[webhook] listening on :{PORT}")
    server.serve_forever()
EOF

Service systemd associé :

pct exec 207 -- bash -c "cat > /etc/systemd/system/wiki-webhook.service" << 'EOF'
[Unit]
Description=Wiki Git Webhook
After=network.target

[Service]
Type=simple
EnvironmentFile=/etc/wiki/webhook.env
ExecStart=/usr/bin/python3 /usr/local/lib/wiki/webhook.py
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF

pct exec 207 -- systemctl daemon-reload
pct exec 207 -- systemctl enable --now wiki-webhook

6⃣ Configuration nginx

pct exec 207 -- bash -c "cat > /etc/nginx/sites-available/wiki" << 'EOF'
server {
    listen 8080;
    server_name _;

    root /var/www/wiki;
    index index.html;

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

    gzip on;
    gzip_types text/css application/javascript application/json image/svg+xml;
    gzip_min_length 1024;

    add_header Cache-Control "public, max-age=3600";
    add_header X-Content-Type-Options nosniff;
}
EOF

pct exec 207 -- rm -f /etc/nginx/sites-enabled/default
pct exec 207 -- ln -sf /etc/nginx/sites-available/wiki /etc/nginx/sites-enabled/wiki
pct exec 207 -- nginx -t
pct exec 207 -- systemctl enable --now nginx
pct exec 207 -- systemctl restart nginx

7⃣ Configuration du webhook côté GitLab

GitLab → repo wiki → Settings → Webhooks
  URL    : http://192.168.1.207:9900/deploy
  Secret : valeur de WEBHOOK_SECRET (/etc/wiki/webhook.env)
  Events : Push events uniquement

8⃣ Redirection du port externe (NAT)

CT_IP=192.168.1.207
iptables -t nat -A PREROUTING -p tcp --dport 9320 -j DNAT --to-destination $CT_IP:8080
iptables -I FORWARD -p tcp -d $CT_IP --dport 8080 -j ACCEPT
iptables-save > /etc/iptables.rules

⚙️ Points d'administration importants

  • Séparer contenu et infrastructure : garder docs/ + mkdocs.yml dans leur propre repo, distinct des scripts d'installation du conteneur — cela permet de versionner et de faire évoluer la documentation sans jamais toucher au conteneur.
  • WEBHOOK_SECRET doit être un secret fort, et identique entre /etc/wiki/webhook.env et la configuration du webhook côté GitLab — sinon les push sont rejetés (403) et le wiki ne se met plus à jour silencieusement.
  • Déploiement manuel en cas de besoin, sans passer par un push Git : pct exec 207 -- /usr/local/bin/wiki-deploy.sh.
  • Logs à surveiller en cas de souci de mise à jour :
  • pct exec 207 -- tail -f /var/log/wiki/deploy.log — résultat du dernier git pull + build
  • pct exec 207 -- journalctl -u wiki-webhook -f — requêtes reçues, erreurs d'authentification
  • pct exec 207 -- journalctl -u nginx -f — erreurs de service du site statique
  • Mettre à jour MkDocs Material : pct exec 207 -- /opt/mkdocs-venv/bin/pip install --upgrade mkdocs-material, puis relancer un build (wiki-deploy.sh ou push Git).
  • Personnalisation du thème (palette, police, page d'accueil) se fait dans mkdocs.yml et docs/assets/css/custom.css du repo contenu, jamais dans les fichiers du conteneur — pour rester reconstruisible à l'identique en cas de réinstallation.

Conclusion

Séparer le contenu (repo wiki) de l'infrastructure (ce conteneur) permet de faire évoluer la documentation au quotidien sans jamais toucher au serveur, tout en gardant un déploiement entièrement automatisé à chaque push.