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 quick start 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 machine 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 optie biedt de editor Google Drive, OneDrive en GitHub aan als opslaglocaties; 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 geconfigureerd, terwijl de poort wel reageert op verzoeken van buitenaf. 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 "Openbare 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 haar viewer- en embed-URL's hierop baseert.
Persistentie: die is er niet, en dat is het ontwerp. Er komt geen volume voor in dat Compose-bestand omdat de container geen diagramgegevens bevat. Een .drawio-bestand is XML die de editor aan uw browser overhandigt. De gekozen opslaglocatie bepaalt waar het bestand terechtkomt: een download op uw eigen machine of de applicatie waarin de editor is ingesloten. Maak een back-up van die bestemming. Als het antwoord een map op de VPS is, dan is die map hetgeen wat beschermd moet worden, samen met de bestandsbeheerder die u gebruikt om deze te bereiken, aangezien draw.io zelf geen kopieën bewaart.
Wat uw server nog wel verlaat. Minder dan voorheen, omdat export niet langer naar externe locaties gaat. De README vermeldt nu EXPORT_URL en DRAWIO_SELF_CONTAINED onder verwijderde variabelen die geen effect meer hebben. Deze verwezen de editor naar de afzonderlijke jgraph/export-server-image, die "het einde van zijn levenscyclus heeft bereikt". Het entrypoint schrijft nu window.EXPORT_URL = null naar PreConfig.js, wat de editor vertelt dat er geen exportservice bestaat. In de praktijk opent Exporteren als > PDF het afdrukvenster van de browser. De optie om een kopie van het diagram toe te voegen wordt daarom niet aangeboden voor PDF, en de server-gerenderde PNG- en JPEG-export is verborgen. PNG-, JPEG- en SVG-export die in de browser wordt gerenderd, werkt nog steeds. ENABLE_DRAWIO_PROXY staat standaard uit en schakelt een /proxy-endpoint 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 vermeldt dit commando.
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, oktober 2026), wat aangeeft hoe beperkt de inhoud is. Er is geen database, geen sessieopslag 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 samenwerken 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 verwijzen, moet u de frontend vanaf de broncode bouwen met uw eigen waarden. Controleer de status van die server voordat u er plannen omheen maakt: per oktober 2026 was de nieuwste push van de excalidraw/excalidraw-room-image op Docker Hub van december 2023.
Waar een tekening daadwerkelijk staat. De scène bevindt zich in de local storage van de browser, op dat apparaat, voor die origin. Open dezelfde URL in een privévenster en het canvas is leeg; dit is de snelste manier om dit zelf vast te stellen. Het wissen van sitegegevens verwijdert de tekening en er is geen kopie op de server om te herstellen. Leer gebruikers daarom om "Save to..." te gebruiken en het .excalidraw-bestand, wat JSON is, op een locatie te bewaren waarvan back-ups worden gemaakt. 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 bevat 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 alleen 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 belangrijkst: een reload bij een defecte 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.
Basic authentication is niet de juiste methode voor Kroki, en de reden daarvoor is belangrijk om te begrijpen. Een wiki-pagina sluit een Kroki-afbeelding in met een <img>-tag. De browser van de lezer haalt die URL op als een cross-origin subresource. De browser voegt Basic-inloggegevens toe die al bekend zijn voor de Kroki-host, maar huidige browsers tonen geen inlogprompt voor een cross-origin afbeelding. Hierdoor krijgt elke lezer die niet rechtstreeks is ingelogd op de Kroki-host een 401-foutmelding en ziet elke diagram op de pagina als een defecte afbeelding. Houd Kroki daarom weg van het openbare internet. Plaats het in hetzelfde Docker-netwerk als de wiki-container en laat de wiki de service bereiken via de servicenaam, zonder dat er poorten op 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 diagramgegevens. Met proto=json verstuurt de editor een init-gebeurtenis wanneer deze gereed is en verwacht als antwoord een load-actie die het diagram bevat. De spinner blijft daarom zichtbaar wanneer deze uitwisseling niet wordt voltooid. 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 exacte bewoording is hoe nginx de fout benoemt, dus de proxy is de eerste verdachte. Kroki kan echter zelf ook een 413 sturen, wat in de documentatie wordt aangeduid als Content Too Large. De standaardwaarde van client_max_body_size in nginx is 1 MB; dit is de limiet waar een grote PlantUML-bron tegenaan loopt, omdat PlantUML binnen de gateway rendert. De eigen KROKI_MAX_BODY_SIZE van Kroki staat standaard ook op 1mb, maar deze bestaat alleen op de companion-containers voor diagrams.net, Excalidraw en Mermaid. Een grote Mermaid-bron kan dus tegen beide limieten aanlopen. Verhoog eerst client_max_body_size en verhoog ook KROKI_MAX_BODY_SIZE op de companion wanneer het diagram via een dergelijke container verloopt.
Mermaid faalt terwijl graphviz werkt. De gateway is in orde en 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. Deze staat standaard op 127.0.0.1, 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 een gewoonte in plaats van een instelling: exporteer het .excalidraw-bestand voor alles wat u wilt bewaren.
FAQ
Blijven mijn diagrammen privé als ik draw.io zelf host?
Het hosten van de applicatiecode op uw eigen server is iets anders dan het waarborgen van de privacy van uw gegevens. draw.io wordt gerenderd in uw browser, waardoor de container zelf nooit een diagram bevat. 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 laat ENABLE_DRAWIO_PROXY uitgeschakeld. Exporteren verstuurt eveneens niets naar buiten: de afbeelding configureert geen exportservice, waardoor PDF via het afdrukvenster van de browser verloopt en DRAWIO_SELF_CONTAINED geen effect meer heeft.
Waarom werkt samenwerking niet in mijn zelf-gehoste Excalidraw?
De officiële pagina van de image vermeldt dat zelf-hosting "geen ondersteuning biedt voor functies voor delen of samenwerken". Live samenwerking vereist de afzonderlijke excalidraw/excalidraw-room websocket-server en voor deellinks is een opslagservice nodig. De adressen van beide worden tijdens het build-proces in de JavaScript-bundel gecompileerd als Vite-variabelen, zoals VITE_APP_WS_SERVER_URL. Het instellen van een omgevingsvariabele op de draaiende container heeft daarom geen effect. Het gebruik van een eigen room-server vereist dat u de frontend vanaf de broncode bouwt met uw eigen waarden.
Hoe render ik Mermaid-diagrammen op mijn eigen server?
Draai Kroki samen 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 daarom rekening met het geheugengebruik: KROKI_MERMAID_MAX_CONCURRENCY staat standaard ingesteld op zes renders tegelijk.
Heb ik een wachtwoord nodig voor deze tools?
Ja, aangezien geen van deze tools over een gebruikersaccount beschikt. draw.io en Excalidraw bieden een volledige editor aan iedereen die de URL vindt, en Kroki rendert alle tekst die ernaartoe wordt gestuurd. Basisauthenticatie bij de reverse proxy volstaat voor de twee editors. Houd Kroki voor de rest ongepubliceerd op een Docker-netwerk dat gedeeld wordt met de wiki. De browser van een lezer zal namelijk geen inlogprompt tonen voor een <img> op een andere oorsprong, waardoor elk ingesloten diagram niet zou laden voor iedereen die niet rechtstreeks bij Kroki is ingelogd.