CryptPad в Docker Compose: готовый файл
Команда просит совместный редактор документов, но содержимое — договоры, финмодели, черновики с чужими персональными данными — вы не готовы держать в Google Docs или доверять серверу, который технически может прочитать любой файл. OnlyOffice и Nextcloud в своём Docker-контейнере решают проблему зависимости от чужого облака, но сам сервер всё равно видит текст открытым текстом: у админа базы есть доступ к содержимому. CryptPad устроен иначе — шифрование происходит в браузере, сервер хранит и синхронизирует только нечитаемые блоки. Ниже — рабочий docker-compose.yml, который поднимает CryptPad за вечер, и нюансы с доменами и конфигом, которые в официальной документации разбросаны по десятку страниц.
Содержание
- Как устроено шифрование и чем это отличается от OnlyOffice
- Требования к серверу и доменам
- Готовый docker-compose.yml
- config.js: домены, админ-доступ и лимиты
- Reverse-proxy с Caddy: два домена, один контейнер
- Первый запуск и ограничение регистрации
- Бэкап, обновление и что «сервер не видит контент» значит для копий
- Частые ошибки
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →Как устроено шифрование и чем это отличается от OnlyOffice
CryptPad шифрует документ на стороне клиента ключом, который выводится из части URL (обычно после # — этот фрагмент браузер никогда не отправляет на сервер) или из пароля, если пад защищён паролем отдельно. На сервер уходит уже шифротекст: он хранится в файлах и рассылается другим участникам через WebSocket как зашифрованные дифы совместного редактирования (CryptPad использует свой CRDT-движок ChainPad). Сервер физически не может отдать содержимое документа регулятору, хостеру или взломщику базы — у него просто нет ключа.
Плата за это — сервер не может сделать то, для чего ему нужно видеть текст: полнотекстовый поиск по содержимому, превью документа в списке файлов, антивирусную проверку загрузок, серверный рендеринг в PDF без участия браузера. Если вам нужен корпоративный файловый сервер с поиском и предпросмотром — лучше подойдёт OnlyOffice в Docker Compose, где сервер обрабатывает документы напрямую. Если приоритет — чтобы содержимое конкретных чувствительных документов не мог прочитать никто, кроме тех, у кого есть ссылка, — CryptPad решает именно эту задачу.
Требования к серверу и доменам
Для команды из нескольких человек с несколькими десятками активных падов хватает скромной VPS: 2 vCPU, 2–4 ГБ RAM для самого приложения. Это ориентир, а не гарантия — реальное потребление зависит от числа одновременных редакторов и размера вложенных файлов, проверяйте по факту через docker stats. Диск считайте по объёму загружаемых файлов (блобов) — сам текст документов весит немного, а вот PDF и картинки внутри падов быстро съедают место.
Домены — отдельный пункт, важный именно для CryptPad. Разработчики рекомендуют два домена (или поддомена): основной — для интерфейса приложения, и «песочница» (sandbox) — для контента падов, который загружается в iframe. Разделение доменов — это защита от XSS: даже если кто-то вставит в документ вредоносный скрипт, он выполнится в изоляции на домене песочницы и не получит доступа к куки и сессии основного домена. Технически CryptPad запускается и на одном домене, но тогда эта изоляция пропадает — для рабочей установки заведите оба.
Понадобится DNS A-запись на оба домена и валидный SSL-сертификат для каждого. Если вы ещё не разворачивали Docker и reverse-proxy на чистом сервере — сначала пройдите базовую установку, она описана в статье про Docker Compose для продакшена.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверГотовый docker-compose.yml
services:
cryptpad:
image: cryptpad/cryptpad:latest
container_name: cryptpad
restart: unless-stopped
environment:
- CPAD_LOG_TO_STDOUT=true
volumes:
- ./cryptpad-data/data:/cryptpad/data
- ./cryptpad-data/datastore:/cryptpad/datastore
- ./cryptpad-data/block:/cryptpad/block
- ./cryptpad-data/blob:/cryptpad/blob
- ./cryptpad-data/customize:/cryptpad/customize.dist
- ./config.js:/cryptpad/config/config.js:ro
ports:
- "127.0.0.1:3000:3000"
networks:
- web
networks:
web:
external: true
Порт 3000 намеренно опубликован только на 127.0.0.1 — наружу CryptPad будет смотреть через reverse-proxy с TLS, сам контейнер по HTTP наружу торчать не должен. Сеть web — внешняя, та же, в которой у вас поднят Caddy или другой прокси; создайте её заранее командой docker network create web, если ещё не создана.
Перед первым запуском создайте директории и пустой config.js:
mkdir -p cryptpad-data/{data,datastore,block,blob,customize}
touch config.js
config.js: домены, админ-доступ и лимиты
CryptPad конфигурируется отдельным файлом, а не только переменными окружения — так проще держать под контролем все настройки в одном месте и класть файл под git (без секретов внутри, там их и нет). Минимальный рабочий config.js:
module.exports = {
httpUnsafeOrigin: 'https://pad.example.com',
httpSafeOrigin: 'https://pad-sandbox.example.com',
httpAddress: '0.0.0.0',
httpPort: 3000,
adminEmail: 'admin@example.com',
adminKeys: [
// сюда добавите публичный ключ после первой регистрации, см. ниже
],
maxUploadSize: 100 * 1024 * 1024, // 100 МБ, поднимите под свои файлы
logFeedback: false,
};
httpUnsafeOrigin — основной домен интерфейса, httpSafeOrigin — домен песочницы из предыдущего раздела. Оба указываются с протоколом https:// и должны точно совпадать с тем, что видит браузер — расхождение в схеме или домене (например, забыли www или перепутали регистр) даёт белый экран без внятной ошибки в интерфейсе, только в консоли браузера.
adminKeys — это не пароль, а публичный ключ подписи вашего аккаунта CryptPad. Пустой на старте, потому что аккаунта ещё нет — заполните его после первого запуска.
Reverse-proxy с Caddy: два домена, один контейнер
Оба домена ведут в один и тот же контейнер — CryptPad сам разбирает, какой домен из конфига к нему пришёл, и отдаёт соответствующий контент. Caddyfile:
pad.example.com {
reverse_proxy 127.0.0.1:3000
}
pad-sandbox.example.com {
reverse_proxy 127.0.0.1:3000
}
Caddy сам получает сертификаты через Let's Encrypt и правильно прокидывает заголовки Upgrade/Connection для WebSocket — отдельно ничего настраивать не нужно. Если вы ставите Caddy с нуля, пошагово это описано в статье про авто-SSL с Caddy. Если используете nginx — не забудьте явно прописать proxy_set_header Upgrade $http_upgrade; и proxy_set_header Connection "upgrade";, иначе совместное редактирование будет постоянно переподключаться.
Первый запуск и ограничение регистрации
docker compose up -d
- Откройте
https://pad.example.com, зарегистрируйте первый аккаунт — он станет вашим админским. - Зайдите в настройки аккаунта (Settings → General) и найдите «Public Signing Key» — это и есть значение для
adminKeys. - Впишите ключ в
config.js:
adminKeys: [
'ваш-длинный-публичный-ключ-в-base64',
],
- Перезапустите контейнер:
docker compose restart cryptpad. - Откройте
https://pad.example.com/admin/— появится админ-панель с настройками сервера, статистикой и, что важно, переключателем регистрации.
По умолчанию CryptPad открыт для саморегистрации кого угодно, кто знает адрес. Для рабочего сервера сразу после создания своего аккаунта отключите открытую регистрацию в админ-панели или ограничьте её инвайтами — иначе ваш диск постепенно займут чужие пады.
Бэкап, обновление и что «сервер не видит контент» значит для копий
Бэкапить нужно директории data, datastore, block, blob и файл config.js. Поскольку всё содержимое уже зашифровано на клиенте, эти копии можно хранить хоть в обычном облачном хранилище без дополнительного шифрования диска — но у этого есть обратная сторона: если пользователь потеряет ссылку на пад или сотрёт локальный ключ, восстановить содержимое не сможете ни вы, ни разработчики CryptPad. Функции «сбросить пароль и вернуть доступ к документу» здесь не существует в принципе — это прямое следствие модели с нулевым знанием (zero-knowledge), а не недоработка. Предупредите команду заранее и заведите привычку сохранять ссылки на важные пады в отдельном месте.
Технически бэкап томов ничем не отличается от любого другого Docker-сервиса — общие принципы и частые грабли разобраны в статье про бэкап Docker volume. Простой вариант — rsync директории cryptpad-data на другой сервер по расписанию, без остановки контейнера: CryptPad пишет файлы атомарно, горячий бэкап работает без проблем.
Обновление — стандартное для Compose:
docker compose pull
docker compose up -d
Перед обновлением на мажорную версию стоит посмотреть changelog образа — иногда меняется структура config.js, и старый файл может не подхватить новые обязательные поля.
Частые ошибки
- Белый экран без ошибок в интерфейсе. Почти всегда — расхождение
httpUnsafeOrigin/httpSafeOriginв конфиге с реальным адресом в браузере. Проверьте протокол (https://обязателен, даже если TLS терминирует прокси), домен и отсутствие завершающего слэша. - Постоянные «Reconnecting» во время совместного редактирования. Reverse-proxy не пробрасывает заголовки апгрейда до WebSocket. С Caddy 2 это работает из коробки, с nginx — нужно добавить заголовки
UpgradeиConnectionвручную. - Загрузка файла обрывается на «слишком большой». Лимит задаётся
maxUploadSizeвconfig.js— по умолчанию он невелик. Если перед CryptPad стоит ещё и nginx, проверьте у негоclient_max_body_size— оба лимита должны совпадать по порядку величины. - Один домен вместо двух — предупреждения в консоли о CSP. CryptPad запустится, но откажется от части защиты песочницы. Для теста на локальном сервере это допустимо, для рабочей установки — заведите второй домен.
- Рост потребления памяти за недели работы. Долгоживущий Node-процесс с множеством открытых падов постепенно накапливает память. Помогает
restart: unless-stoppedв сочетании с плановым перезапуском раз в 1–2 недели через cron, пока не разберётесь, какие именно пады нагружают процесс сильнее всего.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
Обязательно ли заводить второй домен-«песочницу»?
Технически нет, CryptPad стартует и на одном домене, но тогда теряется изоляция контента от интерфейса — часть защиты от XSS отключается. Для рабочего сервера заведите оба.
Что увидит хостер или админ сервера, если получит доступ к диску?
Файлы содержимого документов — только шифротекст, прочитать их без ключа, который живёт в URL пользователя, нельзя. Но структура (сколько падов, когда создавались, размеры блобов) видна — CryptPad шифрует контент, а не метаданные файловой системы.
Можно ли открывать и редактировать настоящие .docx и .xlsx с сохранением форматирования?
Импорт и экспорт в офисные форматы работают, но собственные редакторы CryptPad — не движок OnlyOffice, а свои CRDT-инструменты, и сложное форматирование Word/Excel при конвертации может немного отличаться от оригинала. Для документов, где форматирование критично, проверяйте результат перед отправкой заказчику.
Сколько человек выдержит один сервер на 2 vCPU / 4 ГБ?
Зависит от того, сколько падов редактируется одновременно, а не от числа зарегистрированных аккаунтов — команда из 20–30 человек с несколькими активными документами обычно укладывается, но это ориентир, а не гарантия: смотрите на реальную нагрузку через docker stats в первые недели.
Что будет, если забыть ссылку на важный пад?
Ничего хорошего — без ссылки (или без сохранённого ключа доступа) документ не восстановить. Это цена end-to-end шифрования: заведите в команде привычку сохранять ссылки на рабочие пады в отдельном менеджере паролей или списке.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →