SSD Nodes Learn Hosting plans →
Guides Matt ConnorPar Matt Connor

Outils de diagrammes auto-hébergés : draw.io, Excalidraw

draw.io et Excalidraw dessinent dans le navigateur, tandis que Kroki reçoit le texte par HTTP : découvrez ce qui touche vraiment votre VPS.

Quel outil de diagrammes auto-hébergé devez-vous utiliser ?

Les outils de diagrammes auto-hébergés se répartissent en deux catégories, et cette distinction compte davantage que la liste des fonctionnalités. draw.io et Excalidraw sont des applications web : le conteneur fournit le code JavaScript, votre navigateur réalise le dessin et le serveur ne voit jamais le diagramme. Kroki fonctionne à l’inverse. Vous lui envoyez le texte du diagramme via HTTP et il vous renvoie une image. Chaque diagramme passe donc par votre propre machine.

Utilisez draw.io si vous voulez un éditeur complet associé à un wiki. Utilisez Excalidraw si vous voulez un bloc-notes de croquis rapide et acceptez que rien ne soit enregistré en dehors du navigateur dans lequel vous avez dessiné. Utilisez Kroki si vos diagrammes sont du texte stocké dans git, à côté du code qu’ils décrivent.

Ce que change réellement l’auto-hébergement d’un outil de création de diagrammes

Soyez précis sur les éléments qui touchent votre serveur, car ce point détermine si l’auto-hébergement apporte de la confidentialité ou seulement de la disponibilité.

  • draw.io effectue le rendu dans le navigateur. Votre conteneur fournit le code de l’application. Le fichier est enregistré à l’emplacement que vous indiquez dans l’éditeur.
  • Excalidraw effectue le rendu dans le navigateur et conserve la scène actuelle dans le stockage local de ce navigateur. Rien n’est écrit côté serveur.
  • Kroki effectue le rendu sur le serveur. Le code source du diagramme et l’image finale existent tous deux dans votre conteneur.

Seul le troisième cas transfère les données sur du matériel que vous contrôlez. Pour les deux premiers, l’auto-hébergement apporte le contrôle des ressources et la disponibilité : le code JavaScript vient de votre hôte, et l’éditeur continue donc de fonctionner lorsqu’un tiers subit une panne, modifie ses conditions d’utilisation ou devient inaccessible depuis votre réseau. Pour certaines équipes, cela représente une valeur réelle. Cela ne signifie pas que « le diagramme ne quitte jamais les locaux ».

draw.io : un conteneur officiel qui ne stocke rien

Le projet publie sa propre image, et le démarrage rapide de son README tient en une ligne.

docker run -it --rm --name="draw" -p 8080:8080 -p 8443:8443 jgraph/drawio

Cette commande publie l’éditeur sur toutes les adresses du serveur. Sur un VPS, liez le port publié à loopback et accédez-y via un reverse proxy ou un tunnel SSH.

docker run -d --name drawio --restart unless-stopped -p 127.0.0.1:8080:8080 jgraph/drawio

Ouvrez http://127.0.0.1:8080/?offline=1&https=0 via le tunnel. Le README présente ?offline=1 comme « une fonctionnalité de sécurité qui désactive la prise en charge du cloud storage ». Sans cette option, l’éditeur propose Google Drive, OneDrive et GitHub comme cibles d’enregistrement, c’est-à-dire les serveurs d’autres personnes.

L’association à 127.0.0.1 est ce qui maintient ce port hors de l’Internet public. Un simple -p 8080:8080 n’est pas filtré par ufw, car Docker insère ses propres règles iptables avant les chaînes gérées par ufw. Le pare-feu semble donc correctement configuré alors que le port répond à tout Internet. Publication Docker qui contourne directement ufw décrit le mécanisme et la correction.

Deux variables d’environnement deviennent importantes dès que l’éditeur n’est plus accessible sur localhost.

services:
  drawio:
    image: jgraph/drawio
    container_name: drawio
    restart: unless-stopped
    ports:
      - "127.0.0.1:8080:8080"
    environment:
      DRAWIO_SERVER_URL: "https://drawio.example.com/"
      DRAWIO_BASE_URL: "https://drawio.example.com"

La barre oblique finale n’est pas une erreur. Le README définit DRAWIO_SERVER_URL comme l’« URL de déploiement publique avec une barre oblique finale » et DRAWIO_BASE_URL comme la « même URL sans barre oblique finale ». Le viewer, la lightbox et les chemins du code d’intégration les utilisent. Si vous servez l’éditeur sous un sous-chemin tel que https://www.example.com/drawio/, les deux valeurs doivent inclure ce sous-chemin, car l’application construit ses URL de viewer et d’intégration à partir de ces valeurs.

Persistance : il n’y en a aucune, et c’est voulu. Aucun volume n’apparaît dans ce fichier Compose, car le conteneur ne contient aucune donnée de diagramme. Un fichier .drawio est un fichier XML que l’éditeur transmet à votre navigateur. La cible d’enregistrement que vous choisissez détermine ensuite sa destination : un téléchargement sur votre propre machine ou l’application qui a intégré l’éditeur. Sauvegardez cette destination. S’il s’agit d’un dossier sur le VPS, c’est ce dossier qu’il faut protéger, ainsi que le gestionnaire de fichiers que vous utilisez pour y accéder, car draw.io ne conserve aucune copie.

Ce qui quitte encore votre serveur. Moins qu’auparavant, car l’exportation ne va désormais plus nulle part. Le README répertorie maintenant EXPORT_URL et DRAWIO_SELF_CONTAINED parmi les variables supprimées qui n’ont aucun effet. Elles dirigeaient l’éditeur vers l’image jgraph/export-server distincte, qui « est arrivée en fin de vie ». Le point d’entrée écrit maintenant window.EXPORT_URL = null dans PreConfig.js, ce qui indique à l’éditeur qu’aucun service d’exportation n’existe. En pratique, Exporter sous > PDF ouvre la boîte de dialogue d’impression du navigateur. L’option permettant d’inclure une copie du diagramme n’est donc pas proposée pour le PDF, et l’exportation PNG et JPEG rendue par le serveur est masquée. L’exportation PNG, JPEG et SVG rendue par le navigateur fonctionne toujours. ENABLE_DRAWIO_PROXY est désactivé par défaut et active un endpoint /proxy qui récupère des URL d’images externes à la place du navigateur. Laissez-le désactivé sauf si vous en avez besoin.

Excalidraw : un bundle statique sans serveur derrière

La page officielle de l’image fournit cette commande.

docker run --rm -dit --name excalidraw -p 5000:80 excalidraw/excalidraw:latest

Liez le port publié à loopback, pour la même raison que précédemment.

docker run -d --name excalidraw --restart unless-stopped -p 127.0.0.1:5000:80 excalidraw/excalidraw:latest

Dans le conteneur, nginx sert un bundle JavaScript compilé sur le port 80. L’image publiée fait environ 41 MB une fois compressée (Docker Hub, octobre 2026), ce qui montre le peu de contenu qu’elle contient. Il n’y a ni base de données, ni stockage de sessions, ni répertoire d’upload, car rien ne doit être stocké sur le serveur.

La page de l’image indique clairement la limite : « Pour le moment, l’auto-hébergement de votre propre instance ne prend pas en charge les fonctions de partage ou de collaboration. » Les boutons sont toujours présents dans l’interface, il est donc utile d’en connaître la raison. La collaboration en temps réel nécessite un serveur websocket, publié séparément sous le nom excalidraw/excalidraw-room. Un lien de partage nécessite un service de stockage pour conserver la scène chiffrée. Les adresses de ces deux services sont intégrées au bundle lors du build sous forme de variables Vite (VITE_APP_WS_SERVER_URL, VITE_APP_BACKEND_V2_GET_URL, VITE_APP_BACKEND_V2_POST_URL), et les valeurs de production du dépôt pointent vers les services hébergés par Excalidraw. Vite remplace ces valeurs pendant le build. Elles deviennent donc des chaînes littérales dans le JavaScript. Les définir comme variables d’environnement du conteneur ne change rien, car aucun code ne les lit au runtime. Pour faire pointer la collaboration vers votre propre room server, vous devez builder le frontend depuis les sources avec vos propres valeurs. Vérifiez l’état de ce serveur avant de baser votre architecture dessus : en octobre 2026, le push le plus récent de l’image excalidraw/excalidraw-room sur Docker Hub datait de décembre 2023.

Où se trouve réellement un dessin. La scène se trouve dans le stockage local du navigateur, sur cet appareil et pour cette origine. Ouvrez la même URL dans une fenêtre privée : le canvas est vide. C’est le moyen le plus rapide de le vérifier. La suppression des données du site efface le dessin, et aucune copie serveur ne permet de le restaurer. Apprenez donc aux utilisateurs à utiliser « Save to... » et à conserver le fichier .excalidraw, qui est au format JSON, dans un emplacement sauvegardé. Une instance partagée fournit à chaque personne son propre canvas privé. Considérez-la comme un bloc de croquis personnel hébergé sur un serveur.

Kroki : les diagrammes sous forme de code, rendus sur votre serveur

Kroki est une passerelle HTTP unique devant de nombreux moteurs de rendu. Vous envoyez du texte avec une requête POST et récupérez un fichier SVG ou PNG. Graphviz, PlantUML, D2 et plusieurs autres moteurs sont intégrés à l’image de la passerelle. Le rendu Mermaid, BPMN et Excalidraw s’exécute dans des conteneurs compagnons ; Compose est donc la méthode adaptée pour les lancer. Voici l’exemple fourni dans la documentation de Kroki.

services:
  kroki:
    image: yuzutech/kroki
    depends_on:
      - mermaid
      - bpmn
      - excalidraw
    environment:
      - KROKI_MERMAID_HOST=mermaid
      - KROKI_BPMN_HOST=bpmn
      - KROKI_EXCALIDRAW_HOST=excalidraw
    ports:
      - "8000:8000"
    tmpfs:
      - /tmp:exec
  mermaid:
    image: yuzutech/kroki-mermaid
    expose:
      - "8002"
  bpmn:
    image: yuzutech/kroki-bpmn
    expose:
      - "8003"
  excalidraw:
    image: yuzutech/kroki-excalidraw
    expose:
      - "8004"

expose ne publie aucun port sur l’hôte. Les conteneurs compagnons sont donc accessibles uniquement depuis la passerelle sur le réseau Compose. C’est le comportement recherché. Remplacez la ligne de la passerelle par "127.0.0.1:8000:8000", sauf si le wiki qui l’appelle s’exécute sur un autre hôte. Si vous n’avez encore jamais écrit de fichier Compose sur un serveur, exécuter Docker Compose sur un VPS présente la structure des fichiers et le cycle docker compose up -d.

Exécutez deux tests de bon fonctionnement, dans cet ordre, car ils échouent pour des raisons différentes.

curl -s -X POST http://127.0.0.1:8000/graphviz/svg \
  -H 'Content-Type: text/plain' \
  --data-binary 'digraph G {Hello->World}' | head -c 60

Graphviz s’exécute dans la passerelle. Un document SVG obtenu ici prouve donc que la passerelle elle-même fonctionne. Testez maintenant le chemin qui traverse les conteneurs.

curl -s -X POST http://127.0.0.1:8000/mermaid/svg \
  -H 'Content-Type: text/plain' \
  --data-binary 'graph TD; A-->B;' | head -c 60

Le SVG renvoyé par la deuxième commande prouve que KROKI_MERMAID_HOST a été résolu et que le conteneur compagnon a répondu. Si la première commande fonctionne et pas la deuxième, le problème se situe entre les deux conteneurs. Consultez donc docker compose logs kroki avant de modifier la syntaxe du diagramme.

La forme GET encode le diagramme dans l’URL. C’est ainsi qu’un wiki peut intégrer une image sans aucun plugin. La documentation fournit cet encodeur.

cat hello.dot | python -c "import sys; import base64; import zlib; print(base64.urlsafe_b64encode(zlib.compress(sys.stdin.read().encode('utf-8'), 9)).decode('ascii'))"

Sur Ubuntu, cette commande affiche python: command not found, car le système fournit python3 et aucun python sans numéro de version. Utilisez python3. La sortie est ajoutée à la fin d’une URL construite comme /{diagram-type}/{output-format}/{encoded-diagram}, et n’importe quelle balise <img> peut y faire référence. Il existe toutefois une limite : KROKI_MAX_URI_LENGTH vaut 4096 octets par défaut. Un diagramme volumineux doit donc être envoyé avec POST.

Kroki lit le texte que vous lui envoyez. Ses paramètres de sécurité sont donc ceux qui comptent. KROKI_SAFE_MODE vaut SECURE par défaut, le niveau le plus restrictif des trois, et KROKI_PLANTUML_ALLOW_INCLUDE vaut false par défaut. Ces valeurs par défaut existent parce que la directive !include de PlantUML lit des fichiers et des URL depuis le point de vue du moteur de rendu. Si vous assouplissez ces paramètres sur un endpoint accessible à tous, vous donnez à Internet un lecteur de fichiers exécuté dans votre conteneur. Laissez-les inchangés, sauf si vous savez quel chemin d’inclusion est nécessaire. Dans ce cas, indiquez-le avec KROKI_PLANTUML_INCLUDE_PATH.

Mémoire : quel service pose problème sur un petit VPS

L’ordre est prévisible une fois que vous savez ce que chaque conteneur exécute.

  • L’image Excalidraw utilise nginx pour servir des fichiers statiques. C’est de loin la moins gourmande des trois.
  • draw.io exécute Tomcat, un serveur d’applications Java. Il utilise donc une JVM (machine virtuelle Java), même si personne ne dessine.
  • La gateway Kroki est également un service Java, fourni sous forme de fichier jar pour les installations manuelles.
  • Le compagnon mermaid est le plus gourmand. Son Dockerfile installe Chromium et définit PUPPETEER_EXECUTABLE_PATH=/usr/lib/chromium/chrome, car Mermaid effectue le rendu dans un véritable moteur de navigateur.

Les valeurs au repos vous apprennent donc peu de choses. La valeur importante est le pic pendant le rendu d’un diagramme. KROKI_MERMAID_MAX_CONCURRENCY vaut 6 par défaut : six rendus dans un navigateur peuvent donc être exécutés simultanément. Mesurez la consommation sur votre propre serveur au lieu de vous fier à une valeur publiée.

docker stats --no-stream
docker system df

Exécutez la première commande lorsque tout est au repos, puis de nouveau pendant que vous effectuez en boucle le rendu d’un grand diagramme mermaid. Si le pic est trop élevé sur une petite offre, imposez une limite au lieu de deviner : définir des limites mémoire sur un service Compose présente la syntaxe et le comportement d’un conteneur lorsqu’il atteint sa limite. Supprimer le compagnon mermaid est également une solution valable, car la gateway continue de servir tous les renderers qu’elle intègre.

Aucun de ces logiciels ne fournit de gestion des utilisateurs. Placez donc un composant d’authentification devant le service.

draw.io ne gère aucun compte. Excalidraw ne gère aucun compte. Kroki répond à toute requête qu’il reçoit. L’authentification doit donc être assurée par le proxy.

sudo apt update && sudo apt install -y apache2-utils
sudo htpasswd -c /etc/nginx/.htpasswd alice

htpasswd -c crée le fichier et écrase celui qui existe déjà. Utilisez donc -c la première fois, et jamais ensuite.

server {
    listen 443 ssl;
    server_name drawio.example.com;

    location / {
        auth_basic "diagrams";
        auth_basic_user_file /etc/nginx/.htpasswd;
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Appliquez la configuration avec sudo nginx -t && sudo systemctl reload nginx. La partie nginx -t est essentielle : si la configuration est invalide, un reload laisse l’ancienne configuration active. Le site continue donc de fonctionner, mais votre modification n’est pas prise en compte. La configuration du reverse proxy, expliquée ligne par ligne détaille le bloc d’en-têtes et les chemins des certificats absents de cet extrait.

L’authentification Basic n’est pas adaptée à Kroki, et il est utile d’en comprendre la raison. Une page de wiki intègre une image Kroki avec une balise <img>. Le navigateur du lecteur récupère cette URL comme une sous-ressource cross-origin. Il envoie les identifiants Basic déjà enregistrés pour l’hôte Kroki, mais les navigateurs actuels n’affichent pas de demande de connexion pour une image cross-origin. Tout lecteur qui ne s’est pas connecté directement à l’hôte Kroki reçoit donc une réponse 401 et voit tous les diagrammes de la page comme des images cassées. Gardez plutôt Kroki hors de l’Internet public. Placez-le sur le même réseau Docker que le conteneur du wiki et laissez le wiki y accéder par le nom du service, sans publier aucun port sur l’hôte. Comment les réseaux Compose résolvent les noms de service explique le mécanisme qui permet cela.

Des schémas associés à un wiki auto-hébergé

C’est généralement la raison pour laquelle on veut mettre tout cela en place. Une page de wiki a besoin d’une image, et personne ne veut que cette image soit une capture d’écran provenant de l’ordinateur portable de quelqu’un.

BookStack propose une intégration native avec un éditeur auto-hébergé. Son URL d’intégration par défaut est https://embed.diagrams.net/?embed=1&proto=json&spin=1&configure=1, et une ligne dans .env la redirige vers votre conteneur.

DRAWIO=https://drawio.example.com/?embed=1&proto=json&spin=1&configure=1

Copiez exactement la chaîne de requête. La documentation de BookStack indique que embed=1&proto=json&spin=1 « sont nécessaires pour que l’intégration avec BookStack fonctionne », car ils sélectionnent le protocole de messages JSON utilisé par les deux pages pour communiquer. La même page mentionne stealth=1 « si vous ne voulez pas utiliser d’autres services externes ». C’est l’option à ajouter lorsque l’objectif de l’auto-hébergement est d’empêcher les appels sortants. Une fois cette configuration en place, BookStack enregistre le schéma dans son propre stockage d’images, à côté de la page. La sauvegarde du wiki que vous effectuez déjà contient donc aussi la sauvegarde des schémas.

Si vous n’avez pas encore choisi le wiki, commencez par trancher cette question. Choisir entre BookStack, Wiki.js et Outline correspond à la décision précédente, car le wiki détermine la manière d’ajouter un schéma à une page et, par conséquent, l’outil que vous devrez intégrer.

Modes d’échec et messages affichés

L’éditeur de diagrammes s’ouvre dans BookStack et tourne indéfiniment. L’indicateur de chargement attend spin=1 les données du diagramme. Avec proto=json, l’éditeur envoie un événement init lorsqu’il est prêt et attend en retour une action load contenant le diagramme. L’indicateur reste donc affiché lorsque cet échange n’aboutit pas. Vérifiez que embed=1&proto=json&spin=1 est présent dans votre valeur DRAWIO et que la partie hôte ne contient pas de faute de frappe.

Le cadre de l’éditeur reste vide sur un wiki HTTPS. La console du navigateur signale du contenu mixte et indique que http:// est chargé dans https://. Le navigateur bloque le cadre et draw.io ne s’exécute jamais. Servez l’éditeur en HTTPS.

Kroki renvoie 413 Request Entity Too Large. Cette formulation exacte correspond au nom de l’erreur utilisé par nginx. Le proxy est donc le premier élément à vérifier. Cependant, Kroki peut également produire lui-même une erreur 413, que sa documentation appelle Content Too Large. La valeur par défaut de client_max_body_size dans nginx est de 1 MB. C’est cette limite qu’atteint une source PlantUML volumineuse, car PlantUML effectue le rendu dans la gateway. La valeur KROKI_MAX_BODY_SIZE de Kroki est également définie par défaut sur 1mb, mais elle n’existe que dans les conteneurs compagnons diagrams.net, Excalidraw et Mermaid. Une source Mermaid volumineuse peut donc atteindre l’une ou l’autre limite. Augmentez d’abord client_max_body_size. Augmentez également KROKI_MAX_BODY_SIZE sur le conteneur compagnon lorsque le diagramme passe par celui-ci.

Mermaid échoue alors que graphviz fonctionne. La gateway fonctionne et le conteneur compagnon n’est pas atteint. Vérifiez que le service fonctionne avec docker compose ps, puis vérifiez que KROKI_MERMAID_HOST correspond au nom du service. Sa valeur par défaut est 127.0.0.1, ce qui désigne la gateway elle-même depuis le conteneur de la gateway.

La collaboration Excalidraw ne se connecte jamais. Si vous avez construit un frontend connecté à votre propre serveur de salles et que vous l’avez placé derrière nginx, le proxy doit mettre à niveau la connexion avec proxy_set_header Upgrade $http_upgrade; et proxy_set_header Connection "upgrade";. Sans ces en-têtes, la négociation websocket reçoit une réponse HTTP ordinaire et la session ne démarre jamais.

Le canvas est vide après le nettoyage du navigateur. La scène était stockée dans le stockage local de cet appareil et aucune copie n’existe sur le serveur. La solution repose sur une habitude plutôt que sur un réglage : exportez le fichier .excalidraw pour tout contenu à conserver.

FAQ

L’auto-hébergement de draw.io conserve-t-il mes diagrammes privés ?

Il conserve le code de l’application sur votre serveur, ce qui ne signifie pas que les données restent privées. draw.io effectue le rendu dans votre navigateur : le conteneur ne contient donc jamais de diagramme. La confidentialité dépend ensuite de l’emplacement où vous enregistrez le fichier et des appels sortants que vous laissez activés. Utilisez ?offline=1 pour désactiver les cibles de stockage cloud et laissez ENABLE_DRAWIO_PROXY désactivé. L’export n’envoie rien non plus : l’image ne configure aucun service d’export, le PDF passe donc par la boîte de dialogue d’impression du navigateur et DRAWIO_SELF_CONTAINED n’a plus aucun effet.

Pourquoi la collaboration ne fonctionne-t-elle pas avec mon Excalidraw auto-hébergé ?

La page de l’image officielle indique que l’auto-hébergement « ne prend pas en charge les fonctions de partage ou de collaboration ». La collaboration en temps réel nécessite le serveur websocket excalidraw/excalidraw-room, et les liens de partage nécessitent un service de stockage. Les adresses des deux services sont intégrées au bundle JavaScript lors de la compilation, sous la forme de variables Vite telles que VITE_APP_WS_SERVER_URL. Définir une variable d’environnement sur le conteneur en cours d’exécution n’a donc aucun effet. Pour utiliser votre propre serveur de rooms, vous devez compiler le frontend depuis les sources avec vos valeurs.

Comment effectuer le rendu de diagrammes Mermaid sur mon propre serveur ?

Lancez Kroki avec son conteneur compagnon mermaid et définissez KROKI_MERMAID_HOST sur le nom de ce service. Envoyez ensuite le texte du diagramme en POST vers /mermaid/svg et lisez le SVG dans la réponse, ou encodez le diagramme dans une URL GET et utilisez-la dans un tag <img>. Le conteneur compagnon pilote Chromium via Puppeteer, car Mermaid a besoin d’un moteur de navigateur. Prévoyez donc suffisamment de mémoire : KROKI_MERMAID_MAX_CONCURRENCY est défini par défaut sur six rendus simultanés.

Dois-je placer un mot de passe devant ces outils ?

Oui, car aucun d’eux ne gère de comptes. draw.io et Excalidraw donnent accès à un éditeur complet à toute personne qui découvre l’URL, et Kroki effectue le rendu de tout texte qui lui est envoyé. Une authentification Basic sur le reverse proxy suffit pour les deux éditeurs. Pour Kroki, laissez le service non publié sur un réseau Docker partagé avec le wiki. Le navigateur d’un lecteur n’affichera pas de demande de connexion pour un <img> situé sur une autre origine. Chaque diagramme intégré serait donc inaccessible aux personnes qui ne se sont pas connectées directement à Kroki.

#diagrams#drawio#excalidraw#mermaid#kroki#docker