Paperless-ngx в Docker Compose: готовый файл
Бумажные документы — счета, договоры, чеки, справки — либо лежат стопкой в шкафу, либо разбросаны по папкам на диске без единой системы поиска. Paperless-ngx решает это: сканируете или фотографируете документ, система сама распознаёт текст (OCR), определяет тип, добавляет теги и кладёт в архив, где всё это находится по любому слову за секунду. Ниже — рабочий docker-compose.yml, который поднимает Paperless-ngx со всеми зависимостями с нуля, без танцев с ручной установкой Python-пакетов и Tesseract.
Содержание
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →Что входит в стек и зачем
Paperless-ngx — не одно приложение, а связка из нескольких сервисов, и раздельная установка каждого руками — то, ради чего Docker Compose и придуман. В минимальном рабочем стеке четыре контейнера:
- webserver — сам Paperless-ngx: веб-интерфейс, API, очередь обработки документов.
- db — PostgreSQL, хранит метаданные, теги, историю документов.
- broker — Redis, очередь задач между веб-процессом и воркерами OCR.
- gotenberg (опционально, но рекомендую) — конвертация офисных форматов (docx, xlsx) в PDF перед архивацией.
Плюс отдельно можно поднять tika для извлечения текста из офисных документов без конвертации в PDF, но для большинства домашних архивов (сканы, PDF, фото чеков) достаточно связки без Tika — она не бесплатна по ресурсам и нужна не всем.
Если вы уже разворачивали Paperless-ngx через apt/pip и упирались в версии Tesseract или зависимости qpdf — Docker снимает эту проблему полностью: все версии зафиксированы в образе.
Требования к серверу
Paperless-ngx не тяжёлый в простое, но OCR — CPU-интенсивная задача, особенно на многостраничных сканах.
| Параметр | Минимум | Комфортно |
|---|---|---|
| CPU | 2 vCPU | 4 vCPU |
| RAM | 2 ГБ | 4 ГБ |
| Диск | 20 ГБ | 50+ ГБ (зависит от объёма архива) |
| ОС | Ubuntu 22.04/24.04 | Ubuntu 24.04 LTS |
Диск — самая частая недооценка: если вы архивируете документы с сохранением оригиналов плюс OCR-слой плюс миниатюры, объём растёт быстрее, чем кажется на старте. Закладывайте запас или выносите том /consume и медиатеку на отдельный диск с возможностью расширения — на VPS это обычно вопрос пары кликов в панели, без переустановки системы.
Если ставите с нуля, статья про установку Paperless-ngx на VPS разбирает вариант без Docker — здесь же быстрый путь через compose.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверГотовый docker-compose.yml
Создайте директорию проекта и файл .env рядом с compose-файлом — так секреты не попадают в сам YAML и не улетают в git по ошибке.
mkdir -p /opt/paperless && cd /opt/paperless
mkdir -p data media export consume
Файл .env:
# PostgreSQL
POSTGRES_DB=paperless
POSTGRES_USER=paperless
POSTGRES_PASSWORD=замените_на_длинный_случайный_пароль
# Paperless
PAPERLESS_SECRET_KEY=замените_на_случайную_строку_50_симв
PAPERLESS_TIME_ZONE=Europe/Moscow
PAPERLESS_OCR_LANGUAGE=rus+eng
PAPERLESS_URL=https://docs.example.com
Секреты сгенерируйте сразу, не оставляйте плейсхолдеры:
openssl rand -base64 32 # для POSTGRES_PASSWORD
openssl rand -base64 50 # для PAPERLESS_SECRET_KEY
Сам docker-compose.yml:
services:
broker:
image: docker.io/library/redis:7
restart: unless-stopped
volumes:
- redisdata:/data
db:
image: docker.io/library/postgres:16
restart: unless-stopped
volumes:
- pgdata:/var/lib/postgresql/data
environment:
POSTGRES_DB: ${POSTGRES_DB}
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
gotenberg:
image: docker.io/gotenberg/gotenberg:8
restart: unless-stopped
command:
- "gotenberg"
- "--chromium-disable-javascript=true"
- "--chromium-allow-list=file:///tmp/.*"
webserver:
image: ghcr.io/paperless-ngx/paperless-ngx:latest
restart: unless-stopped
depends_on:
- db
- broker
- gotenberg
ports:
- "127.0.0.1:8010:8000"
volumes:
- ./data:/usr/src/paperless/data
- ./media:/usr/src/paperless/media
- ./export:/usr/src/paperless/export
- ./consume:/usr/src/paperless/consume
environment:
PAPERLESS_REDIS: redis://broker:6379
PAPERLESS_DBHOST: db
PAPERLESS_DBNAME: ${POSTGRES_DB}
PAPERLESS_DBUSER: ${POSTGRES_USER}
PAPERLESS_DBPASS: ${POSTGRES_PASSWORD}
PAPERLESS_SECRET_KEY: ${PAPERLESS_SECRET_KEY}
PAPERLESS_TIME_ZONE: ${PAPERLESS_TIME_ZONE}
PAPERLESS_OCR_LANGUAGE: ${PAPERLESS_OCR_LANGUAGE}
PAPERLESS_URL: ${PAPERLESS_URL}
PAPERLESS_TIKA_ENABLED: "false"
PAPERLESS_TASK_WORKERS: "2"
PAPERLESS_THREADS_PER_WORKER: "1"
volumes:
pgdata:
redisdata:
Обратите внимание: порт веб-интерфейса привязан к 127.0.0.1:8010 — наружу он не торчит. Это сознательное решение: перед Paperless должен стоять reverse proxy с HTTPS, а не голый контейнер на публичном порту.
Первый запуск и создание администратора
docker compose up -d
docker compose logs -f webserver
Дождитесь строк о применении миграций (Applying ... OK) и старта gunicorn. Затем создайте суперпользователя:
docker compose exec webserver python manage.py createsuperuser
Введите логин, email и пароль — и заходите на http://127.0.0.1:8010 (пока локально, через SSH-туннель, если проверяете до настройки прокси) либо сразу через домен, если reverse proxy уже настроен.
Проверить, что OCR-воркер действительно работает, проще всего практикой: положите тестовый PDF в папку consume/ — Paperless должен подхватить его автоматически в течение минуты и разложить по архиву.
cp test-scan.pdf /opt/paperless/consume/
docker compose logs -f webserver | grep -i consume
Reverse proxy и HTTPS
Открывать Paperless напрямую в интернет без TLS — плохая идея: в архиве обычно лежат паспортные данные, договоры, финансовые документы. Проще всего дать HTTPS через Caddy — он сам получает сертификат Let's Encrypt без ручной возни с certbot.
Отдельный Caddyfile рядом с проектом (или в общем прокси-стеке, если он у вас уже есть):
docs.example.com {
reverse_proxy 127.0.0.1:8010
}
Если у вас уже есть общий reverse proxy на сервере (например, для нескольких сервисов), просто добавьте туда ещё один блок — Paperless в этом плане обычный HTTP-бэкенд, никаких websocket-нюансов, требующих отдельной настройки, у него нет. О выборе между Caddy и Nginx для такого сценария есть отдельный разбор в статье Caddy или Nginx: что выбрать для сервера.
Настройка OCR и языка распознавания
PAPERLESS_OCR_LANGUAGE=rus+eng в .env включает распознавание сразу двух языков — это покрывает большинство смешанных архивов (русские счета и договоры + англоязычные инвойсы от зарубежных сервисов). Если документы только на одном языке, укажите один код — так OCR будет чуть быстрее, потому что Tesseract не перебирает лишний словарь.
Полезные параметры для тонкой настройки в том же .env:
# Пропускать OCR у страниц, где текстовый слой уже есть (обычный PDF, не скан)
PAPERLESS_OCR_SKIP_ARCHIVE_FILE=never
PAPERLESS_OCR_MODE=skip
# Автоматически поворачивать перекошенные сканы
PAPERLESS_OCR_ROTATE_PAGES=true
PAPERLESS_OCR_ROTATE_PAGES_THRESHOLD=12.0
# Ускорить обработку многостраничных документов, если хватает CPU
PAPERLESS_OCR_PAGES=0
После изменения .env контейнеры нужно пересоздать, а не просто перезапустить — переменные окружения читаются при старте контейнера:
docker compose up -d --force-recreate webserver
Ориентировочно: распознавание одной страницы обычного скана на 2 vCPU занимает от нескольких секунд до пары десятков — сильно зависит от качества скана, языка и загрузки процессора другими сервисами на том же сервере. Точных цифр без тестов на конкретном железе не дам — у вас может быть заметно иначе.
Бэкап архива документов
Архив документов — тот случай, когда бэкап не опция, а обязательная часть настройки: пропавшие сканы паспорта или договора восстановить неоткуда. У Paperless-ngx есть встроенный экспорт, который переносит документы в исходном виде вместе с метаданными:
docker compose exec webserver python manage.py document_exporter ../export
Это кладёт файлы в примонтированную папку export/ на хосте — оттуда их нужно забирать на отдельное хранилище, а не оставлять рядом с боевыми данными. Для регулярного автоматического бэкапа удобно завести cron-задачу на хосте:
# /etc/cron.d/paperless-export
0 3 * * * root docker compose -f /opt/paperless/docker-compose.yml exec -T webserver python manage.py document_exporter ../export
А сам каталог export/ синхронизировать на внешнее S3-совместимое хранилище — например, через MinIO, если держите его отдельным контейнером (см. MinIO в Docker Compose), или через restic для версионированных инкрементальных бэкапов с шифрованием (готовый compose-файл — в статье Restic в Docker Compose). Правило "3-2-1" тут не абстракция: минимум одна копия архива должна физически жить не на том же сервере, что и оригинал.
Отдельно не забудьте про volume pgdata — там метаданные, теги, связи документов. document_exporter уже включает их в экспорт, но если делаете дамп базы вручную:
docker compose exec db pg_dump -U paperless paperless > paperless_db_$(date +%F).sql
Обновление и обслуживание
Paperless-ngx развивается активно, обновления выходят регулярно. Обновление через Compose — замена тега образа и пересоздание контейнера:
docker compose pull webserver
docker compose up -d webserver
Перед крупным обновлением (смена major-версии) стоит свериться с release notes проекта — иногда меняется формат хранения архивных копий или требуется разовая миграция. Автоматизировать рутинные патч-обновления можно через Watchtower, но для сервиса с чувствительными документами я бы держал ручной контроль над обновлениями, а не полный автопилот — почитайте про частые грабли автообновлений в статье Watchtower на сервере: частые ошибки и решения, прежде чем включать его на архиве документов.
Логи стоит поглядывать периодически, особенно после обновлений:
docker compose logs --tail=100 webserver
docker compose ps
Если контейнер webserver уходит в restart-loop — почти всегда причина в несовпадении версии схемы базы данных с образом (пропущенное обновление) или в неверном PAPERLESS_SECRET_KEY, изменённом после первого запуска — это ключ шифрования сессий, менять его на живой системе нельзя.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
Нужен ли Tika для нормальной работы?
Нет, если вы архивируете в основном PDF и сканы. Tika нужен, чтобы Paperless извлекал текст из офисных форматов (docx, xlsx) без конвертации через Gotenberg — для большинства архивов это необязательная надстройка, а не базовая потребность.
Можно ли распознавать документы на нескольких языках одновременно?
Да, PAPERLESS_OCR_LANGUAGE=rus+eng включает оба словаря Tesseract сразу. Можно добавить и больше языков через +, но каждый добавленный язык немного замедляет распознавание.
Что будет, если закончится место на диске во время OCR?
Обработка документа упадёт с ошибкой в логах, файл останется в очереди consume необработанным. Мониторьте свободное место заранее — Paperless не удаляет исходники автоматически при нехватке диска, но и не защищает от переполнения тома.
Как перенести архив на другой сервер?
Через document_exporter на старом сервере и document_importer на новом — это официальный и надёжный способ, сохраняющий все теги, типы документов и историю, в отличие от прямого копирования volume между разными версиями Paperless.
Безопасно ли открывать Paperless напрямую в интернет по HTTP?
Нет. В архиве обычно лежат персональные и финансовые данные — обязательно ставьте reverse proxy с HTTPS и рассмотрите дополнительную защиту (Basic Auth перед Paperless или VPN-доступ), особенно если сканируете паспортные данные.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →