Documenso в Docker Compose: готовый файл
Когда договор нужно подписать за пять минут, а не гонять курьера с бумагами, компании ставят DocuSign или его аналоги. Проблема в том, что зарубежные сервисы электронной подписи всё чаще недоступны из России или требуют корпоративную карту, которой у вас может не быть. Documenso решает задачу иначе: это открытый проект на GitHub, который вы разворачиваете на своём сервере и полностью контролируете — данные, домен, сертификаты, лимиты. Ниже — рабочий docker-compose.yml с PostgreSQL и S3-хранилищем, и разбор мест, где новички обычно спотыкаются.
Содержание
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →Что такое Documenso и зачем он вам
Documenso — это self-hosted платформа для электронной подписи документов: загружаете PDF, расставляете поля подписи, даты, инициалов, отправляете ссылку получателю — он подписывает прямо в браузере, без регистрации. Documenso хранит историю операций для каждого документа — кто, когда и с какого IP поставил подпись.
Для кого это имеет смысл: юридические и бухгалтерские фирмы, которым важна независимость от иностранных сервисов и контроль над тем, где физически лежат документы; продукты, которым подпись нужна как встроенная функция через API; компании с требованиями хранить данные внутри своей инфраструктуры — банки, госструктуры, здравоохранение.
Важная оговорка: юридическая сила электронной подписи регулируется законодательством вашей страны (в России — 63-ФЗ). Documenso даёт техническую инфраструктуру для подписания и аудита, но не заменяет собой квалифицированную электронную подпись там, где она требуется по закону. Для внутреннего документооборота, договоров с зарубежными партнёрами и NDA этого обычно достаточно; для сделок, где по закону нужна КЭП, уточняйте требования у юриста.
Требования к серверу
Documenso сама по себе лёгкая — Next.js приложение плюс PostgreSQL, но рендеринг PDF и генерация превью страниц кратковременно нагружают CPU при загрузке больших документов.
| Ресурс | Минимум | Комфортно |
|---|---|---|
| CPU | 2 vCPU | 4 vCPU |
| RAM | 2 ГБ | 4 ГБ |
| Диск | 20 ГБ SSD | 40+ ГБ SSD (растёт с объёмом документов) |
| ОС | Ubuntu 24.04 LTS | Ubuntu 24.04 LTS |
Для продакшена лучше сразу закладывать 4 ГБ ОЗУ — при одновременной обработке нескольких PDF (особенно многостраничных, со сканами) память уходит быстро, а OOM-killer в Docker убивает контейнер без внятного сообщения в логах. Если у вас уже есть Docker Compose на сервере, сверьтесь с чек-листом в статье про частые ошибки в Docker Compose на проде — там разобраны похожие сценарии с нехваткой памяти.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверГотовый docker-compose.yml
Documenso официально не публикует единый docker-compose файл для self-hosting — команда рекомендует деплой через их облако или Docker с ручной сборкой окружения. Ниже — конфигурация на основе переменных, которые понимает образ documenso/documenso из Docker Hub, и которая реально поднимается на чистом VPS.
Создайте директорию проекта и файл .env:
mkdir -p /opt/documenso && cd /opt/documenso
Файл .env:
# База данных
POSTGRES_USER=documenso
POSTGRES_PASSWORD=замените_на_длинный_случайный_пароль
POSTGRES_DB=documenso
# Домен
NEXT_PUBLIC_WEBAPP_URL=https://sign.example.com
# Ключи шифрования (сгенерировать: openssl rand -hex 32)
NEXTAUTH_SECRET=замените_на_свой_секрет
ENCRYPTION_KEY=замените_на_свой_ключ
ENCRYPTION_SECONDARY_KEY=замените_на_второй_ключ
# S3-совместимое хранилище (можно взять MinIO на этом же сервере)
NEXT_PRIVATE_UPLOAD_TRANSPORT=s3
NEXT_PRIVATE_UPLOAD_ENDPOINT=http://minio:9000
NEXT_PRIVATE_UPLOAD_BUCKET=documenso
NEXT_PRIVATE_UPLOAD_REGION=us-east-1
NEXT_PRIVATE_UPLOAD_ACCESS_KEY_ID=documenso_access
NEXT_PRIVATE_UPLOAD_SECRET_ACCESS_KEY=замените_на_свой_секрет
# SMTP для отправки писем с приглашением подписать
NEXT_PRIVATE_SMTP_HOST=smtp.example.com
NEXT_PRIVATE_SMTP_PORT=587
NEXT_PRIVATE_SMTP_USERNAME=noreply@example.com
NEXT_PRIVATE_SMTP_PASSWORD=пароль_от_почты
NEXT_PRIVATE_SMTP_FROM_ADDRESS=noreply@example.com
NEXT_PRIVATE_SMTP_FROM_NAME="Documenso"
Файл docker-compose.yml:
services:
postgres:
image: postgres:16-alpine
restart: unless-stopped
environment:
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"]
interval: 10s
timeout: 5s
retries: 5
minio:
image: minio/minio:latest
restart: unless-stopped
command: server /data --console-address ":9001"
environment:
MINIO_ROOT_USER: ${NEXT_PRIVATE_UPLOAD_ACCESS_KEY_ID}
MINIO_ROOT_PASSWORD: ${NEXT_PRIVATE_UPLOAD_SECRET_ACCESS_KEY}
volumes:
- miniodata:/data
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
interval: 10s
timeout: 5s
retries: 5
documenso:
image: documenso/documenso:latest
restart: unless-stopped
depends_on:
postgres:
condition: service_healthy
minio:
condition: service_healthy
environment:
NEXTAUTH_URL: ${NEXT_PUBLIC_WEBAPP_URL}
NEXTAUTH_SECRET: ${NEXTAUTH_SECRET}
NEXT_PUBLIC_WEBAPP_URL: ${NEXT_PUBLIC_WEBAPP_URL}
ENCRYPTION_KEY: ${ENCRYPTION_KEY}
ENCRYPTION_SECONDARY_KEY: ${ENCRYPTION_SECONDARY_KEY}
DATABASE_URL: postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB}
NEXT_PRIVATE_UPLOAD_TRANSPORT: ${NEXT_PRIVATE_UPLOAD_TRANSPORT}
NEXT_PRIVATE_UPLOAD_ENDPOINT: ${NEXT_PRIVATE_UPLOAD_ENDPOINT}
NEXT_PRIVATE_UPLOAD_BUCKET: ${NEXT_PRIVATE_UPLOAD_BUCKET}
NEXT_PRIVATE_UPLOAD_REGION: ${NEXT_PRIVATE_UPLOAD_REGION}
NEXT_PRIVATE_UPLOAD_ACCESS_KEY_ID: ${NEXT_PRIVATE_UPLOAD_ACCESS_KEY_ID}
NEXT_PRIVATE_UPLOAD_SECRET_ACCESS_KEY: ${NEXT_PRIVATE_UPLOAD_SECRET_ACCESS_KEY}
NEXT_PRIVATE_SMTP_HOST: ${NEXT_PRIVATE_SMTP_HOST}
NEXT_PRIVATE_SMTP_PORT: ${NEXT_PRIVATE_SMTP_PORT}
NEXT_PRIVATE_SMTP_USERNAME: ${NEXT_PRIVATE_SMTP_USERNAME}
NEXT_PRIVATE_SMTP_PASSWORD: ${NEXT_PRIVATE_SMTP_PASSWORD}
NEXT_PRIVATE_SMTP_FROM_ADDRESS: ${NEXT_PRIVATE_SMTP_FROM_ADDRESS}
NEXT_PRIVATE_SMTP_FROM_NAME: ${NEXT_PRIVATE_SMTP_FROM_NAME}
ports:
- "127.0.0.1:3000:3000"
volumes:
- documenso_cert:/app/cert
volumes:
pgdata:
miniodata:
documenso_cert:
Порт приложения намеренно привязан к 127.0.0.1 — наружу его отдаёт reverse proxy с TLS, о нём ниже. Если вы уже разворачивали MinIO отдельно, можно вынести его в отдельный стек — подробности в статье про MinIO в Docker Compose.
Первый запуск и создание бакета
Перед стартом сгенерируйте секреты вместо заглушек в .env:
openssl rand -hex 32 # для NEXTAUTH_SECRET
openssl rand -hex 32 # для ENCRYPTION_KEY
openssl rand -hex 32 # для ENCRYPTION_SECONDARY_KEY
Запускаем стек:
docker compose up -d
docker compose logs -f documenso
MinIO не создаёт бакет автоматически — сделайте это через консоль (http://ваш-сервер:9001, туннелируйте порт через SSH, если не хотите открывать его наружу) или через клиент mc (имя сети смотрите в docker network ls — обычно это имя директории проекта плюс _default):
docker run --rm --network documenso_default \
minio/mc alias set local http://minio:9000 documenso_access ваш_секрет
docker run --rm --network documenso_default \
minio/mc mb local/documenso
После того как приложение поднялось, откройте https://sign.example.com, зарегистрируйте первого пользователя — он автоматически станет владельцем инстанса — и в настройках организации подключите свой SMTP, если не сделали это через .env: без него получатели не получат ссылку на подписание.
Reverse proxy и TLS
Сертификат для подписи PDF — не то же самое, что TLS-сертификат сайта, их легко перепутать. TLS нужен, чтобы браузер не ругался на https://sign.example.com, и проще всего выпустить его через Caddy — он делает это автоматически по ACME без ручной возни с certbot:
sign.example.com {
reverse_proxy 127.0.0.1:3000
}
Если Caddy на сервере ещё не настроен, разверните его по инструкции Caddy с авто-SSL на VPS — это займёт минут пятнадцать вместе с DNS-записью.
Отдельно про подпись PDF: по умолчанию Documenso подписывает документы самоподписанным сертификатом, который генерирует при первом запуске и кладёт в volume documenso_cert. Для внутреннего оборота этого достаточно, но получатель увидит в Adobe Reader пометку «подпись не проверена доверенным центром». Чтобы избавиться от неё, нужен сертификат от доверенного CA — процедура его получения зависит от юрисдикции и выходит за рамки этой статьи.
Почта: без неё сервис бесполезен
Documenso отправляет письма на каждом шаге: приглашение подписать, напоминание, уведомление о завершении. Без рабочего SMTP получатели просто не узнают, что документ ждёт подписи, а вы будете думать, что всё работает, потому что в интерфейсе документ висит в статусе «отправлено».
Два рабочих варианта:
- Свой Postfix на том же сервере — контроль над всем, но придётся заниматься репутацией IP, SPF/DKIM/DMARC записями и следить, чтобы письма не улетали в спам. Разворачивается по инструкции почтовый сервер Postfix на VPS.
- Внешний транзакционный SMTP (Mailgun, SendGrid, Postmark, Yandex 360 для бизнеса) — проще для старта, доставляемость обычно выше «из коробки», но требует внешнего аккаунта и оплаты сверх аренды сервера.
Для проверки, что письма реально уходят, отправьте тестовый документ самому себе на другой почтовый ящик и посмотрите заголовки — если письмо пришло из папки «Входящие», а не «Спам», конфигурация в порядке.
Бэкапы
Теряете базу PostgreSQL — теряете все документы и историю аудита, даже если файлы PDF физически целы в MinIO: без записей в базе Documenso не свяжет их с пользователями. Бэкапить нужно оба хранилища синхронно, скриптом для cron:
#!/bin/bash
set -e
DATE=$(date +%Y-%m-%d_%H-%M)
BACKUP_DIR=/opt/backups/documenso
mkdir -p "$BACKUP_DIR"
docker compose -f /opt/documenso/docker-compose.yml exec -T postgres \
pg_dump -U documenso documenso | gzip > "$BACKUP_DIR/db_$DATE.sql.gz"
docker run --rm --network documenso_default -v "$BACKUP_DIR:/backup" \
minio/mc mirror local/documenso "/backup/minio_$DATE" \
--overwrite
find "$BACKUP_DIR" -mtime +14 -delete
Добавьте в crontab -e:
0 3 * * * /opt/documenso/backup.sh >> /var/log/documenso-backup.log 2>&1
Храните копии не только на самом сервере — синхронизируйте BACKUP_DIR на другой сервер или во внешнее объектное хранилище через rclone, иначе при смерти диска локальные бэкапы умрут вместе с ним.
Обновление и мониторинг места на диске
Обновление сводится к смене тега образа и пересборке:
docker compose pull documenso
docker compose up -d documenso
Перед обновлением на новую мажорную версию проверяйте changelog на GitHub — между релизами иногда меняется схема базы, и Documenso сама прогоняет миграции при старте, но откатить их вручную сложнее, чем накатить. Делайте бэкап базы непосредственно перед обновлением, не полагаясь на ночной cron.
Отдельно следите за диском: каждый PDF и его превью-страницы оседают в MinIO, а база растёт за счёт логов аудита. На активном сервере (сотни документов в месяц) закладывайте прирост в несколько гигабайт ежемесячно и настройте алерт на заполнение диска заранее, а не когда PostgreSQL откажется писать.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
Чем Documenso отличается от DocuSign по возможностям?
Базовый функционал — загрузка PDF, поля подписи/даты/текста, несколько подписантов по очереди или параллельно, история аудита — на месте. Продвинутых workflow и готовых интеграций с CRM, как у крупных SaaS, меньше, но для большинства сценариев подписания договоров этого достаточно.
Можно ли обойтись без MinIO и хранить файлы на диске сервера?
Образ Documenso ориентирован на S3-совместимое хранилище как основной сценарий для self-hosting — надёжнее сразу поднять MinIO рядом, тем более он занимает минимум ресурсов при небольшой нагрузке.
Нужен ли отдельный сервер под MinIO, если документов немного?
Нет, для старта достаточно поднять его в том же docker-compose файле на одном VPS. Выносить его отдельно имеет смысл, когда объём файлов начнёт конкурировать за диск с базой данных.
Подходит ли самоподписанный сертификат Documenso для юридически значимых документов?
Он подтверждает целостность документа и факт подписания в системе, но не имеет статуса квалифицированной электронной подписи по 63-ФЗ. Для внутренних процессов и договоров с обоюдным согласием сторон это рабочий вариант; там, где закон требует КЭП, нужен отдельный удостоверяющий центр.
Как перенести Documenso на другой сервер?
Перенесите volume с данными PostgreSQL (через pg_dump/pg_restore) и содержимое бакета MinIO, затем поднимите тот же docker-compose файл с теми же переменными окружения — ключи шифрования обязательно должны совпадать, иначе расшифровать уже сохранённые данные не получится.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →