MAATRIX / Блог / Paperless-ngx на сервере: частые ошибки и решения

Paperless-ngx на сервере: частые ошибки и решения

MAATRIX

Paperless-ngx на бумаге — идеальное решение: скинул скан в папку, через минуту документ уже в поисковом архиве с распознанным текстом и тегами. На практике первая неделя эксплуатации обычно уходит на борьбу с зависшим OCR, файлами, которые не подхватываются, и редисом, который то и дело отваливается. Ниже — конкретные причины и решения для самых частых проблем, с которыми сталкиваются на VPS и выделенных серверах.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →

Базовый docker-compose и переменные окружения

Большинство проблем закладывается ещё на этапе конфигурации. Рабочий минимальный стек — три контейнера: webserver, redis (брокер для очереди задач) и postgres (основная БД, хотя можно и на sqlite, но для сервера это не вариант — блокировки при параллельной обработке).

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: paperless
      POSTGRES_USER: paperless
      POSTGRES_PASSWORD: change_me_strong

  webserver:
    image: ghcr.io/paperless-ngx/paperless-ngx:latest
    restart: unless-stopped
    depends_on:
      - db
      - broker
    ports:
      - "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_DBUSER: paperless
      PAPERLESS_DBPASS: change_me_strong
      PAPERLESS_SECRET_KEY: сгенерируйте_своё_значение
      PAPERLESS_URL: https://docs.example.com
      PAPERLESS_TIME_ZONE: Europe/Moscow
      PAPERLESS_OCR_LANGUAGE: rus+eng
      USERMAP_UID: 1000
      USERMAP_GID: 1000

volumes:
  pgdata:
  redisdata:
  data:
  media:

Три момента, на которых спотыкаются чаще всего:

  • PAPERLESS_SECRET_KEY пустой или дефолтный. Контейнер стартует, но сессии слетают при каждом рестарте, а иногда приложение просто не поднимает веб-интерфейс. Сгенерируйте случайную строку (openssl rand -base64 32) и зафиксируйте её — при смене ключа все активные сессии обнуляются.
  • USERMAP_UID/USERMAP_GID не совпадают с владельцем папок на хосте. Контейнер пишет в media и data от имени этого UID. Если папки на сервере созданы от root, а в переменной стоит 1000 — получаете PermissionError в логах воркера при попытке сохранить обработанный файл.
  • Postgres и Redis подняты позже webserver. depends_on в Compose не ждёт готовности сервиса, только его старта. Postgres может ещё не принимать соединения, когда веб-контейнер уже пытается накатить миграции — первый запуск иногда падает именно из-за этого. Решение — healthcheck на db и condition: service_healthy в depends_on, либо просто docker compose up во второй раз (это не поломка, а гонка при первом старте).

Подробно про типичные проблемы самого Postgres, если база у вас общая для нескольких сервисов, — в статье PostgreSQL на сервере: частые ошибки и решения.

Consume-папка не подхватывает файлы

Кладёте PDF в ./consume, а он так и лежит нетронутым. Три типичные причины по убыванию частоты:

  1. Права на файл. Если файл скопирован с правами, недоступными для чтения тому UID, от которого работает контейнер, Paperless его просто не увидит — в логах будет тихо, без явной ошибки. Проверьте: ls -la consume/ и сравните владельца с USERMAP_UID.
  2. inotify не срабатывает на сетевых хранилищах. Если consume смонтирован через NFS, SMB или это bind-mount из сетевой папки, файловые события inotify часто не долетают до контейнера. Симптом — файл лежит бесконечно, пока вы вручную не тронете содержимое папки. Решение — включить polling вместо inotify:
environment:
  PAPERLESS_CONSUMER_POLLING: 30

Это заставляет consumer каждые 30 секунд сканировать папку вручную, а не полагаться на события ядра. Расход CPU минимальный, зато надёжно на любой файловой системе.

  1. Файл ещё дописывается. Если скрипт сканера или облачный синк пишет файл долго (например, большой multi-page TIFF), Paperless может попытаться забрать его раньше, чем запись завершится, и получить битый документ. Используйте PAPERLESS_CONSUMER_RECURSIVE: true вместе с записью через временное имя и последующим mv — атомарное переименование гарантирует, что consumer увидит файл только целиком.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

OCR падает или пропускает документы

Самая частая жалоба — статус задачи "Failed" без внятной причины в интерфейсе. Загляните в логи именно воркера, не веб-контейнера:

docker compose logs -f webserver | grep -i ocrmypdf

Типичные сценарии:

  • Не хватает языкового пакета. PAPERLESS_OCR_LANGUAGE: rus+eng требует, чтобы оба языка Tesseract были установлены в образе. Официальный образ ghcr.io/paperless-ngx/paperless-ngx включает основные языки, но если вы используете кастомную сборку или урезанный образ — получите ошибку Failed loading language 'rus'. Проверка внутри контейнера:
docker compose exec webserver tesseract --list-langs
  • OCR уже был выполнен, но не устроил. Если PDF пришёл со встроенным (плохим) текстовым слоем, по умолчанию Paperless его не трогает (PAPERLESS_OCR_MODE: skip). Чтобы принудительно распознать заново поверх кривого слоя, поставьте PAPERLESS_OCR_MODE: redo (пересоздаёт текстовый слой) или force (полностью игнорирует существующий слой и OCR-ит с нуля, включая растеризацию).
  • Нехватка памяти на слабом VPS. OCR — не столько CPU-, сколько RAM-затратная операция, особенно на многостраничных сканах с высоким DPI. На тарифах 2 ГБ ОЗУ параллельная обработка 2-3 документов может уронить контейнер по OOM. Смотрите dmesg | grep -i oom — если видите там paperless или python3, снижайте параллелизм:
environment:
  PAPERLESS_OCR_MAX_WORKERS: 1
  PAPERLESS_TASK_WORKERS: 1

Ориентировочно: для комфортной обработки офисных сканов формата A4 достаточно 2 ГБ ОЗУ при одном воркере, но конкретная цифра сильно зависит от DPI и количества страниц в документе — у вас может отличаться в разы, проверяйте по факту через docker stats.

Redis и Postgres теряют соединение

redis.exceptions.ConnectionError в логах — почти всегда одно из двух: контейнер broker не запущен (проверьте docker compose ps), либо имя хоста в PAPERLESS_REDIS не совпадает с именем сервиса в Compose. Если вы переименовали сервис broker во что-то другое, не забудьте поправить переменную — Paperless резолвит хост через внутреннюю docker-сеть по имени сервиса, не по IP.

С Postgres чаще встречается FATAL: password authentication failed после смены пароля в .env — том с данными БД уже создан со старым паролем, и Postgres его не переинициализирует автоматически. Если меняли POSTGRES_PASSWORD уже после первого запуска, пароль нужно поменять и внутри самой БД:

docker compose exec db psql -U paperless -c "ALTER USER paperless WITH PASSWORD 'новый_пароль';"

После этого обновите PAPERLESS_DBPASS в переменных окружения webserver и перезапустите контейнер.

Реверс-прокси, HTTPS и ошибка CSRF

Если Paperless стоит за Nginx или Traefik и вы получаете 403 Forbidden — CSRF verification failed при попытке залогиниться, причина почти всегда одна: приложение не знает свой внешний адрес и не доверяет источнику запроса. Начиная с современных версий Paperless-ngx (Django 4+) нужно явно указать доверенные origin'ы:

environment:
  PAPERLESS_URL: https://docs.example.com

Этой переменной обычно достаточно — она автоматически формирует CSRF_TRUSTED_ORIGINS и ALLOWED_HOSTS. Плюс важно, чтобы прокси прокидывал заголовок протокола:

location / {
    proxy_pass http://127.0.0.1:8010;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
}

Без X-Forwarded-Proto приложение думает, что работает по HTTP, даже если снаружи HTTPS, и часть проверок безопасности (включая CSRF) начинает вести себя непредсказуемо. Если используете Traefik — там своя специфика с middleware и лейблами, разобрана в статье Traefik на сервере: частые ошибки и решения.

Диск заполняется быстрее, чем ожидалось

Архив документов растёт незаметно: каждый скан хранится минимум дважды — оригинал в media/documents/originals и обработанная версия с текстовым слоем в media/documents/archive, плюс миниатюры и полнотекстовый индекс. На объёме в несколько тысяч документов легко набегают десятки гигабайт.

Что реально экономит место:

  • Снизить DPI сканирования до 300 — для OCR этого достаточно, а 600 DPI раздувает архив почти вчетверо без ощутимого прироста качества распознавания.
  • PAPERLESS_OCR_PAGES, если нужно распознавать только первые N страниц длинных документов (например, для многостраничных приложений к договорам, где важна первая страница-обложка).
  • Проверять реальное потребление тома регулярно: docker system df -v покажет, сколько места съедает конкретный volume, а не контейнер в целом.

Если место на диске уже кончилось и контейнер не стартует — это отдельная и довольно частая история, разобрана в статье Docker занимает всё место на диске: причины и решение. На практике для домашнего архива на 5-10 тысяч документов с запасом хватает 40-60 ГБ диска, но если сканируете цветные развороты в высоком разрешении — закладывайте больше.

Бэкап и восстановление

Экспортёр документов — единственный способ бэкапа, который переживает смену версии Paperless (структура БД между релизами может меняться, а document_exporter сохраняет данные в переносимом формате с манифестом):

docker compose exec webserver document_exporter ../export

Команда выгружает все документы, метаданные и thumbnails в папку export, которая уже смонтирована как volume — то есть окажется на хосте. Восстановление — обратная команда:

docker compose exec webserver document_importer ../export

Для регулярного автобэкапа добавьте это в cron на хосте раз в сутки и синхронизируйте папку export на отдельное хранилище (S3-совместимое или другой сервер) — держать бэкап на том же диске, что и оригинал, бессмысленно при отказе диска. Общие практики автоматизации бэкапов Docker-томов, применимые и к media-папке Paperless, — в статье Как настроить бэкап Docker volume на VPS.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.

Частые вопросы

Paperless-ngx работает без Docker, напрямую на сервере?

Да, есть bare-metal установка через pip/venv, но она требует ручной настройки Tesseract, Redis, Postgres и systemd-юнитов для воркеров. Docker Compose закрывает всё это одной командой и остаётся рекомендуемым способом даже в официальной документации проекта.

Почему интерфейс открывается, но документы не ищутся по тексту?

Обычно причина в том, что полнотекстовый индекс не пересобран после массового импорта. Запустите docker compose exec webserver document_index reindex — команда пересоздаёт поисковый индекс с нуля.

Можно ли распознавать документы на нескольких языках одновременно?

Да, PAPERLESS_OCR_LANGUAGE: rus+eng+deu и так далее — Tesseract попробует все указанные языки и выберет лучший результат, но это заметно увеличивает время OCR на каждый документ.

Сколько ресурсов сервера нужно для 20-30 сканов в день?

Для такого потока хватает 2 vCPU и 2-4 ГБ ОЗУ на одном воркере — сама по себе задача не постоянная, а пиковая, нагрузка приходится только на момент обработки новой партии.

Что делать, если после обновления образа воркеры зависли в статусе "Started"?

Чаще всего это старая задача в очереди Redis, которая ссылается на несуществующий уже task ID. Помогает docker compose exec broker redis-cli FLUSHDB — очистит очередь задач (сами документы и БД это не затрагивает) — и перезапуск webserver.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →