Бэкап и восстановление Excalidraw
Если вы поднимали self-hosted Excalidraw и теперь ищете, как его бэкапить, первая новость неприятная: стандартная связка «фронтенд + room-сервер» не хранит доски на диске вообще — там просто нечего архивировать в привычном смысле «продампил базу и спи спокойно». Вторая новость хорошая: это значит, что бэкап Excalidraw — не про резервное копирование какого-то хранилища, а про дисциплину экспорта и защиту инфраструктуры вокруг. Разберём, что реально стоит защищать в зависимости от того, какую версию Excalidraw вы развернули, и как не потерять важные схемы при переезде или падении сервера.
Содержание
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →Что на самом деле нужно бэкапить: архитектура и её последствия
У self-hosted Excalidraw есть два принципиально разных сценария, и от того, какой у вас, зависит вся стратегия бэкапа.
Сценарий 1 — типовая связка excalidraw + excalidraw-room из статьи про установку. Фронтенд — статический бандл, room-сервер — WebSocket-транспорт, который держит состояние активных досок только в памяти процесса, пока идёт совместная сессия. Как только последний участник закрывает вкладку (или контейнер перезапускается), это состояние исчезает безвозвратно — на диске сервера от него не остаётся ни файла, ни строки в базе. Постоянное хранение целиком лежит на стороне клиента: браузер каждого участника кладёт сцену в localStorage/IndexedDB, а «настоящим» сохранением считается явный экспорт .excalidraw / PNG / SVG через меню приложения.
Сценарий 2 — кастомный backend с хранилищем. В официальном репозитории Excalidraw есть внутренний backend с Postgres и S3-совместимым хранилищем, но в открытом виде готового Docker-образа под него не публикуется — доразвернуть такой backend можно только руками, из исходников, адаптировав под свою инфраструктуру. Если вы (или кто-то до вас) это сделали — у вас действительно есть база данных и объектное хранилище, и тогда бэкап выглядит как у любого сервиса с Postgres + S3.
Ниже — оба случая по порядку, чтобы вы не тратили время на бэкап того, чего у вас физически нет.
Сценарий без backend: бэкапим то, что реально существует
Если у вас типовая установка (сценарий 1), содержательного бэкапа «состояния приложения» не будет — но три вещи защитить всё же нужно.
1. Директория с экспортами команды. Если вы приучили команду сохранять .excalidraw-файлы не куда попало, а в общую папку на сервере (например, смонтированную через Samba/SFTP или синхронизируемую Syncthing/Nextcloud), это и есть ваши реальные данные. Бэкапьте её как обычную файловую директорию:
mkdir -p /backup/excalidraw-exports
tar -czf /backup/excalidraw-exports/exports_$(date +%F).tar.gz \
-C /srv/team-exports excalidraw
Если единой папки для экспортов нет и каждый хранит файлы у себя локально — это тоже нормальный режим работы для небольшой команды, но тогда бэкап на сервере в принципе не покрывает эти данные: их защита — ответственность каждого участника на своей машине.
2. Конфигурация развёртывания. docker-compose.yml, .env с VITE_APP_WS_SERVER_URL, а если фронтенд собирался из исходников — файл .env.production с зашитыми в сборку адресами. Потерять это не критично (переразвернуть с нуля — 10 минут по инструкции), но восстановить с бэкапом быстрее, чем гуглить заново нюансы с runtime-подменой WS-адреса в готовом образе.
tar -czf /backup/excalidraw-exports/config_$(date +%F).tar.gz \
-C ~/excalidraw docker-compose.yml
3. Nginx-конфиг и TLS-сертификаты. Reverse proxy с пробросом WebSocket на /socket — та часть, где чаще всего ошибаются при повторной настройке (забытые заголовки Upgrade/Connection). Сохраните конфиг из /etc/nginx/sites-available/ и, если сертификаты не от Let's Encrypt (тот перевыпустится сам после восстановления домена), — сами файлы сертификата.
tar -czf /backup/excalidraw-exports/nginx_$(date +%F).tar.gz \
/etc/nginx/sites-available/excalidraw \
/etc/letsencrypt/live/draw.example.com \
/etc/letsencrypt/archive/draw.example.com 2>/dev/null || true
Это весь объём бэкапа для типовой установки — счёт идёт на мегабайты, а не гигабайты, потому что основной массив данных (сами доски в процессе рисования) в принципе не живёт на сервере.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверСценарий с кастомным backend: Postgres и S3
Если у вас развёрнут собственный backend с хранением сцен (Postgres для метаданных + S3-совместимое хранилище для содержимого досок), бэкап становится полноценным — как у любого сервиса с базой данных.
Дамп Postgres:
docker exec excalidraw-postgres pg_dump -U excalidraw -Fc excalidraw \
> /backup/excalidraw-exports/db_$(date +%F).dump
Флаг -Fc (custom format) даёт сжатый дамп с возможностью выборочного восстановления таблиц через pg_restore --list — это удобнее плоского SQL-дампа, если база вырастет и понадобится восстановить не всё целиком.
Синхронизация S3-хранилища сцен (bucket с содержимым досок) через rclone — тот же принцип, что для любого объектного хранилища:
rclone sync s3-excalidraw:excalidraw-scenes /backup/excalidraw-exports/scenes \
--transfers 4 --checksum
Если ставили свой backend с нуля, вам пригодится общий подход к настройке rclone под S3-совместимое хранилище — он разобран в статье про установку rclone на VPS, а типовые грабли с правами доступа и таймаутами синхронизации — в статье про частые ошибки rclone.
Важный нюанс консистентности: если Postgres хранит ссылки на объекты в S3 (например, идентификаторы сцен), дамп базы и синхронизация S3 должны сниматься максимально близко по времени — иначе при восстановлении вы рискуете получить в базе ссылки на объекты, которых ещё нет в свежесинхронизированном бэкапе S3 (или наоборот, «осиротевшие» объекты без записи в базе, что менее страшно, но засоряет хранилище).
Автоматизация: скрипт и cron
Собираем всё в один скрипт — он адаптируется под ваш сценарий простым закомментированием лишнего блока:
#!/bin/bash
# /usr/local/bin/excalidraw-backup.sh
set -euo pipefail
DEST=/backup/excalidraw-exports
DATE=$(date +%F)
RETENTION_DAYS=14
mkdir -p "$DEST"
# 1. Экспорты команды (если есть общая папка)
tar -czf "$DEST/exports_$DATE.tar.gz" -C /srv/team-exports excalidraw
# 2. Конфигурация compose и nginx
tar -czf "$DEST/config_$DATE.tar.gz" \
-C ~/excalidraw docker-compose.yml \
--directory=/etc/nginx/sites-available excalidraw
# 3. Если развёрнут кастомный backend — раскомментируйте:
# docker exec excalidraw-postgres pg_dump -U excalidraw -Fc excalidraw \
# > "$DEST/db_$DATE.dump"
# rclone sync s3-excalidraw:excalidraw-scenes "$DEST/scenes" --transfers 4
find "$DEST" -name '*.tar.gz' -mtime +"$RETENTION_DAYS" -delete
find "$DEST" -name '*.dump' -mtime +"$RETENTION_DAYS" -delete
chmod +x /usr/local/bin/excalidraw-backup.sh
crontab -e
# бэкап Excalidraw каждую ночь в 03:20
20 3 * * * /usr/local/bin/excalidraw-backup.sh >> /var/log/excalidraw-backup.log 2>&1
Скрипт стоит совмещать с общим подходом к бэкапу Docker-томов, если под экспорты или под данные backend-а выделен именно named volume, а не bind-mount — разница в командах архивации и типичные ошибки разобраны в статье про бэкап Docker volume.
Офсайт-копии: не храните бэкап рядом с оригиналом
Архив на том же диске, что и сам сервер, не защищает от отказа диска, кражи сервера или блокировки аккаунта у провайдера. Отправляйте копии за пределы сервера — например, через rclone в S3-совместимое хранилище или через restic, если нужна дедупликация и версионирование снапшотов:
rclone sync /backup/excalidraw-exports remote-s3:excalidraw-backups \
--transfers 4 --checksum
Если объём бэкапов растёт (частые дампы Postgres при активном использовании backend-а), restic экономнее по месту за счёт дедупликации между снапшотами — общая логика настройки описана в статье про restic в Docker Compose. Для простого сценария без backend-а, где бэкап — это несколько мегабайт конфигов и экспортов раз в сутки, разница в объёме не критична, и rclone вполне достаточно.
Восстановление на новый сервер
Порядок восстановления зависит от того же сценария.
Без backend (типовая установка):
- Разверните новый сервер и поднимите Excalidraw заново по инструкции установки — либо распакуйте сохранённый
docker-compose.ymlи запуститеdocker compose up -dнапрямую. - Восстановите nginx-конфиг из архива, проверьте синтаксис (
nginx -t) и перезагрузите (systemctl reload nginx). - Если сертификаты не Let's Encrypt — распакуйте их в
/etc/letsencrypt/; если Let's Encrypt — просто перевыпустите черезcertbot --nginx -d draw.example.comпосле того, как домен указывает на новый IP. - Разложите архив экспортов обратно в общую папку — это единственные «данные», которые физически существовали.
- Проверьте, что WebSocket-адрес в конфиге фронтенда (
VITE_APP_WS_SERVER_URL) по-прежнему указывает на правильный домен — при переносе на новый IP сам домен не меняется, но если меняли и домен, пересоберите фронтенд с новым значением.
Активные, не сохранённые в файл доски на момент падения старого сервера восстановить нельзя в принципе — они существовали только в памяти room-сервера и в браузерах участников (если кто-то не закрыл вкладку, экспорт оттуда ещё возможен вручную).
С кастомным backend:
# восстановление Postgres
docker exec -i excalidraw-postgres pg_restore -U excalidraw -d excalidraw --clean \
< /backup/excalidraw-exports/db_2026-08-20.dump
# восстановление S3-хранилища сцен
rclone sync /backup/excalidraw-exports/scenes s3-excalidraw:excalidraw-scenes \
--transfers 4
Флаг --clean в pg_restore сначала удаляет существующие объекты перед восстановлением — используйте его только на чистой базе или там, где вы осознанно хотите перезаписать текущее состояние. После восстановления сверьте количество записей в таблице сцен с количеством объектов в S3-бакете — расхождение подскажет, была ли рассинхронизация между дампом базы и синхронизацией хранилища на момент бэкапа.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
Почему у Excalidraw нет «нормального» бэкапа базы данных, как у других сервисов?
Потому что в типовой self-hosted установке базы данных просто нет — room-сервер хранит состояние сессии только в оперативной памяти, а постоянное хранение отдано на откуп клиенту (браузер + ручной экспорт файлов). Это осознанное архитектурное решение проекта, а не недоработка.
Что случится с открытыми досками, если перезапустить контейнер excalidraw-room без экспорта?
Несохранённые изменения в активных комнатах пропадут — сервер не хранит их на диске. Уже экспортированные .excalidraw-файлы на дисках участников это не затронет, они не связаны с состоянием room-сервера.
Стоит ли ставить кастомный backend с Postgres и S3 только ради бэкапов?
Только если команда большая и вы регулярно теряете доски из-за забытого экспорта. Для небольшой команды дисциплина «важное — экспортировать в общую папку» проще в поддержке, чем разворачивание и обслуживание отдельного backend-а с базой и объектным хранилищем, который в открытом виде даже не публикуется готовым образом.
Как часто нужно снимать бэкап, если данных на сервере физически мало?
Хватает раз в сутки ночью — объём небольшой, а частота экспортов зависит не от расписания cron, а от того, как часто команда сохраняет доски вручную. Ежедневный бэкап конфигурации и nginx нужен скорее на случай аварии сервера, а не потери данных досок.
Можно ли автоматизировать сам экспорт досок, а не полагаться на ручное сохранение?
Из коробки — нет, у типового self-hosted Excalidraw нет API для программного экспорта содержимого активных комнат без клиента в браузере. Это ещё один аргумент в пользу простого правила для команды: закончили работу над важной схемой — сразу экспортируйте файл, не откладывая на «потом».
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →