Zelfgehoste diagramtools vergelijken: welke kiest u?
Ontdek het verschil tussen draw.io, Excalidraw en Kroki voor uw eigen VPS. Leer welke tool data lokaal verwerkt en welke uw diagrammen via de server verstuurt voor privacy.
Welke zelfgehoste diagramtool moet u gebruiken?
Zelfgehoste diagramtools zijn er in twee soorten, en het type is belangrijker dan de lijst met functies. draw.io en Excalidraw zijn browserapplicaties: de container levert JavaScript, uw browser verzorgt het tekenwerk en de server ziet het diagram nooit. Kroki werkt precies andersom. U verstuurt diagramtekst via HTTP en de server stuurt een afbeelding terug, waardoor elk diagram via uw eigen machine verloopt.
Gebruik draw.io als u een volledige editor naast een wiki wilt. Gebruik Excalidraw als u een snelle schetsomgeving zoekt en accepteert dat er niets wordt opgeslagen buiten de browser waarin u tekent. Gebruik Kroki als uw diagrammen bestaan uit tekst die in git naast de bijbehorende code staat.
Wat het zelf hosten van een diagramtool daadwerkelijk verandert
Wees nauwkeurig over welke onderdelen uw server raken, omdat dat ene feit bepaalt of zelf-hosting privacy oplevert of enkel beschikbaarheid.
- draw.io rendert in de browser. Uw container serveert de applicatiecode. Het bestand gaat naar de locatie waar u de editor opdracht geeft het op te slaan.
- Excalidraw rendert in de browser en bewaart de huidige scène in de local storage van die browser. Er wordt niets aan de serverzijde geschreven.
- Kroki rendert op de server. Zowel de diagrambron als de voltooide afbeelding bevinden zich binnen uw container.
Alleen in het derde geval verplaatst data zich naar hardware die u beheert. Voor de eerste twee gevallen levert zelf-hosting controle over assets en beschikbaarheid op: de JavaScript komt van uw host, waardoor de editor blijft werken wanneer een derde partij een storing heeft, de voorwaarden wijzigt of onbereikbaar wordt vanaf uw netwerk. Dat is voor sommige teams van reële waarde. Het is een andere claim dan "het diagram verlaat nooit het pand".
draw.io: een officiële container die niets opslaat
Het project publiceert een eigen image en de snelstartgids in de README bestaat uit één regel.
docker run -it --rm --name="draw" -p 8080:8080 -p 8443:8443 jgraph/drawioDit publiceert de editor op elk adres dat de server heeft. Bind op een VPS de gepubliceerde poort aan loopback en benader deze via een reverse proxy of een SSH-tunnel.
docker run -d --name drawio --restart unless-stopped -p 127.0.0.1:8080:8080 jgraph/drawioOpen http://127.0.0.1:8080/?offline=1&https=0 via de tunnel. De README noemt ?offline=1 "een beveiligingsfunctie die ondersteuning voor cloudopslag uitschakelt". Zonder deze instelling biedt de editor Google Drive, OneDrive en GitHub aan als opslagdoelen; dit zijn servers van derden.
Het binden aan 127.0.0.1 zorgt ervoor dat de poort niet toegankelijk is vanaf het openbare internet. Een eenvoudige -p 8080:8080 wordt niet gefilterd door ufw, omdat Docker eigen iptables-regels invoegt vóór de chains die ufw beheert. Hierdoor lijkt de firewall correct ingesteld, terwijl de poort wel reageert op extern verkeer. Docker publiceert direct voorbij ufw behandelt het mechanisme en de oplossing.
Twee omgevingsvariabelen zijn van belang zodra de editor niet op localhost draait.
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"De afsluitende slash is geen typefout. De README definieert DRAWIO_SERVER_URL als de "Publieke implementatie-URL met een afsluitende slash" en DRAWIO_BASE_URL als de "Dezelfde URL zonder afsluitende slash", gebruikt door de viewer, lightbox en embed-codepaden. Als u de editor aanbiedt onder een subpad zoals https://www.example.com/drawio/, moeten beide waarden dit subpad bevatten, omdat de applicatie zijn viewer- en embed-URL's hierop baseert.
Persistentie: die is er niet, en dat is het ontwerp. Er verschijnt geen volume in dat Compose-bestand omdat de container geen diagramgegevens bevat. Een .drawio-bestand is XML die de editor aan uw browser doorgeeft, en het opslagdoel dat u kiest bepaalt waar het terechtkomt: een download op uw eigen machine of de applicatie die de editor heeft ingesloten. Maak een back-up van die bestemming. Als het antwoord een map op de VPS is, dan is dat de map die bescherming verdient, samen met de bestandsbeheerder die u gebruikt om deze te bereiken, aangezien draw.io nergens een kopie van bewaart.
Wat uw server nog wel verlaat. Exporteren naar PDF is het duidelijkste voorbeeld. De README beschrijft DRAWIO_SELF_CONTAINED als "Instellen op 1 om exportverzoeken via Tomcat's ExportProxyServlet (/service/0) te routeren in plaats van de exportserver direct aan te roepen". Lees dit andersom: standaard blijft een exportverzoek niet binnen uw eigen implementatie. Het project publiceert ook jgraph/export-server, een "standalone image-export-server van draw.io", voor gebruikers die die rendering op hun eigen hardware willen draaien. ENABLE_DRAWIO_PROXY staat standaard uit en schakelt een /proxy-eindpunt in dat externe afbeeldings-URL's ophaalt namens de browser; laat dit uitgeschakeld tenzij u het nodig heeft.
Excalidraw: een statische bundel zonder backend
De officiële image-pagina geeft dit commando op.
docker run --rm -dit --name excalidraw -p 5000:80 excalidraw/excalidraw:latestVerplaats de gepubliceerde poort naar loopback om dezelfde reden als voorheen.
docker run -d --name excalidraw --restart unless-stopped -p 127.0.0.1:5000:80 excalidraw/excalidraw:latestBinnen de container serveert Nginx een gecompileerde JavaScript-bundel op poort 80. De gepubliceerde image is ongeveer 41 MB gecomprimeerd (Docker Hub, augustus 2026), wat aangeeft hoe beperkt de inhoud is. Er is geen database, geen sessie-opslag en geen uploadmap, omdat er op de server niets is om op te slaan.
De image-pagina vermeldt de beperking duidelijk: "Op dit moment ondersteunt het zelf hosten van een eigen instantie geen functies voor delen of samenwerken." De knoppen zijn nog steeds aanwezig in de interface, dus de reden is relevant om te kennen. Live samenwerking vereist een websocket-server, die afzonderlijk wordt gepubliceerd als excalidraw/excalidraw-room. Een link om te delen vereist een opslagservice om de versleutelde scène te bewaren. De adressen van beide worden tijdens het build-proces in de bundel gecompileerd als Vite-variabelen (VITE_APP_WS_SERVER_URL, VITE_APP_BACKEND_V2_GET_URL, VITE_APP_BACKEND_V2_POST_URL), en de productiewaarden in de repository verwijzen naar de eigen gehoste services van Excalidraw. Vite vervangt die waarden tijdens de build, waardoor ze als letterlijke strings in de JavaScript terechtkomen. Het instellen ervan als omgevingsvariabelen voor de container verandert niets, omdat geen enkele code ze tijdens runtime uitleest. Om samenwerking naar uw eigen room-server te laten wijzen, moet u de frontend vanuit de broncode bouwen met uw eigen waarden. Controleer de status van die server voordat u er plannen omheen maakt: de excalidraw/excalidraw-room-image op Docker Hub was in augustus 2026 al meer dan twee jaar niet opnieuw gebouwd.
Waar een tekening daadwerkelijk leeft. De scène bevindt zich in de local storage van de browser, op dat specifieke apparaat, voor die specifieke origin. Open dezelfde URL in een privévenster en het canvas is leeg; dit is de snelste manier om dit zelf te verifiëren. Het wissen van sitegegevens verwijdert de tekening en er is geen kopie op de server om deze te herstellen. Leer gebruikers daarom om "Save to..." te gebruiken en bewaar het .excalidraw-bestand, wat een JSON-bestand is, op een locatie die wordt geback-upt. Een gedeelde instantie geeft elke persoon een eigen privé-canvas. Behandel het als een persoonlijk kladblok dat toevallig wordt gehost.
Kroki: diagrammen als code, gerenderd op uw server
Kroki is één HTTP-gateway voor diverse renderers. U verstuurt tekst via POST en ontvangt SVG of PNG terug. Graphviz, PlantUML, D2 en diverse andere zijn ingebouwd in de gateway-image. Mermaid, BPMN en Excalidraw draaien in bijbehorende containers, waardoor Compose de logische keuze is voor de uitvoering. Dit is het voorbeeld uit de documentatie van 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 publiceert niets naar de host, waardoor de bijbehorende containers alleen bereikbaar zijn vanaf de gateway op het Compose-netwerk. Dat is de gewenste situatie. Wijzig de gateway-regel naar "127.0.0.1:8000:8000", tenzij de wiki die de aanroep doet op een andere host draait. Als u nog niet eerder een Compose-bestand op een server heeft geschreven, behandelt Docker Compose draaien op een VPS de bestandsstructuur en de docker compose up -d-cyclus.
Voer twee rooktests uit, in deze volgorde, omdat ze om verschillende redenen kunnen falen.
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 60Graphviz draait binnen de gateway, dus een SVG-document hier bewijst dat de gateway zelf correct functioneert. Test nu het pad dat tussen containers verloopt.
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 60SVG van het tweede commando bewijst dat KROKI_MERMAID_HOST is opgelost en de bijbehorende container heeft geantwoord. Als de eerste test slaagt en de tweede niet, ligt het probleem tussen de twee containers. Lees daarom docker compose logs kroki voordat u de diagramsyntaxis aanpast.
Het GET-formulier codeert het diagram in de URL; dit is hoe een wiki een afbeelding insluit zonder enige plugin. De documentatie biedt hiervoor deze encoder.
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'))"Op Ubuntu geeft dit python: command not found als resultaat, omdat het systeem python3 levert en geen versie-loze python. Gebruik python3. De uitvoer wordt achter een URL geplaatst in de vorm /{diagram-type}/{output-format}/{encoded-diagram}, en elke <img>-tag kan hiernaar verwijzen. Er is een limiet: KROKI_MAX_URI_LENGTH staat standaard op 4096 bytes, dus een lang diagram moet via POST worden verstuurd.
Kroki leest de tekst die u verstuurt, dus de beveiligingsinstellingen zijn cruciaal. KROKI_SAFE_MODE staat standaard op SECURE, het meest restrictieve van de drie niveaus, en KROKI_PLANTUML_ALLOW_INCLUDE staat standaard op false. Deze standaardinstellingen bestaan omdat de !include-richtlijn van PlantUML bestanden en URL's leest vanuit het perspectief van de renderer. Als u deze versoepelt op een endpoint dat voor iedereen bereikbaar is, geeft u het internet een bestandslezer die binnen uw container draait. Wijzig deze instellingen niet, tenzij u weet welk include-pad u nodig heeft; specificeer dit dan met KROKI_PLANTUML_INCLUDE_PATH.
Geheugen: wat is belastend op een kleine VPS
De volgorde is voorspelbaar zodra u weet wat elke container uitvoert.
- De Excalidraw-image is nginx die statische bestanden serveert. Dit is veruit de goedkoopste van de drie.
- draw.io draait op Tomcat, een Java-applicatieserver, en bevat dus een JVM (Java virtual machine), ongeacht of er iemand tekent of niet.
- De Kroki-gateway is ook een Java-service, geleverd als een jar voor handmatige installaties.
- De mermaid-companion is de kostbare variant. De Dockerfile installeert Chromium en stelt
PUPPETEER_EXECUTABLE_PATH=/usr/lib/chromium/chromein, omdat Mermaid rendert in een echte browser-engine.
Getallen bij inactiviteit zeggen daarom weinig. Het getal dat ertoe doet is de piek tijdens het renderen van een diagram, en KROKI_MERMAID_MAX_CONCURRENCY staat standaard op 6, waardoor er zes browser-renders tegelijkertijd actief kunnen zijn. Meet dit op uw eigen systeem in plaats van te vertrouwen op gepubliceerde cijfers.
docker stats --no-stream
docker system dfVoer de eerste opdracht uit terwijl alles inactief is, en daarna nogmaals terwijl u in een lus een groot mermaid-diagram rendert. Als de piek oncomfortabel hoog is voor uw kleine abonnement, stel dan een limiet in in plaats van te gokken: geheugenlimieten instellen voor een Compose-service toont de syntaxis en wat er gebeurt wanneer een container zijn plafond bereikt. Het verwijderen van de mermaid-companion is ook een valide optie, aangezien de gateway alle ingebouwde renderers blijft ondersteunen.
Geen van deze applicaties levert een gebruikersmodel, dus plaats er een voor
draw.io heeft geen accounts. Excalidraw heeft geen accounts. Kroki beantwoordt elk verzoek dat het bereikt. Elke vorm van inloggen moet via de proxy verlopen.
sudo apt update && sudo apt install -y apache2-utils
sudo htpasswd -c /etc/nginx/.htpasswd alicehtpasswd -c maakt het bestand aan en overschrijft een bestaand bestand, dus gebruik -c de eerste keer en daarna nooit meer.
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;
}
}Pas het toe met sudo nginx -t && sudo systemctl reload nginx. Het nginx -t-gedeelte is het belangrijkste: een reload bij een foutieve configuratie zorgt ervoor dat de oude configuratie actief blijft, waardoor de site blijft werken en uw wijziging niet live gaat. De reverse proxy-configuratie, regel voor regel uitgelegd behandelt het header-blok en de certificaatpaden die in dit fragment zijn weggelaten.
Basisauthenticatie is niet de juiste methode voor Kroki, en het is belangrijk om te begrijpen waarom. Een wikipagina sluit een Kroki-afbeelding in met een <img>-tag. De browser van de bezoeker haalt die URL op als een subresource, en stuurt uw inloggegevens niet mee naar een andere origin. Het verzoek resulteert daarom in een 401-fout en elk diagram op de pagina wordt weergegeven als een defecte afbeelding. Houd Kroki daarom buiten het publieke internet. Plaats het in hetzelfde Docker-netwerk als de wiki-container en laat de wiki het bereiken via de servicenaam, zonder dat er poorten naar de host worden gepubliceerd. Hoe Compose-netwerken servicenamen omzetten is het onderdeel dat dit mogelijk maakt.
Diagrammen die naast een self-hosted wiki staan
Dit is de gebruikelijke reden waarom men dit wil opzetten. Een wikipagina heeft een afbeelding nodig, en niemand wil dat die afbeelding een screenshot van iemands laptop is.
BookStack heeft een eersteklas koppeling voor een self-hosted editor. De standaard embed-URL is https://embed.diagrams.net/?embed=1&proto=json&spin=1&configure=1, en één regel in .env verplaatst deze naar uw container.
DRAWIO=https://drawio.example.com/?embed=1&proto=json&spin=1&configure=1Kopieer de query-string exact. De documentatie van BookStack stelt dat embed=1&proto=json&spin=1 "vereist zijn voor de integratie met BookStack om te functioneren", omdat deze het JSON-berichtprotocol selecteren dat de twee pagina's gebruiken om met elkaar te communiceren. Dezelfde pagina wijst naar stealth=1 "als u niet wilt dat andere externe services worden gebruikt", wat de optie is die u toevoegt wanneer het stoppen van uitgaande oproepen de reden was voor self-hosting. Met deze configuratie slaat BookStack de tekening op in zijn eigen opslag voor afbeeldingen naast de pagina, zodat de wikiback-up die u al maakt ook de back-up van het diagram is.
Als de keuze voor de wiki zelf nog niet is gemaakt, bepaal dat dan eerst. Kiezen tussen BookStack, Wiki.js en Outline is de eerdere beslissing, omdat de wiki bepaalt hoe een diagram aan een pagina wordt gekoppeld en dus welke van deze tools u toevoegt.
Foutmodi en de meldingen die u zult zien
De tekeneditor opent in BookStack en blijft oneindig laden. De spinner is spin=1 aan het wachten op een handshake die nooit aankomt. Controleer of embed=1&proto=json&spin=1 aanwezig is in uw DRAWIO-waarde en of het hostgedeelte geen typefout bevat.
Het editorframe blijft leeg op een HTTPS-wiki. De browserconsole rapporteert mixed content, waarbij http:// wordt geladen binnen https://. De browser blokkeert het frame en draw.io start niet. Serveer de editor via HTTPS.
Kroki retourneert 413 Request Entity Too Large. Die melding is afkomstig van nginx, niet van Kroki. De nginx client_max_body_size-standaardwaarde is 1 MB en de eigen KROKI_MAX_BODY_SIZE-standaardwaarde van Kroki is 1mb, dus een grote PlantUML-bron overschrijdt de limiet die als eerste wordt bereikt. Verhoog beide waarden.
Mermaid werkt niet terwijl graphviz wel werkt. De gateway is in orde, maar de companion wordt niet bereikt. Controleer of de service actief is met docker compose ps en controleer vervolgens of KROKI_MERMAID_HOST overeenkomt met de servicenaam, aangezien deze standaard op 127.0.0.1 staat, wat binnen de gateway-container betekent dat de gateway naar zichzelf verwijst.
Excalidraw-samenwerking maakt nooit verbinding. Als u een frontend heeft gebouwd tegen uw eigen room server en deze achter nginx heeft geplaatst, moet de proxy de verbinding upgraden met proxy_set_header Upgrade $http_upgrade; en proxy_set_header Connection "upgrade";. Zonder deze headers wordt de websocket-handshake beantwoord als een gewoon HTTP-verzoek en start de sessie nooit.
Het canvas is leeg na een browseropschoning. De scène stond in de lokale opslag op dat apparaat en er is geen kopie op de server. De oplossing is eerder een gewoonte dan een instelling: exporteer het .excalidraw-bestand voor alles wat u wilt bewaren.
FAQ
Blijven mijn diagrammen privé als ik draw.io zelf host?
Het houdt de applicatiecode op uw server, wat iets anders is dan het privé houden van de data. draw.io wordt in uw browser gerenderd, dus de container bevat op geen enkel moment een diagram. Privacy hangt daarom af van waar u het bestand opslaat en welke uitgaande verbindingen u ingeschakeld laat. Gebruik ?offline=1 om de cloudopslagdoelen uit te schakelen, en onthoud dat exportverzoeken naar een exportserver gaan, tenzij u DRAWIO_SELF_CONTAINED=1 instelt en zelf jgraph/export-server draait.
Waarom werkt samenwerking niet op mijn zelf-gehoste Excalidraw?
De officiële image-pagina vermeldt dat zelf-hosting "geen ondersteuning biedt voor functies voor delen of samenwerken". Live samenwerking vereist de afzonderlijke excalidraw/excalidraw-room websocket-server, en deellinks vereisen een opslagservice. De adressen van beide worden tijdens het build-proces in de JavaScript-bundel gecompileerd als Vite-variabelen zoals VITE_APP_WS_SERVER_URL, dus het instellen van een omgevingsvariabele op de draaiende container heeft geen effect. Het gebruik van uw eigen room-server betekent dat u de frontend vanuit de broncode moet bouwen met uw eigen waarden.
Hoe render ik Mermaid-diagrammen op mijn eigen server?
Draai Kroki met de bijbehorende mermaid-container en stel KROKI_MERMAID_HOST in op die servicenaam. POST vervolgens de diagramtekst naar /mermaid/svg en lees de SVG uit het antwoord, of codeer het diagram in een GET URL en verwijs er met een <img> tag naar. De companion-container stuurt Chromium aan via Puppeteer omdat Mermaid een browser-engine nodig heeft; houd dus rekening met het geheugen: KROKI_MERMAID_MAX_CONCURRENCY staat standaard ingesteld op zes renders tegelijk.
Heb ik een wachtwoord nodig voor deze tools?
Ja, want geen van deze tools heeft gebruikersaccounts. draw.io en Excalidraw geven een volledige editor aan iedereen die de URL vindt, en Kroki rendert alle tekst die ernaar wordt verzonden. Basisauthenticatie bij de reverse proxy is voldoende voor de twee editors. Houd Kroki voor de zekerheid ongepubliceerd op een Docker-netwerk dat gedeeld wordt met de wiki, omdat een <img> verzoek vanuit de browser van een lezer geen inloggegevens naar een andere origin meestuurt en elk ingesloten diagram anders zou breken.