SSD Nodes Learn Hosting plans →
Guias Matt ConnorPor Matt Connor · Atualizado 2026-08-21

draw.io, Excalidraw ou Kroki: qual hospedar?

Compare draw.io, Excalidraw e Kroki no seu VPS e veja o que realmente chega ao servidor: os dois primeiros desenham no browser; Kroki recebe o texto por HTTP.

Que ferramenta de diagramas self-hosted deve executar?

As ferramentas de diagramas self-hosted têm dois modelos, e o modelo importa mais do que a lista de funcionalidades. draw.io e Excalidraw são aplicações de browser: o container fornece JavaScript, o browser faz o desenho e o servidor nunca vê o diagrama. Kroki funciona de forma oposta. Envia o texto do diagrama por HTTP e recebe uma imagem em resposta. Assim, todos os diagramas passam pela sua própria máquina.

Execute draw.io se quiser um editor completo junto de uma wiki. Execute Excalidraw se quiser uma ferramenta rápida para esboços e aceitar que nada seja guardado fora do browser onde fez o desenho. Execute Kroki se os seus diagramas forem texto armazenado no git junto do código que descrevem.

O que o self-hosting de uma ferramenta de diagramas altera na prática

Seja exato sobre quais partes comunicam com o seu servidor, porque esse facto determina se o self-hosting proporciona privacidade ou apenas disponibilidade.

  • draw.io é renderizado no navegador. O seu container fornece o código da aplicação. O ficheiro é guardado no local que indicar ao editor.
  • Excalidraw é renderizado no navegador e mantém a cena atual no armazenamento local desse navegador. Nada é escrito no servidor.
  • Kroki é renderizado no servidor. O código-fonte do diagrama e a imagem final existem dentro do seu container.

Apenas o terceiro caso transfere dados para hardware que controla. Nos dois primeiros, o self-hosting proporciona controlo dos recursos e disponibilidade: o JavaScript vem do seu host, por isso o editor continua a funcionar quando um terceiro sofre uma indisponibilidade, altera os seus termos ou deixa de estar acessível a partir da sua rede. Isto tem valor real para algumas equipas. É uma afirmação diferente de "o diagrama nunca sai das instalações".

draw.io: um container oficial que não armazena nada

O projeto publica a sua própria imagem, e o início rápido no README resume-se a uma linha.

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

Isso publica o editor em todos os endereços que o servidor tiver. Numa VPS, associe a porta publicada ao loopback e aceda a ela através de um reverse proxy ou de um túnel SSH.

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

Abra http://127.0.0.1:8080/?offline=1&https=0 através do túnel. O README chama a ?offline=1 de "uma funcionalidade de segurança que desativa o suporte para armazenamento na cloud". Sem essa opção, o editor oferece Google Drive, OneDrive e GitHub como destinos de gravação, que são servidores de terceiros.

Associar a porta a 127.0.0.1 é o que mantém essa porta fora da Internet pública. Um -p 8080:8080 simples não é filtrado pelo ufw, porque o Docker insere as suas próprias regras do iptables antes das chains geridas pelo ufw. Assim, a configuração da firewall parece correta, mas a porta responde ao mundo. Docker publica diretamente, ignorando o ufw explica o mecanismo e a correção.

Duas variáveis de ambiente são importantes assim que o editor deixa de estar no 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"

A barra final não é um erro de digitação. O README define DRAWIO_SERVER_URL como o "URL público da implementação com uma barra final" e DRAWIO_BASE_URL como o "mesmo URL sem uma barra final", utilizados pelo viewer, pelo lightbox e pelos caminhos do código de incorporação. Se servir o editor num subcaminho como https://www.example.com/drawio/, ambos os valores têm de incluir esse subcaminho, porque a aplicação constrói os URLs do viewer e da incorporação a partir deles.

Persistência: não existe, e isso é intencional. Esse ficheiro Compose não contém nenhum volume porque o container não armazena dados de diagramas. Um ficheiro .drawio é XML que o editor entrega ao seu browser, e o destino de gravação escolhido determina onde ele fica: um download na sua própria máquina ou a aplicação que incorporou o editor. Faça cópias de segurança desse destino. Se o destino for uma pasta na VPS, então o que deve proteger é essa pasta e o gestor de ficheiros que utiliza para lhe aceder, porque o draw.io não mantém nenhuma cópia.

O que continua a sair do seu servidor. A exportação para PDF é o caso mais claro. O README descreve DRAWIO_SELF_CONTAINED como "Definir como 1 para encaminhar os pedidos de exportação através de ExportProxyServlet do Tomcat (/service/0), em vez de chamar diretamente o servidor de exportação". Interpretando isto ao contrário: por predefinição, uma chamada de exportação não permanece dentro da sua implementação. O projeto também publica jgraph/export-server, um "servidor autónomo de exportação de imagens do draw.io", para quem quiser executar essa renderização no seu próprio hardware. ENABLE_DRAWIO_PROXY está desativado por predefinição e ativa um endpoint /proxy que obtém URLs de imagens externas em nome do browser, por isso mantenha-o desativado, a menos que precise dele.

Excalidraw: um bundle estático sem servidor por trás

A página oficial da imagem fornece este comando.

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

Mova a porta publicada para a interface de loopback pelo mesmo motivo de antes.

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

Dentro do contentor, o nginx serve um bundle JavaScript compilado na porta 80. A imagem publicada tem cerca de 41 MB comprimidos (Docker Hub, agosto de 2026), o que mostra como é pequeno o seu conteúdo. Não há base de dados, armazenamento de sessões nem diretório de uploads, porque não existe nada para armazenar no servidor.

A página da imagem declara claramente a limitação: "Neste momento, alojar a sua própria instância não suporta funcionalidades de partilha ou colaboração." Os botões continuam presentes na interface, por isso é importante conhecer o motivo. A colaboração em tempo real precisa de um servidor websocket, publicado separadamente como excalidraw/excalidraw-room. Um link de partilha precisa de um serviço de armazenamento para guardar a cena cifrada. Os endereços de ambos são compilados no bundle durante o build, como variáveis do Vite (VITE_APP_WS_SERVER_URL, VITE_APP_BACKEND_V2_GET_URL, VITE_APP_BACKEND_V2_POST_URL), e os valores de produção no repositório apontam para os serviços alojados pela própria Excalidraw. O Vite substitui esses valores durante o build, pelo que acabam como strings literais dentro do JavaScript. Defini-los como variáveis de ambiente do contentor não altera nada, porque nenhum código os lê em runtime. Apontar a colaboração para o seu próprio room server implica compilar o frontend a partir do código-fonte com os seus próprios valores. Verifique o estado desse servidor antes de basear o seu planeamento nele: a imagem excalidraw/excalidraw-room no Docker Hub não era reconstruída há mais de dois anos em agosto de 2026.

Onde um desenho fica efetivamente. A cena fica no armazenamento local do browser, nesse dispositivo, para essa origin. Abra o mesmo URL numa janela privada e o canvas estará vazio. Esta é a forma mais rápida de confirmar o comportamento. Limpar os dados do site elimina o desenho, e não existe uma cópia no servidor que possa ser restaurada. Por isso, ensine as pessoas a usar "Guardar em..." e a manter o ficheiro .excalidraw, que é JSON, num local incluído nos backups. Uma instância partilhada dá a cada pessoa o seu próprio canvas privado. Trate-a como um bloco de notas pessoal que está alojado num servidor.

Kroki: diagramas como código, renderizados no seu servidor

Kroki é um gateway HTTP à frente de vários renderizadores. Envia texto com POST e recebe SVG ou PNG. Graphviz, PlantUML, D2 e vários outros renderizadores estão incluídos na imagem do gateway. O renderizador de Mermaid, BPMN e Excalidraw é executado em contentores complementares, por isso Compose é a forma adequada de o executar. Este é o exemplo da documentação do 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 não publica nada no host, por isso os complementos só podem ser alcançados pelo gateway na rede do Compose. É isso que pretende. Altere a linha do gateway para "127.0.0.1:8000:8000", a menos que o wiki que o chama seja executado noutro host. Se ainda não criou um ficheiro Compose num servidor, executar Docker Compose numa VPS explica a estrutura dos ficheiros e o ciclo docker compose up -d.

Execute dois testes básicos, por esta ordem, porque falham por motivos diferentes.

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 é executado dentro do gateway, por isso um documento SVG aqui confirma que o próprio gateway está saudável. Agora teste o caminho que atravessa os contentores.

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

O SVG do segundo comando confirma que KROKI_MERMAID_HOST foi resolvido e que o complemento respondeu. Se o primeiro comando funcionar e o segundo não, o problema está entre os dois contentores. Consulte docker compose logs kroki antes de alterar a sintaxe do diagrama.

O formato GET codifica o diagrama no URL. É assim que um wiki incorpora uma imagem sem qualquer plugin. A documentação fornece este codificador.

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'))"

No Ubuntu, isso imprime python: command not found, porque o sistema fornece python3 e não fornece python sem versão. Use python3. O resultado é colocado no final de um URL com o formato /{diagram-type}/{output-format}/{encoded-diagram}, e qualquer etiqueta <img> pode apontar para esse URL. Existe um limite: KROKI_MAX_URI_LENGTH tem o valor predefinido de 4096 bytes, por isso um diagrama longo tem de ser enviado com POST.

O Kroki lê o texto que lhe envia, por isso as definições de segurança são as que importam. KROKI_SAFE_MODE tem o valor predefinido SECURE, o mais restritivo dos três níveis, e KROKI_PLANTUML_ALLOW_INCLUDE tem o valor predefinido false. Estes valores predefinidos existem porque a diretiva !include do PlantUML lê ficheiros e URLs do ponto de vista do renderizador. Se os flexibilizar num endpoint acessível a qualquer pessoa, estará a disponibilizar na Internet um leitor de ficheiros executado dentro do seu contentor. Mantenha-os inalterados, a menos que saiba de que caminho de inclusão precisa. Nesse caso, especifique-o com KROKI_PLANTUML_INCLUDE_PATH.

Memória: qual é o impacto num VPS pequeno

A ordem é previsível quando se sabe o que cada contentor executa.

  • A imagem do Excalidraw é o nginx a servir ficheiros estáticos. É, de longe, a opção mais económica das três.
  • O draw.io executa o Tomcat, um servidor de aplicações Java, por isso mantém uma JVM (Java virtual machine) mesmo quando ninguém está a desenhar.
  • O gateway Kroki também é um serviço Java, distribuído como um jar para instalações manuais.
  • O companion do mermaid é o mais dispendioso. O respetivo Dockerfile instala o Chromium e define PUPPETEER_EXECUTABLE_PATH=/usr/lib/chromium/chrome, porque o Mermaid faz a renderização num motor de browser real.

Por isso, os valores em repouso dizem muito pouco. O valor importante é o pico enquanto um diagrama é renderizado, e KROKI_MERMAID_MAX_CONCURRENCY tem o valor predefinido 6, portanto podem estar em curso seis renderizações no browser ao mesmo tempo. Meça no seu próprio servidor em vez de confiar num valor publicado.

docker stats --no-stream
docker system df

Execute o primeiro comando quando tudo estiver em repouso e depois novamente enquanto renderiza um diagrama Mermaid grande num ciclo. Se o pico for demasiado elevado num plano pequeno, limite-o em vez de tentar adivinhar: definir limites de memória num serviço Compose mostra a sintaxe e o que acontece quando um contentor atinge o seu limite. Remover o companion do mermaid também é uma opção válida, porque o gateway continua a servir todos os renderizadores integrados.

Nenhum destes serviços fornece um modelo de utilizadores, por isso coloque um à frente

draw.io não tem contas. Excalidraw não tem contas. O Kroki responde a qualquer pedido que lhe chegue. Qualquer início de sessão tem de ser fornecido pelo proxy.

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

htpasswd -c cria o ficheiro e substitui um ficheiro existente, por isso passe -c na primeira vez e nunca mais.

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

Aplique-o com sudo nginx -t && sudo systemctl reload nginx. A parte nginx -t é a mais importante: um reload de uma configuração inválida mantém a configuração anterior em execução. Assim, o site continua a funcionar, mas a alteração não fica ativa. A configuração do reverse proxy, explicada linha a linha descreve o bloco de cabeçalhos e os caminhos dos certificados omitidos por este trecho.

A autenticação básica é a ferramenta errada para o Kroki, e vale a pena compreender o motivo. Uma página wiki incorpora uma imagem do Kroki com uma tag <img>. O browser do leitor obtém esse URL como um subrecurso e não envia as suas credenciais para uma origem diferente. Por isso, o pedido devolve 401 e todos os diagramas da página são apresentados como imagens quebradas. Em vez disso, mantenha o Kroki fora da internet pública. Coloque-o na mesma rede Docker que o contentor da wiki e permita que a wiki o alcance pelo nome do serviço, sem publicar qualquer porta no host. Como as redes Compose resolvem nomes de serviços é o elemento que permite isto.

Diagramas junto de um wiki self-hosted

Esta é a razão habitual para as pessoas quererem esta configuração. Uma página do wiki precisa de uma imagem, e ninguém quer que essa imagem seja uma captura de ecrã do portátil de alguém.

O BookStack tem uma integração nativa com um editor self-hosted. O URL de incorporação predefinido é https://embed.diagrams.net/?embed=1&proto=json&spin=1&configure=1, e uma linha em .env redireciona-o para o seu contentor.

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

Copie a cadeia de consulta exatamente. A documentação do BookStack diz que embed=1&proto=json&spin=1 "são necessários para a integração com o BookStack funcionar", porque selecionam o protocolo de mensagens JSON usado pelas duas páginas para comunicarem. A mesma página indica stealth=1 "se não quiser que sejam utilizados outros serviços externos", que é a opção a adicionar quando o objetivo do self-hosting era impedir chamadas de saída. Com esta configuração, o BookStack guarda o desenho no seu próprio armazenamento de imagens, junto da página, pelo que a cópia de segurança do wiki que já faz também inclui a cópia de segurança dos diagramas.

Se ainda não decidiu qual será o wiki, resolva isso primeiro. Escolher entre BookStack, Wiki.js e Outline é a decisão anterior, porque o wiki determina como um diagrama é anexado a uma página e, por isso, quais destas ferramentas deve adicionar.

Modos de falha e as mensagens que verá

O editor de desenhos abre no BookStack e fica a carregar indefinidamente. O indicador fica spin=1 à espera de um handshake que nunca chega. Confirme se embed=1&proto=json&spin=1 está presente no valor de DRAWIO e se a parte do host não contém erros de escrita.

A moldura do editor permanece vazia numa wiki HTTPS. A consola do browser indica conteúdo misto ao carregar http:// dentro de https://. O browser bloqueia a moldura e o draw.io nunca é executado. Sirva o editor através de HTTPS.

O Kroki devolve 413 Request Entity Too Large. Essa mensagem vem do nginx, não do Kroki. O valor predefinido de client_max_body_size no nginx é 1 MB e o valor predefinido de KROKI_MAX_BODY_SIZE no Kroki é 1mb, pelo que um código-fonte PlantUML grande atinge o limite mais baixo. Aumente ambos.

O Mermaid falha, mas o graphviz funciona. O gateway está saudável, mas o companion não está a ser contactado. Confirme se o serviço está ativo com docker compose ps. Depois, verifique se KROKI_MERMAID_HOST corresponde ao nome do serviço, porque o valor predefinido é 127.0.0.1. Dentro do contentor do gateway, esse nome refere-se ao próprio gateway.

A colaboração do Excalidraw nunca estabelece ligação. Se criou um frontend ligado ao seu próprio room server e o colocou atrás do nginx, o proxy tem de atualizar a ligação com proxy_set_header Upgrade $http_upgrade; e proxy_set_header Connection "upgrade";. Sem estas diretivas, o handshake websocket é tratado como um pedido HTTP normal e a sessão nunca é iniciada.

A tela fica vazia depois de limpar os dados do browser. A cena estava guardada no armazenamento local desse dispositivo e não existe uma cópia no servidor. A correção é um hábito, não uma definição: exporte o ficheiro .excalidraw para tudo o que mereça ser preservado.

FAQ

O self-hosting do draw.io mantém os meus diagramas privados?

Mantém o código da aplicação no seu servidor, mas isso é diferente de manter os dados privados. O draw.io faz a renderização no seu navegador, por isso o contentor nunca chega a conter um diagrama. A privacidade depende então do local onde guarda o ficheiro e das chamadas de saída que mantém ativas. Use ?offline=1 para desativar os destinos de armazenamento na nuvem e lembre-se de que os pedidos de exportação são enviados para um servidor de exportação, a menos que defina DRAWIO_SELF_CONTAINED=1 e execute jgraph/export-server por sua conta.

Por que razão a colaboração não funciona no meu Excalidraw self-hosted?

A página oficial da imagem indica que o self-hosting "não suporta funcionalidades de partilha ou colaboração". A colaboração em tempo real precisa do servidor websocket separado excalidraw/excalidraw-room, e as ligações de partilha precisam de um serviço de armazenamento. Os endereços de ambos são compilados no bundle JavaScript durante a compilação, como variáveis Vite do tipo VITE_APP_WS_SERVER_URL. Por isso, definir uma variável de ambiente no contentor em execução não tem efeito. Para usar o seu próprio servidor de salas, tem de compilar o frontend a partir do código-fonte com os seus valores.

Como renderizo diagramas Mermaid no meu próprio servidor?

Execute o Kroki com o respetivo contentor companion do Mermaid e defina KROKI_MERMAID_HOST com o nome desse serviço. Depois, faça POST do texto do diagrama para /mermaid/svg e leia o SVG na resposta, ou codifique o diagrama num URL GET e aponte uma tag <img> para esse URL. O companion controla o Chromium através do Puppeteer porque o Mermaid precisa de um motor de navegador. Por isso, planeie a memória: KROKI_MERMAID_MAX_CONCURRENCY assume por predefinição seis renderizações simultâneas.

Preciso de colocar uma palavra-passe à frente destas ferramentas?

Sim, porque nenhuma delas tem contas de utilizador. O draw.io e o Excalidraw disponibilizam um editor completo a qualquer pessoa que encontre o URL, e o Kroki renderiza qualquer texto que lhe seja enviado. A autenticação básica no reverse proxy é suficiente para os dois editores. No caso do Kroki, mantenha-o não publicado numa rede Docker partilhada com o wiki, porque um pedido <img> enviado pelo navegador do leitor não transportará credenciais para outra origem e todos os diagramas incorporados deixariam de funcionar.

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