Excalidraw в Docker Compose: готовый файл
Excalidraw — та самая доска для набросков «от руки»: архитектурные схемы, диаграммы, мокапы интерфейсов, объяснение идеи на созвоне. Публичная версия на excalidraw.com удобна, но всё, что вы рисуете, улетает на чужой сервер, а совместная сессия зависит от чужого аптайма. Если вы храните на досках рабочие схемы инфраструктуры или конфиденциальные диаграммы продукта — это повод развернуть Excalidraw у себя. Ниже — рабочий docker-compose.yml с постоянным хранилищем комнат, HTTPS через Traefik и парой нюансов, о которые реально спотыкаются при первом запуске.
Содержание
- Что разворачиваем и почему не просто фронтенд
- Минимальный вариант: только доска без коллаборации
- Полный стек с realtime-коллаборацией
- Своя сборка образа под свой WS-сервер
- Хранилище: почему шаринг по ссылке живёт не вечно
- Nginx как альтернатива Traefik
- Ограничение доступа: доска не должна быть открыта всему интернету
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →Что разворачиваем и почему не просто фронтенд
Открытый исходный код Excalidraw — это в первую очередь клиентское приложение: сама доска, рендеринг, экспорт в SVG/PNG работают полностью в браузере без сервера. Но у self-hosted версии есть два серверных компонента, без которых теряется весь смысл коллективной работы:
- excalidraw-room — WebSocket-сервер для realtime-совместной работы (тот самый механизм «Live collaboration», когда несколько человек рисуют на одной доске одновременно и видят курсоры друг друга);
- backend/storage — сервис хранения сцен для расшаривания по ссылке (когда вы жмёте «Share» и получаете постоянную ссылку на доску, а не временную сессию в localStorage).
Официальный образ фронтенда excalidraw/excalidraw разворачивает только статику — этого достаточно, чтобы рисовать локально в браузере, но недостаточно для совместной работы между разными людьми и устройствами. Поэтому в компоузе три сервиса: сама доска, комната для realtime и Traefik для HTTPS-терминации перед ними.
Без своего realtime-сервера кнопка «Start collaboration» в self-hosted сборке либо не работает, либо продолжает стучаться на публичный excalidraw.com — а это как раз то, чего вы хотели избежать, разворачивая свою копию.
Минимальный вариант: только доска без коллаборации
Если совместное редактирование не нужно и хочется просто личный инструмент для рисования схем с сохранением на диск браузера — хватит одного контейнера:
services:
excalidraw:
image: excalidraw/excalidraw:latest
container_name: excalidraw
restart: unless-stopped
ports:
- "8080:80"
Поднимаете, идёте на http://ip-сервера:8080 — и это уже рабочая доска. Данные при этом лежат в IndexedDB браузера конкретного пользователя, между устройствами не синхронизируются, ссылка «Share» не будет постоянной. Для одного человека, который иногда рисует схему и экспортирует её в PNG, этого достаточно и разворачивать что-то сложнее не имеет смысла.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверПолный стек с realtime-коллаборацией
Для команды, которая рисует вместе, нужен excalidraw-room — WebSocket-сервер обмена событиями между участниками одной комнаты:
services:
excalidraw:
image: excalidraw/excalidraw:latest
container_name: excalidraw
restart: unless-stopped
environment:
- VITE_APP_WS_SERVER_URL=https://room.example.com
- VITE_APP_BACKEND_V2_GET_URL=https://room.example.com/api/v2/scenes/
- VITE_APP_BACKEND_V2_POST_URL=https://room.example.com/api/v2/scenes/
depends_on:
- excalidraw-room
labels:
- "traefik.enable=true"
- "traefik.http.routers.excalidraw.rule=Host(`draw.example.com`)"
- "traefik.http.routers.excalidraw.entrypoints=websecure"
- "traefik.http.routers.excalidraw.tls.certresolver=letsencrypt"
- "traefik.http.services.excalidraw.loadbalancer.server.port=80"
excalidraw-room:
image: excalidraw/excalidraw-room:latest
container_name: excalidraw-room
restart: unless-stopped
labels:
- "traefik.enable=true"
- "traefik.http.routers.excalidraw-room.rule=Host(`room.example.com`)"
- "traefik.http.routers.excalidraw-room.entrypoints=websecure"
- "traefik.http.routers.excalidraw-room.tls.certresolver=letsencrypt"
- "traefik.http.services.excalidraw-room.loadbalancer.server.port=80"
networks:
default:
name: traefik-net
external: true
Важный момент: переменные VITE_APP_* вшиваются в статический билд фронтенда на этапе сборки образа, а не читаются рантаймом. Официальный образ собран с дефолтными переменными, указывающими на публичный excalidraw.com. Чтобы переопределить их по-настоящему, нужно либо форкнуть репозиторий и собрать образ самостоятельно с нужными VITE_APP_WS_SERVER_URL в .env перед сборкой, либо смириться с тем, что кнопка коллаборации в готовом образе поведёт на публичный сервис. Это не баг конфига — так устроена сборка Vite-приложений: import.meta.env резолвится компилятором в момент vite build, а не в браузере при загрузке страницы.
Своя сборка образа под свой WS-сервер
Рабочий путь для полностью автономной установки — собрать фронтенд из исходников с нужными переменными:
git clone https://github.com/excalidraw/excalidraw.git
cd excalidraw
cat > .env.production <<EOF
VITE_APP_WS_SERVER_URL=https://room.example.com
VITE_APP_BACKEND_V2_GET_URL=https://room.example.com/api/v2/scenes/
VITE_APP_BACKEND_V2_POST_URL=https://room.example.com/api/v2/scenes/
EOF
docker build -t excalidraw-self:latest .
Дальше в docker-compose.yml вместо image: excalidraw/excalidraw:latest указываете image: excalidraw-self:latest. Сборка на VPS с 2 vCPU / 4 ГБ RAM занимает несколько минут — Vite и yarn install — терпимо, но заложите время и место на диске под node_modules при первом билде.
Хранилище: почему шаринг по ссылке живёт не вечно
В официальной инфраструктуре excalidraw.com сцены, расшаренные по ссылке, автоматически удаляются через некоторое время — это политика публичного сервиса, а не ограничение самого протокола. У self-hosted excalidraw-room в базовой поставке хранение сцен реализовано в памяти процесса: без volume и без внешней БД контейнер держит расшаренные доски только пока не перезапущен. Для реальной эксплуатации нужен персистентный слой — либо доработанный форк с Redis/S3-бэкендом, либо прикладной компромисс: не полагаться на «Share Link» как на постоянное хранилище, а регулярно экспортировать важные доски в .excalidraw-файл (JSON-формат самого приложения, открывается назад тем же Excalidraw) и версионировать их в git.
Если храните файлы .excalidraw на сервере — не забудьте про бэкап директории с данными, здесь пригодится наш готовый рецепт BorgBackup в Docker Compose — инкрементальные снапшоты с шифрованием куда надёжнее, чем ручное копирование раз в квартал.
Nginx как альтернатива Traefik
Если Traefik в вашей инфраструктуре не прижился и вы предпочитаете классическую связку nginx + certbot, конфиг для фронтенда простой:
server {
listen 443 ssl http2;
server_name draw.example.com;
ssl_certificate /etc/letsencrypt/live/draw.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/draw.example.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
server {
listen 443 ssl http2;
server_name room.example.com;
ssl_certificate /etc/letsencrypt/live/room.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/room.example.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:8081;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
}
}
Для WS-сервиса критичны именно строки proxy_http_version 1.1 и заголовки Upgrade/Connection — без них апгрейд HTTP-соединения до WebSocket не пройдёт, и коллаборация просто не подключится, хотя сама страница откроется без ошибок. Если ставите nginx с нуля, у нас есть пошаговая инструкция установки Let's Encrypt SSL на VPS, а сам продакшен-стек Docker Compose разобран в статье Docker Compose для продакшена на Ubuntu 24.04.
Ограничение доступа: доска не должна быть открыта всему интернету
По умолчанию у self-hosted Excalidraw нет встроенной авторизации — кто угодно, зная URL, откроет доску и комнаты. Для внутреннего инструмента это обычно неприемлемо. Практичных варианта три:
| Вариант | Сложность | Что даёт |
|---|---|---|
| Basic Auth на Traefik/nginx | низкая | Логин-пароль на весь фронтенд, простая защита от случайных заходов |
| VPN / приватная сеть (WireGuard) | средняя | Доска вообще не торчит в публичный интернет |
| Отдельный SSO-прокси (Authelia/Authentik) | выше | Полноценная аутентификация с группами и логами доступа |
Для Traefik базовая авторизация добавляется middleware:
labels:
- "traefik.http.middlewares.excalidraw-auth.basicauth.users=user:$$apr1$$xyz...hash"
- "traefik.http.routers.excalidraw.middlewares=excalidraw-auth"
Хеш пароля генерируется через htpasswd -nb user 'пароль' (пакет apache2-utils), а доллары в compose-файле нужно экранировать удвоением — иначе Compose попытается подставить их как переменные окружения. Если нужна более серьёзная авторизация с ролями — разверните перед Excalidraw Authelia, у нас есть отдельный разбор для Authelia в Docker Compose, который закрывает этот сценарий полнее, чем голый Basic Auth.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
Почему коллаборация не работает, хотя сервис excalidraw-room запущен?
Скорее всего, фронтенд собран с дефолтными переменными окружения, указывающими на публичный excalidraw.com — VITE_APP_WS_SERVER_URL вшивается на этапе сборки, простой правкой compose-файла её не поменять, нужна пересборка образа из исходников со своим .env.production.
Сохраняются ли доски между перезапусками контейнера?
Комнаты, расшаренные по ссылке через штатный excalidraw-room, хранятся в памяти процесса и теряются при рестарте контейнера — для надёжного хранения экспортируйте важные схемы в .excalidraw-файлы и держите их вне контейнера, с бэкапом.
Можно ли использовать Excalidraw без сервера коллаборации вообще?
Да, полностью рабочий сценарий — один контейнер с образом excalidraw/excalidraw, доска работает в браузере, данные хранятся в localStorage/IndexedDB, но без синхронизации между устройствами и без совместной работы в реальном времени.
Сколько ресурсов нужно серверу под Excalidraw?
Сами контейнеры фронтенда и room-сервера лёгкие — конфигурации от 1 vCPU / 1-2 ГБ RAM хватает с запасом для команды в несколько человек; основной расход ресурсов у вас уйдёт на сборку образа при кастомной сборке, а не на рантайм.
Excalidraw поддерживает совместную работу с чужого excalidraw.com одновременно со своим сервером?
Нет, один инстанс фронтенда указывает ровно на один WS-сервер коллаборации, зашитый в билд, — переключаться между своим сервером и публичным на лету нельзя.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →