SSD Nodes Learn Hosting plans →
Guide Matt ConnorDi Matt Connor · Aggiornato 2026-08-21

Strumenti per diagrammi self-hosted a confronto

draw.io ed Excalidraw disegnano nel browser; Kroki invia il testo al server. Scopri cosa resta sul client e quando il self-hosting protegge davvero la privacy.

Quale strumento per diagrammi self-hosted dovresti eseguire?

Gli strumenti per diagrammi self-hosted rientrano in due categorie, e questa distinzione conta più dell’elenco delle funzionalità. draw.io ed Excalidraw sono applicazioni browser: il container distribuisce JavaScript, il browser esegue il disegno e il server non vede mai il diagramma. Kroki funziona al contrario. Gli invii il testo del diagramma tramite HTTP e restituisce un’immagine, quindi ogni diagramma passa dal tuo computer.

Esegui draw.io se vuoi un editor completo affiancato a un wiki. Esegui Excalidraw se vuoi una superficie per schizzi rapidi e accetti che non salvi nulla al di fuori del browser in cui hai creato il disegno. Esegui Kroki se i tuoi diagrammi sono testo archiviato in git insieme al codice che descrivono.

Che cosa cambia davvero con il self-hosting di uno strumento per diagrammi

Indica con precisione quali componenti interagiscono con il server, perché questo determina se il self-hosting offre privacy oppure soltanto disponibilità.

  • draw.io esegue il rendering nel browser. Il container distribuisce il codice dell'applicazione. Il file viene salvato nella posizione indicata nell'editor.
  • Excalidraw esegue il rendering nel browser e conserva la scena corrente nel local storage del browser. Sul server non viene scritto nulla.
  • Kroki esegue il rendering sul server. Sia il sorgente del diagramma sia l'immagine generata si trovano all'interno del container.

Solo il terzo caso trasferisce i dati su hardware sotto il tuo controllo. Nei primi due casi, il self-hosting offre controllo sugli asset e disponibilità: il codice JavaScript viene distribuito dal tuo host, quindi l'editor continua a funzionare quando un fornitore esterno subisce un'interruzione, modifica le proprie condizioni o non è raggiungibile dalla tua rete. Per alcuni team questo ha un valore economico concreto. È un'affermazione diversa da «il diagramma non lascia mai l'azienda».

draw.io: un container ufficiale che non memorizza nulla

Il progetto pubblica una propria immagine e la procedura rapida nel README consiste in una sola riga.

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

In questo modo l'editor viene pubblicato su ogni indirizzo disponibile sul server. Su un VPS, associa la porta pubblicata all'interfaccia di loopback e raggiungila tramite un reverse proxy o un tunnel SSH.

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

Apri http://127.0.0.1:8080/?offline=1&https=0 attraverso il tunnel. Il README definisce ?offline=1 una «funzionalità di sicurezza che disabilita il supporto per il cloud storage». Senza questa opzione, l'editor offre Google Drive, OneDrive e GitHub come destinazioni di salvataggio, cioè server gestiti da terzi.

L'associazione a 127.0.0.1 impedisce che la porta sia esposta a Internet. Un semplice -p 8080:8080 non viene filtrato da ufw, perché Docker inserisce le proprie regole iptables prima delle chain gestite da ufw. Di conseguenza, il firewall può sembrare configurato correttamente mentre la porta risponde a chiunque su Internet. Docker pubblica le porte aggirando direttamente ufw descrive il meccanismo e la correzione.

Non appena l'editor non è più disponibile solo su localhost, diventano importanti due variabili d'ambiente.

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 barra finale non è un errore di battitura. Il README definisce DRAWIO_SERVER_URL come «URL pubblico della distribuzione con una barra finale» e DRAWIO_BASE_URL come «lo stesso URL senza barra finale», utilizzati dal viewer, dal lightbox e dai percorsi del codice di incorporamento. Se pubblichi l'editor in un sottopercorso come https://www.example.com/drawio/, entrambi i valori devono includere quel sottopercorso, perché l'applicazione costruisce da questi valori gli URL del viewer e del codice incorporato.

Persistenza: non esiste, ed è una scelta progettuale. Nel file Compose non compare alcun volume, perché il container non contiene dati dei diagrammi. Un file .drawio contiene XML che l'editor invia al browser; la destinazione di salvataggio scelta determina dove viene memorizzato: un download sul computer locale oppure l'applicazione che ha incorporato l'editor. Esegui il backup di quella destinazione. Se si tratta di una directory sul VPS, l'elemento da proteggere è quella directory e il file manager utilizzato per raggiungerla, perché draw.io non conserva alcuna copia.

Cosa lascia comunque il server. L'esportazione in PDF è il caso più evidente. Il README descrive DRAWIO_SELF_CONTAINED come «Impostare su 1 per instradare le richieste di esportazione tramite ExportProxyServlet di Tomcat (/service/0) invece di chiamare direttamente il server di esportazione». In altri termini, per impostazione predefinita una richiesta di esportazione non rimane all'interno della tua distribuzione. Il progetto pubblica anche jgraph/export-server, un «server standalone per l'esportazione delle immagini di draw.io», destinato a chi vuole eseguire il rendering sulla propria infrastruttura. ENABLE_DRAWIO_PROXY è disabilitato per impostazione predefinita e abilita un endpoint /proxy che recupera URL di immagini esterne per conto del browser; lascialo disabilitato se non ti serve.

Excalidraw: un bundle statico senza server a monte

La pagina dell'immagine ufficiale fornisce questo comando.

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

Sposta la porta pubblicata sull'interfaccia loopback per lo stesso motivo indicato in precedenza.

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

All'interno del container, nginx pubblica un bundle JavaScript compilato sulla porta 80. L'immagine pubblicata occupa circa 41 MB compressi (Docker Hub, agosto 2026), quindi contiene una quantità minima di componenti. Non ci sono database, session store o directory per gli upload, perché sul server non c'è nulla da memorizzare.

La pagina dell'immagine indica chiaramente il limite: "Al momento, l'hosting autonomo di una propria istanza non supporta le funzionalità di condivisione o collaborazione." I pulsanti sono comunque presenti nell'interfaccia, quindi è utile conoscere il motivo. La collaborazione in tempo reale richiede un server websocket, pubblicato separatamente come excalidraw/excalidraw-room. Un link di condivisione richiede un servizio di storage che conservi la scena crittografata. Gli indirizzi di entrambi vengono compilati nel bundle durante la build come variabili Vite (VITE_APP_WS_SERVER_URL, VITE_APP_BACKEND_V2_GET_URL, VITE_APP_BACKEND_V2_POST_URL), mentre i valori di produzione nel repository puntano ai servizi ospitati da Excalidraw. Vite sostituisce questi valori durante la build, che quindi finiscono nel JavaScript come stringhe letterali. Impostarli come variabili d'ambiente del container non cambia nulla, perché nessun codice li legge durante l'esecuzione. Per puntare la collaborazione al proprio room server è necessario compilare il frontend dai sorgenti usando i propri valori. Verifica lo stato di quel server prima di basarci il progetto: ad agosto 2026, l'immagine excalidraw/excalidraw-room su Docker Hub non veniva ricompilata da oltre due anni.

Dove viene memorizzato realmente un disegno. La scena si trova nel local storage del browser, su quel dispositivo e per quell'origine. Apri lo stesso URL in una finestra privata: la tela sarà vuota. È il modo più rapido per verificarlo. La cancellazione dei dati del sito elimina il disegno e non esiste una copia sul server da cui ripristinarlo. Insegna quindi agli utenti a usare "Salva in..." e a conservare il file .excalidraw, che è in formato JSON, in una posizione inclusa nei backup. Un'istanza condivisa assegna a ogni persona una tela privata. Considerala un blocco da disegno personale che viene semplicemente ospitato su un server.

Kroki: diagrammi come codice, renderizzati sul server

Kroki è un gateway HTTP unico davanti a numerosi renderer. Si invia testo con una richiesta POST e si riceve un SVG o un PNG. Graphviz, PlantUML, D2 e diversi altri renderer sono inclusi nell'immagine del gateway. Il rendering di Mermaid, BPMN ed Excalidraw viene eseguito in container complementari, quindi Compose è la soluzione più adatta per eseguirli. Questo è l'esempio della documentazione di 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 non pubblica nulla sull'host, quindi i container complementari sono raggiungibili soltanto dal gateway sulla rete Compose. È il comportamento desiderato. Modifica la riga del gateway in "127.0.0.1:8000:8000", a meno che il wiki che lo richiama non sia eseguito su un host diverso. Se non hai mai scritto un file Compose su un server, eseguire Docker Compose su un VPS descrive la struttura dei file e il ciclo docker compose up -d.

Esegui due test di base, in quest'ordine, perché possono fallire per motivi diversi.

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 viene eseguito all'interno del gateway, quindi un documento SVG in questo punto dimostra che il gateway è operativo. Ora verifica il percorso che attraversa i container.

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

L'SVG restituito dal secondo comando dimostra che KROKI_MERMAID_HOST è stato risolto e che il container complementare ha risposto. Se il primo comando funziona e il secondo no, il problema è tra i due container. Consulta quindi docker compose logs kroki prima di modificare la sintassi del diagramma.

La modalità GET codifica il diagramma nell'URL. È così che un wiki incorpora un'immagine senza alcun plugin. La documentazione fornisce questo 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'))"

Su Ubuntu viene visualizzato python: command not found, perché il sistema include python3 e non include alcun python senza versione. Usa python3. L'output viene aggiunto alla fine di un URL con struttura /{diagram-type}/{output-format}/{encoded-diagram} e qualsiasi tag <img> può puntare a quell'URL. Esiste un limite: KROKI_MAX_URI_LENGTH è impostato per impostazione predefinita su 4096 byte, quindi un diagramma lungo deve essere inviato tramite POST.

Kroki legge il testo che gli invii, quindi le impostazioni di sicurezza pertinenti sono quelle del servizio. KROKI_SAFE_MODE è impostato per impostazione predefinita su SECURE, il più restrittivo dei tre livelli, mentre KROKI_PLANTUML_ALLOW_INCLUDE è impostato per impostazione predefinita su false. Questi valori predefiniti sono necessari perché la direttiva !include di PlantUML legge file e URL dal punto di vista del renderer. Se li rendi meno restrittivi su un endpoint raggiungibile da chiunque, concedi a Internet un lettore di file eseguito all'interno del container. Lasciali invariati, a meno che tu non sappia quale percorso di inclusione ti serve; in quel caso, specificarlo con KROKI_PLANTUML_INCLUDE_PATH.

Memoria: quale componente pesa di più su un VPS di piccole dimensioni

L’ordine è prevedibile se si considera cosa esegue ogni container.

  • L’immagine Excalidraw usa nginx per servire file statici. È di gran lunga la soluzione che consuma meno risorse tra le tre.
  • draw.io esegue Tomcat, un application server Java, quindi utilizza una JVM (Java virtual machine) anche quando nessuno sta disegnando.
  • Anche il gateway Kroki è un servizio Java, distribuito come file jar per le installazioni manuali.
  • Il componente aggiuntivo mermaid è quello più oneroso. Il relativo Dockerfile installa Chromium e imposta PUPPETEER_EXECUTABLE_PATH=/usr/lib/chromium/chrome, perché Mermaid esegue il rendering in un motore browser reale.

Per questo i valori a riposo forniscono poche indicazioni. Il dato importante è il picco durante il rendering di un diagramma, e KROKI_MERMAID_MAX_CONCURRENCY ha valore predefinito 6: possono quindi essere in corso contemporaneamente fino a sei rendering nel browser. Misura il consumo sul tuo server invece di affidarti a un valore pubblicato.

docker stats --no-stream
docker system df

Esegui il primo comando quando tutto è inattivo, quindi ripetilo mentre esegui in ciclo il rendering di un diagramma Mermaid di grandi dimensioni. Se il picco è problematico su un piano di piccole dimensioni, imposta un limite invece di procedere per tentativi: impostare limiti di memoria su un servizio Compose mostra la sintassi e il comportamento del container quando raggiunge il limite. Anche rimuovere il componente aggiuntivo mermaid è una scelta valida, perché il gateway continua a fornire tutti i renderer integrati.

Nessuno di questi servizi include un modello di utenti, quindi bisogna anteporre un componente che lo fornisca

draw.io non gestisce account. Excalidraw non gestisce account. Kroki risponde a qualsiasi richiesta che lo raggiunga. L'autenticazione deve quindi essere gestita dal proxy.

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

htpasswd -c crea il file e sovrascrive quello esistente, quindi va passato -c la prima volta e mai più in seguito.

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;
    }
}

Applicarlo con sudo nginx -t && sudo systemctl reload nginx. La parte nginx -t è quella importante: se la configurazione non è valida, un reload mantiene in esecuzione la configurazione precedente. Il sito continua quindi a funzionare, ma la modifica non è attiva. La configurazione del reverse proxy, spiegata riga per riga descrive il blocco degli header e i percorsi dei certificati omessi da questo frammento.

L'autenticazione di base è lo strumento sbagliato per Kroki, e vale la pena capire il motivo. Una pagina wiki incorpora un'immagine Kroki con un tag <img>. Il browser del lettore recupera quell'URL come sotto-risorsa e non invia le credenziali a un'origine diversa. La richiesta restituisce quindi 401 e tutti i diagrammi della pagina vengono visualizzati come immagini non disponibili. È preferibile non esporre Kroki a Internet. Collocarlo sulla stessa rete Docker del container wiki e consentire al wiki di raggiungerlo tramite il nome del servizio, senza pubblicare alcuna porta sull'host. Come le reti Compose risolvono i nomi dei servizi descrive il meccanismo che rende possibile questa configurazione.

Diagrammi affiancati a una wiki self-hosted

Questo è il motivo principale per cui le persone cercano una soluzione di questo tipo. Una pagina wiki ha bisogno di un'immagine, e nessuno vuole che quell'immagine sia uno screenshot dal laptop di qualcuno.

BookStack offre un hook nativo per un editor self-hosted. Il relativo URL di incorporamento predefinito è https://embed.diagrams.net/?embed=1&proto=json&spin=1&configure=1 e una riga in .env lo sposta nel container.

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

Copia esattamente la stringa di query. La documentazione di BookStack indica che embed=1&proto=json&spin=1 «sono necessari affinché l'integrazione con BookStack funzioni», perché selezionano il protocollo di messaggi JSON usato dalle due pagine per comunicare. La stessa pagina indica stealth=1 «se non vuoi usare altri servizi esterni»: è l'opzione da aggiungere quando l'obiettivo del self-hosting è bloccare le chiamate in uscita. Dopo questa configurazione, BookStack salva il disegno nel proprio archivio di immagini, accanto alla pagina. Il backup della wiki che esegui già include quindi anche il backup dei diagrammi.

Se non hai ancora scelto la wiki, occupati prima di questo. Scegliere tra BookStack, Wiki.js e Outline è la decisione da prendere per prima, perché la wiki determina come allegare un diagramma a una pagina e quindi quale di questi strumenti aggiungere.

Modalità di errore e stringhe visualizzate

L’editor di disegni si apre in BookStack e resta in caricamento indefinito. Lo spinner resta in attesa di un handshake che non arriva mai: spin=1. Verificare che embed=1&proto=json&spin=1 sia presente nel valore DRAWIO e che la parte relativa all’host non contenga errori di battitura.

Il frame dell’editor resta vuoto in un wiki HTTPS. La console del browser segnala contenuti misti durante il caricamento di http:// all’interno di https://. Il browser blocca il frame e draw.io non viene mai eseguito. Pubblicare l’editor tramite HTTPS.

Kroki restituisce 413 Request Entity Too Large. Questa stringa proviene da nginx, non da Kroki. Il valore predefinito di client_max_body_size in nginx è 1 MB, mentre il valore predefinito di KROKI_MAX_BODY_SIZE in Kroki è 1mb. Una sorgente PlantUML di grandi dimensioni raggiunge quindi il limite più basso tra i due. Aumentare entrambi i valori.

Mermaid non funziona, mentre graphviz funziona. Il gateway è operativo, ma il companion non viene raggiunto. Verificare che il servizio sia attivo con docker compose ps, quindi controllare che KROKI_MERMAID_HOST corrisponda al nome del servizio. Per impostazione predefinita è 127.0.0.1, che all’interno del container del gateway indica il gateway stesso.

La collaborazione in Excalidraw non stabilisce mai la connessione. Se è stato creato un frontend che usa un proprio room server e lo si è pubblicato dietro nginx, il proxy deve aggiornare la connessione usando proxy_set_header Upgrade $http_upgrade; e proxy_set_header Connection "upgrade";. Senza questi header, l’handshake WebSocket riceve una risposta come normale richiesta HTTP e la sessione non viene mai avviata.

La canvas è vuota dopo la pulizia del browser. La scena era archiviata nel local storage di quel dispositivo e non esiste una copia sul server. La soluzione consiste in un’abitudine, non in un’impostazione: esportare il file .excalidraw per qualsiasi contenuto che si desidera conservare.

FAQ

I diagrammi restano privati se eseguo draw.io in self-hosting?

Il codice dell’applicazione resta sul server, ma questo non significa che i dati siano privati. draw.io esegue il rendering nel browser, quindi il container non contiene mai alcun diagramma. La privacy dipende da dove salvi il file e dalle chiamate in uscita che lasci abilitate. Usa ?offline=1 per disabilitare le destinazioni di archiviazione cloud e ricorda che le richieste di esportazione vengono inviate a un server di esportazione, a meno che tu non imposti DRAWIO_SELF_CONTAINED=1 e non esegua direttamente jgraph/export-server.

Perché la collaborazione non funziona con Excalidraw in self-hosting?

La pagina dell’immagine ufficiale specifica che il self-hosting «non supporta le funzionalità di condivisione o collaborazione». La collaborazione in tempo reale richiede il server WebSocket separato excalidraw/excalidraw-room, mentre i link di condivisione richiedono un servizio di archiviazione. Gli indirizzi di entrambi vengono incorporati nel bundle JavaScript al momento della build come variabili Vite, ad esempio VITE_APP_WS_SERVER_URL, quindi impostare una variabile d’ambiente nel container in esecuzione non ha alcun effetto. Per usare un server delle stanze personalizzato devi compilare il frontend dal codice sorgente con i tuoi valori.

Come posso eseguire il rendering dei diagrammi Mermaid sul mio server?

Esegui Kroki con il relativo container companion per Mermaid e imposta KROKI_MERMAID_HOST sul nome di quel servizio. Invia quindi con POST il testo del diagramma a /mermaid/svg e leggi l’SVG dalla risposta, oppure codifica il diagramma in un URL GET e usalo come destinazione di un tag <img>. Il companion controlla Chromium tramite Puppeteer perché Mermaid richiede un motore browser; pianifica quindi la memoria necessaria: KROKI_MERMAID_MAX_CONCURRENCY è impostato per impostazione predefinita su sei rendering simultanei.

Devo proteggere questi strumenti con una password?

Sì, perché nessuno di questi strumenti gestisce account. draw.io ed Excalidraw forniscono un editor completo a chiunque trovi l’URL, mentre Kroki esegue il rendering di qualsiasi testo gli venga inviato. L’autenticazione di base sul reverse proxy è sufficiente per i due editor. Per Kroki, non pubblicarlo e mantienilo su una rete Docker condivisa con il wiki, perché una richiesta <img> dal browser del lettore non trasporta le credenziali verso un’altra origine e ogni diagramma incorporato non verrebbe visualizzato.

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