Self-hosted инструменты для диаграмм: сравнение
Сравнение draw.io, Excalidraw и Kroki для локального размещения. Узнайте, какие данные передаются на сервер, а какие остаются в браузере, и как это влияет на приватность.
Какой инструмент для создания диаграмм выбрать для self-hosted размещения?
Self-hosted инструменты для создания диаграмм делятся на два типа, и этот тип важнее, чем список функций. draw.io и Excalidraw — это браузерные приложения: контейнер поставляет JavaScript, отрисовка происходит в вашем браузере, а сервер никогда не видит саму диаграмму. Kroki работает иначе. Вы отправляете ему текст диаграммы по HTTP, а он возвращает изображение, поэтому каждая диаграмма проходит через ваш сервер.
Используйте draw.io, если вам нужен полноценный редактор рядом с wiki. Используйте Excalidraw, если вам нужна быстрая «доска» для набросков и вы готовы к тому, что данные не сохраняются вне браузера, в котором вы рисовали. Используйте Kroki, если ваши диаграммы представляют собой текст, который хранится в git рядом с описываемым кодом.
Что на самом деле меняет self-hosting инструментов для построения диаграмм
Чётко определите, какие компоненты взаимодействуют с вашим сервером. Именно этот факт определяет, обеспечивает ли self-hosting конфиденциальность или только доступность.
- draw.io выполняет рендеринг в браузере. Ваш контейнер раздаёт только код приложения. Файл сохраняется туда, куда вы укажете в редакторе.
- Excalidraw выполняет рендеринг в браузере и хранит текущую сцену в локальном хранилище (local storage) этого браузера. На стороне сервера ничего не записывается.
- Kroki выполняет рендеринг на сервере. И исходный код диаграммы, и готовое изображение находятся внутри вашего контейнера.
Только в третьем случае данные перемещаются на оборудование, которое вы контролируете. В первых двух случаях self-hosting обеспечивает контроль над ресурсами и доступность: JavaScript загружается с вашего хоста, поэтому редактор продолжает работать, даже если сторонний сервис недоступен, изменил условия использования или заблокирован в вашей сети. Для некоторых команд это имеет реальную ценность. Это утверждение отличается от тезиса «диаграмма никогда не покидает периметр сети».
draw.io: официальный контейнер, который ничего не хранит
Проект публикует собственный образ, а инструкция по быстрому запуску в его README состоит из одной строки.
docker run -it --rm --name="draw" -p 8080:8080 -p 8443:8443 jgraph/drawioЭта команда публикует редактор на всех адресах, доступных серверу. На VPS привязывайте опубликованный порт к loopback и обращайтесь к нему через reverse proxy или SSH-туннель.
docker run -d --name drawio --restart unless-stopped -p 127.0.0.1:8080:8080 jgraph/drawioОткройте http://127.0.0.1:8080/?offline=1&https=0 через туннель. В README ?offline=1 назван «функцией безопасности, отключающей поддержку облачных хранилищ». Без этого параметра редактор предлагает Google Drive, OneDrive и GitHub в качестве мест для сохранения, что означает использование сторонних серверов.
Привязка к 127.0.0.1 — это то, что защищает порт от доступа из публичного интернета. Обычный -p 8080:8080 не фильтруется через ufw, так как Docker вставляет свои правила iptables перед цепочками, которыми управляет ufw. В результате файрвол выглядит корректно настроенным, но порт отвечает всему миру. В публикации Docker в обход ufw описаны механизм этой проблемы и способы её решения.
Две переменные окружения важны, как только редактор перестаёт работать на 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"Слеш в конце — это не опечатка. В README DRAWIO_SERVER_URL определяется как «публичный URL развёртывания со слешем в конце», а DRAWIO_BASE_URL — как «тот же URL без слеша в конце», который используется для просмотра, lightbox и путей встраиваемого кода. Если вы размещаете редактор по подпути, например https://www.example.com/drawio/, оба значения должны содержать этот подпуть, так как приложение строит URL для просмотра и встраивания на их основе.
Персистентность: её нет, и это заложено в архитектуру. В этом файле Compose нет разделов volume, так как контейнер не хранит данные диаграмм. Файл .drawio — это XML, который редактор передаёт вашему браузеру, а выбранное место сохранения определяет, где он окажется: загрузка на ваш локальный компьютер или приложение, в которое встроен редактор. Создавайте резервные копии этого целевого расположения. Если это папка на VPS, то защищать нужно именно её и файловый менеджер, который вы используете для доступа к ней, так как draw.io не хранит никаких копий.
Что всё-таки покидает ваш сервер. Экспорт в PDF — самый очевидный пример. В README DRAWIO_SELF_CONTAINED описан как параметр, который нужно «установить в 1 для перенаправления запросов на экспорт через ExportProxyServlet (/service/0) в Tomcat вместо прямого вызова сервера экспорта». Читайте это наоборот: по умолчанию запрос на экспорт не остаётся внутри вашего развёртывания. Проект также публикует jgraph/export-server — «автономный сервер экспорта изображений для draw.io» для тех, кто хочет выполнять рендеринг на собственном оборудовании. ENABLE_DRAWIO_PROXY по умолчанию выключен и активирует эндпоинт /proxy, который загружает внешние URL изображений от имени браузера, поэтому оставляйте его выключенным, если в нём нет необходимости.
Excalidraw: статический бандл без серверной части
Официальная страница образа предлагает следующую команду.
docker run --rm -dit --name excalidraw -p 5000:80 excalidraw/excalidraw:latestПеренесите опубликованный порт на loopback по той же причине, что и ранее.
docker run -d --name excalidraw --restart unless-stopped -p 127.0.0.1:5000:80 excalidraw/excalidraw:latestВнутри контейнера nginx отдает скомпилированный JavaScript-бандл на 80 порту. Размер опубликованного образа составляет около 41 MB в сжатом виде (Docker Hub, август 2026 года), что говорит о минимальном составе содержимого. Здесь нет базы данных, хранилища сессий или директории для загрузок, так как на сервере нечего хранить.
Страница образа прямо указывает на ограничение: «На данный момент самостоятельный хостинг собственного экземпляра не поддерживает функции совместного использования или совместной работы». Кнопки по-прежнему присутствуют в интерфейсе, поэтому важно понимать причину. Для совместной работы в реальном времени требуется websocket-сервер, который публикуется отдельно как excalidraw/excalidraw-room. Для ссылки на общий доступ требуется сервис хранения, где будет размещена зашифрованная сцена. Адреса обоих сервисов вшиваются в бандл на этапе сборки в виде переменных Vite (VITE_APP_WS_SERVER_URL, VITE_APP_BACKEND_V2_GET_URL, VITE_APP_BACKEND_V2_POST_URL), а значения в репозитории указывают на собственные хостинговые сервисы Excalidraw. Vite подставляет эти значения во время сборки, поэтому они становятся обычными строками внутри JavaScript. Установка их в качестве переменных окружения контейнера ничего не меняет, так как никакой код не считывает их во время выполнения. Чтобы направить совместную работу на свой сервер комнат, необходимо собрать фронтенд из исходного кода с собственными значениями. Проверьте состояние этого сервера, прежде чем планировать работу с ним: образ excalidraw/excalidraw-room на Docker Hub не пересобирался более двух лет по состоянию на август 2026 года.
Где на самом деле хранится рисунок. Сцена находится в локальном хранилище браузера, на конкретном устройстве, для конкретного источника (origin). Откройте тот же URL в приватном окне, и холст будет пустым — это самый быстрый способ убедиться в этом самостоятельно. Очистка данных сайта удаляет рисунок, и серверной копии для восстановления не существует. Поэтому приучите пользователей использовать «Save to...» и хранить файл .excalidraw, который представляет собой JSON, в месте, где выполняется резервное копирование. Общий экземпляр предоставляет каждому человеку собственный приватный холст. Рассматривайте его как личный блокнот для эскизов, который просто размещен на сервере.
Kroki: диаграммы как код с отрисовкой на вашем сервере
Kroki — это единый HTTP-шлюз для множества инструментов отрисовки. Вы отправляете текст методом POST и получаете в ответ SVG или PNG. Graphviz, PlantUML, D2 и ряд других инструментов уже встроены в образ шлюза. Отрисовка Mermaid, BPMN и Excalidraw выполняется в отдельных контейнерах, поэтому для запуска оптимально использовать Compose. Ниже приведен пример из документации 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 не публикует порты на хост, поэтому вспомогательные контейнеры доступны только шлюзу внутри сети Compose. Это именно то, что требуется. Измените строку шлюза на "127.0.0.1:8000:8000", если только вики, обращающаяся к нему, не работает на другом хосте. Если вы ранее не создавали файлы Compose на сервере, в руководстве запуск Docker Compose на VPS описана структура файлов и цикл docker compose up -d.
Выполните два теста работоспособности в указанном порядке, так как они могут завершиться ошибкой по разным причинам.
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 работает внутри шлюза, поэтому получение SVG-документа здесь подтверждает исправность самого шлюза. Теперь проверьте путь, проходящий между контейнерами.
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, полученный после второй команды, подтверждает, что KROKI_MERMAID_HOST успешно разрешилось и вспомогательный контейнер ответил. Если первый тест проходит, а второй — нет, проблема кроется во взаимодействии между контейнерами, поэтому изучите docker compose logs kroki, прежде чем проверять синтаксис диаграммы.
Форма GET кодирует диаграмму прямо в URL, что позволяет вики встраивать изображения без использования плагинов. Документация предоставляет соответствующий кодировщик.
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'))"В Ubuntu эта команда выведет python: command not found, так как в системе поставляется python3, а не версия без суффикса python. Используйте python3. Полученный результат добавляется в конец URL вида /{diagram-type}/{output-format}/{encoded-diagram}, и на него может ссылаться любой тег <img>. Существует ограничение: KROKI_MAX_URI_LENGTH по умолчанию составляет 4096 байт, поэтому длинные диаграммы необходимо отправлять методом POST.
Kroki считывает отправленный вами текст, поэтому настройки безопасности имеют решающее значение. KROKI_SAFE_MODE по умолчанию имеет значение SECURE — это самый строгий из трех уровней, а KROKI_PLANTUML_ALLOW_INCLUDE по умолчанию имеет значение false. Эти значения установлены по умолчанию, так как директива !include в PlantUML считывает файлы и URL с точки зрения модуля отрисовки. Если ослабить эти настройки на общедоступном эндпоинте, вы предоставите любому пользователю интернета возможность считывать файлы изнутри вашего контейнера. Не меняйте их без необходимости; если вам нужно указать путь для включения файлов, используйте KROKI_PLANTUML_INCLUDE_PATH.
Память: что сильнее нагружает VPS малого размера
Порядок потребления ресурсов предсказуем, если знать, что именно запускает каждый контейнер.
- Образ Excalidraw — это nginx, отдающий статические файлы. Это самый легковесный сервис из трех.
- draw.io работает на Tomcat, сервере приложений Java, поэтому он всегда использует JVM (Java virtual machine), независимо от того, рисует кто-то в данный момент или нет.
- Шлюз Kroki — это тоже Java-сервис, поставляемый в виде jar-файла для ручной установки.
- Компонент mermaid — самый ресурсоемкий. Его Dockerfile устанавливает Chromium и задает
PUPPETEER_EXECUTABLE_PATH=/usr/lib/chromium/chrome, так как Mermaid выполняет рендеринг в полноценном движке браузера.
Показатели в режиме ожидания дают мало информации. Важен скачок потребления во время рендеринга диаграммы. Параметр KROKI_MERMAID_MAX_CONCURRENCY по умолчанию равен 6, что позволяет выполнять до шести процессов рендеринга одновременно. Измерьте показатели на своем сервере, вместо того чтобы полагаться на опубликованные цифры.
docker stats --no-stream
docker system dfЗапустите первую команду, пока система простаивает, а затем повторите её во время циклического рендеринга большой диаграммы mermaid. Если скачок потребления критичен для вашего тарифного плана, лучше ограничить ресурсы, чем гадать: настройка лимитов памяти для сервиса в Compose описывает синтаксис и поведение контейнера при достижении лимита. Отказ от компонента mermaid также является допустимым решением, так как шлюз продолжит обслуживать все остальные встроенные средства рендеринга.
Ни один из этих инструментов не содержит модели пользователей, поэтому установите её перед ними
В draw.io нет учетных записей. В Excalidraw нет учетных записей. Kroki отвечает на любой запрос, который до него доходит. Любая авторизация должна выполняться на стороне прокси.
sudo apt update && sudo apt install -y apache2-utils
sudo htpasswd -c /etc/nginx/.htpasswd alicehtpasswd -c создает файл и перезаписывает существующий, поэтому используйте -c только при первом запуске.
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;
}
}Примените конфигурацию с помощью sudo nginx -t && sudo systemctl reload nginx. Часть nginx -t является критически важной: при перезагрузке с некорректной конфигурацией продолжает работать старая версия, поэтому сайт остается доступным, а ваши изменения не вступают в силу. В разборе конфигурации reverse proxy по строкам описаны блок заголовков и пути к сертификатам, которые не включены в этот фрагмент.
Базовая аутентификация — неподходящий инструмент для Kroki, и важно понимать почему. Страница вики встраивает изображение Kroki с помощью тега <img>. Браузер читателя запрашивает этот URL как подресурс и не отправляет ваши учетные данные на другой источник, поэтому запрос возвращает ошибку 401, и все диаграммы на странице отображаются как битые изображения. Вместо этого ограничьте доступ к Kroki из публичного интернета. Поместите его в ту же сеть Docker, что и контейнер с вики, и позвольте вики обращаться к нему по имени сервиса, не публикуя порты на хост-машине. Как сети Compose разрешают имена сервисов — это именно тот механизм, который обеспечивает работу такой схемы.
Диаграммы, размещенные рядом с self-hosted вики
Это основная причина, по которой пользователям требуется подобная функциональность. Странице вики нужно изображение, и никто не хочет использовать скриншот с чьего-либо ноутбука.
BookStack имеет встроенную поддержку для self-hosted редактора. URL для встраивания по умолчанию — https://embed.diagrams.net/?embed=1&proto=json&spin=1&configure=1, а одна строка в .env перенаправляет его на ваш контейнер.
DRAWIO=https://drawio.example.com/?embed=1&proto=json&spin=1&configure=1Скопируйте строку запроса в точности. В документации BookStack указано, что embed=1&proto=json&spin=1 «необходимы для работы интеграции с BookStack», так как они выбирают протокол обмена сообщениями JSON, который используется для взаимодействия между двумя страницами. На той же странице приведена ссылка на stealth=1 «если вы не хотите использовать другие внешние сервисы» — это параметр, который следует добавить, если целью self-hosting было именно прекращение исходящих вызовов. После настройки BookStack сохраняет рисунок в собственное хранилище изображений рядом со страницей, поэтому резервная копия вики, которую вы уже создаете, также будет содержать резервную копию диаграммы.
Если вы еще не выбрали саму вики, определитесь с этим в первую очередь. Выбор между BookStack, Wiki.js и Outline — это более раннее решение, так как именно вики определяет способ прикрепления диаграммы к странице и, следовательно, то, какой из этих инструментов вы будете подключать.
Режимы сбоев и сообщения, которые вы увидите
Редактор схем в BookStack открывается и бесконечно отображает индикатор загрузки. Индикатор spin=1 ожидает подтверждения связи, которое не приходит. Убедитесь, что embed=1&proto=json&spin=1 присутствует в вашем значении DRAWIO, и что в имени хоста нет опечаток.
Фрейм редактора остается пустым в wiki, работающей по HTTPS. Консоль браузера сообщает о смешанном контенте (mixed content), попытке загрузки http:// внутри https://. Браузер блокирует фрейм, и draw.io не запускается. Настройте отдачу редактора по HTTPS.
Kroki возвращает 413 Request Entity Too Large. Эта строка исходит от nginx, а не от Kroki. Значение по умолчанию client_max_body_size в nginx составляет 1 MB, а значение по умолчанию KROKI_MAX_BODY_SIZE в самом Kroki — 1mb, поэтому при обработке большого исходного кода PlantUML срабатывает тот лимит, который меньше. Увеличьте оба значения.
Mermaid не работает, в то время как graphviz функционирует нормально. Шлюз исправен, но связь с вспомогательным сервисом отсутствует. Проверьте, запущен ли сервис с помощью docker compose ps, затем убедитесь, что KROKI_MERMAID_HOST соответствует имени сервиса, так как по умолчанию используется 127.0.0.1, что внутри контейнера шлюза означает сам шлюз.
Совместная работа в Excalidraw не подключается. Если вы собрали фронтенд для собственного сервера комнат и разместили его за nginx, прокси должен обновлять соединение с помощью proxy_set_header Upgrade $http_upgrade; и proxy_set_header Connection "upgrade";. Без них рукопожатие websocket обрабатывается как обычный HTTP-запрос, и сессия не начинается.
Холст пуст после очистки данных браузера. Сцена хранилась в локальном хранилище (local storage) на этом устройстве, и серверной копии не существует. Решение заключается не в настройках, а в привычке: экспортируйте файл .excalidraw для всего, что важно сохранить.
FAQ
Гарантирует ли self-hosting draw.io конфиденциальность моих диаграмм?
Это оставляет код приложения на вашем сервере, что не тождественно конфиденциальности данных. draw.io отрисовывается в вашем браузере, поэтому контейнер вообще не хранит диаграммы. Конфиденциальность зависит от того, где вы сохраняете файл и какие исходящие вызовы остаются разрешёнными. Используйте ?offline=1 для отключения облачных хранилищ и помните, что запросы на экспорт отправляются на сервер экспорта, если вы не настроите DRAWIO_SELF_CONTAINED=1 и не запустите jgraph/export-server самостоятельно.
Почему совместная работа не работает в моём self-hosted Excalidraw?
Официальная страница образа гласит, что self-hosting «не поддерживает функции обмена или совместной работы». Для совместной работы в реальном времени требуется отдельный websocket-сервер excalidraw/excalidraw-room, а для ссылок на общий доступ — сервис хранения. Адреса обоих компонентов вшиваются в JavaScript-бандл во время сборки как переменные Vite, например VITE_APP_WS_SERVER_URL, поэтому установка переменной окружения в работающем контейнере не даст эффекта. Использование собственного сервера комнат означает сборку фронтенда из исходного кода с вашими значениями.
Как отрисовывать диаграммы Mermaid на собственном сервере?
Запустите Kroki вместе с сопутствующим контейнером mermaid и установите KROKI_MERMAID_HOST на имя этого сервиса. Затем отправьте текст диаграммы методом POST на /mermaid/svg и получите SVG из ответа, либо закодируйте диаграмму в URL для GET-запроса и укажите его в теге <img>. Сопутствующий контейнер управляет Chromium через Puppeteer, так как Mermaid требует браузерный движок, поэтому учитывайте потребление памяти: KROKI_MERMAID_MAX_CONCURRENCY по умолчанию настроен на шесть одновременных рендеров.
Нужен ли пароль перед этими инструментами?
Да, так как ни один из них не имеет системы учётных записей. draw.io и Excalidraw предоставляют полный редактор любому, кто узнает URL, а Kroki отрисовывает любой отправленный ему текст. Базовой аутентификации на reverse proxy достаточно для двух редакторов. Для Kroki оставьте сервис неопубликованным в Docker-сети, общей с вики, так как <img>-запрос из браузера читателя не передаст учётные данные на другой origin, и каждая встроенная диаграмма перестанет отображаться.