Самостоятельный хостинг Paperless-ngx на VPS с Docker Compose

10 мин чтения·Matthieu·self-hostingdocker-composeocrdocument-managementpaperless-ngx|

Разверните Paperless-ngx на VPS с Docker Compose, PostgreSQL и Redis. Настройте языки и режимы OCR, правила автоматической разметки, получение документов по email и стратегию резервного копирования с удалённой синхронизацией.

Paperless-ngx превращает VPS в архив документов с полнотекстовым поиском. Сканируешь или загружаешь документы, Paperless-ngx прогоняет их через OCR, делает доступными для поиска и автоматически расставляет теги и корреспондентов. Работает как Docker Compose-стек с PostgreSQL, Redis и Gotenberg для конвертации документов.

Это руководство разворачивает Paperless-ngx на VPS, где уже установлены Docker и reverse proxy. Если этой базы ещё нет, начни с нашего руководства по настройке базового Docker-стека.

Какие ресурсы нужны Paperless-ngx на VPS?

Paperless-ngx требует минимум 2 ГБ RAM, рекомендуется 4 ГБ. Стек запускает PostgreSQL, Redis, Gotenberg, Tika и веб-сервер. В простое общее потребление памяти около 800 МБ. Во время OCR-обработки отсканированных PDF нагрузка CPU поднимается до 100% на одном ядре, а потребление RAM растёт до 1,5-2 ГБ. Дисковое пространство в среднем 5-10 МБ на документ (оригинал + архив + миниатюра).

Компонент RAM (простой) RAM макс. (OCR) Диск на 1000 документов
Paperless-ngx webserver ~300 МБ ~900 МБ 5-10 ГБ
PostgreSQL ~50 МБ ~100 МБ ~500 МБ
Redis ~10 МБ ~10 МБ пренебрежимо
Gotenberg ~150 МБ ~300 МБ
Tika ~250 МБ ~400 МБ
Итого ~760 МБ ~1 710 МБ 5-10 ГБ

Планируй дисковое пространство исходя из объёма документов. Домашнему пользователю, сканирующему 50 документов в месяц, нужно около 6 ГБ в год. Малому бизнесу с 500 документами/месяц стоит закладывать 50-60 ГБ в год.

Как развернуть Paperless-ngx с Docker Compose на VPS?

Создай директорию для стека, сгенерируй секреты и напиши Compose-файл. Для каждого сервиса настроен health check, чтобы Docker автоматически перезапускал проблемные контейнеры.

Создать директорию проекта

mkdir -p /opt/paperless-ngx && cd /opt/paperless-ngx

Сгенерировать секреты

Никогда не используй пароли по умолчанию. Сгенерируй надёжный пароль для базы данных и секретный ключ Django:

openssl rand -base64 32 > .db_password
openssl rand -base64 48 > .secret_key
chmod 600 .db_password .secret_key

Файлы остаются на диске с ограниченными правами. Compose-файл читает их при старте контейнеров.

Написать файл окружения

cat > .env << 'EOF'
COMPOSE_PROJECT_NAME=paperless
EOF

Написать Compose-файл

# docker-compose.yml
services:
  broker:
    image: docker.io/library/redis:8
    restart: unless-stopped
    volumes:
      - redisdata:/data
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 30s
      timeout: 5s
      retries: 3

  db:
    image: docker.io/library/postgres:18
    restart: unless-stopped
    volumes:
      - pgdata:/var/lib/postgresql/data
    environment:
      POSTGRES_DB: paperless
      POSTGRES_USER: paperless
      POSTGRES_PASSWORD_FILE: /run/secrets/db_password
    secrets:
      - db_password
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U paperless"]
      interval: 30s
      timeout: 5s
      retries: 3

  gotenberg:
    image: docker.io/gotenberg/gotenberg:8
    restart: unless-stopped
    command:
      - "gotenberg"
      - "--chromium-disable-javascript=true"
      - "--chromium-allow-list=file:///tmp/.*"
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
      interval: 30s
      timeout: 5s
      retries: 3

  tika:
    image: docker.io/apache/tika:latest
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:9998/"]
      interval: 30s
      timeout: 5s
      retries: 3

  webserver:
    image: ghcr.io/paperless-ngx/paperless-ngx:latest
    restart: unless-stopped
    depends_on:
      db:
        condition: service_healthy
      broker:
        condition: service_healthy
      gotenberg:
        condition: service_healthy
      tika:
        condition: service_healthy
    ports:
      - "127.0.0.1:8000: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_FILE: /run/secrets/db_password
      PAPERLESS_TIKA_ENABLED: 1
      PAPERLESS_TIKA_GOTENBERG_ENDPOINT: http://gotenberg:3000
      PAPERLESS_TIKA_ENDPOINT: http://tika:9998
      PAPERLESS_SECRET_KEY_FILE: /run/secrets/secret_key
      PAPERLESS_OCR_LANGUAGE: eng
      PAPERLESS_OCR_MODE: skip
      PAPERLESS_OCR_OUTPUT_TYPE: pdfa
      PAPERLESS_FILENAME_FORMAT: "{created_year}/{correspondent}/{title}"
      PAPERLESS_URL: https://paperless.example.com
      USERMAP_UID: 1000
      USERMAP_GID: 1000
    secrets:
      - db_password
      - secret_key
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8000"]
      interval: 30s
      timeout: 10s
      retries: 5
      start_period: 60s

secrets:
  db_password:
    file: .db_password
  secret_key:
    file: .secret_key

volumes:
  data:
  media:
  pgdata:
  redisdata:

Об этой конфигурации:

  • Порты привязаны только к 127.0.0.1. Reverse proxy обеспечивает публичный доступ по HTTPS. Если открыть порт 8000 на 0.0.0.0, это обойдёт TLS и файрвол.
  • Секреты используют Docker secrets (переменные с суффиксом _FILE). Пароли не появляются в выводе docker inspect или в списке процессов.
  • USERMAP_UID/USERMAP_GID маппят пользователя контейнера на UID 1000 на хосте. Файлы, создаваемые в директориях consume и export, принадлежат этому пользователю.
  • PAPERLESS_URL должен совпадать с публичным доменом. Paperless-ngx использует его для генерации ссылок и URL в письмах. Замени paperless.example.com на свой реальный домен.

Запустить стек

docker compose up -d

Подожди около 60 секунд, пока все сервисы инициализируются. Проверь, что каждый контейнер здоров:

docker compose ps
NAME                   SERVICE      STATUS                  PORTS
paperless-broker-1     broker       running (healthy)
paperless-db-1         db           running (healthy)
paperless-gotenberg-1  gotenberg    running (healthy)
paperless-tika-1       tika         running (healthy)
paperless-webserver-1  webserver    running (healthy)       127.0.0.1:8000->8000/tcp

Все пять контейнеров должны показывать (healthy). Если какой-то показывает (health: starting), подожди ещё 30 секунд. Если контейнер остаётся (unhealthy), проверь его логи:

docker compose logs gotenberg --tail 20

Создать суперпользователя

docker compose exec webserver createsuperuser

Следуй подсказкам для ввода имени пользователя, email и пароля. Это твой аккаунт администратора для веб-интерфейса.

Доступ к веб-интерфейсу

Если reverse proxy настроен, открой https://paperless.example.com в браузере. Загрузится страница входа Paperless-ngx. Если reverse proxy ещё не настроен, проверь локально:

curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:8000
200

Какие языки и режимы OCR настроить?

Paperless-ngx использует Tesseract для OCR. Язык и режим обработки настраиваются через переменные окружения. Значения по умолчанию подходят для документов на английском, но большинству пользователей нужно их изменить.

Язык OCR

Установи PAPERLESS_OCR_LANGUAGE в трёхбуквенный код языка Tesseract для твоих документов. Для нескольких языков соедини их через +:

# Один язык
PAPERLESS_OCR_LANGUAGE: rus

# Несколько языков
PAPERLESS_OCR_LANGUAGE: rus+eng+deu

Распространённые коды языков: eng (английский), deu (немецкий), fra (французский), spa (испанский), ita (итальянский), nld (нидерландский), por (португальский), rus (русский). Полный список в документации Tesseract.

Образ контейнера включает большинство языковых пакетов. Добавление языков в PAPERLESS_OCR_LANGUAGE увеличивает время OCR-обработки на страницу.

Режимы OCR

Параметр PAPERLESS_OCR_MODE определяет, как Paperless-ngx обрабатывает документы, которые уже содержат текстовый слой (типично для PDF, созданных цифровым способом).

Режим Поведение Когда использовать
skip OCR только для страниц без текста. Всегда создаёт архивную копию. По умолчанию. Подходит для смешанного ввода: сканы + цифровые PDF.
skip_noarchive Как skip, но без создания архива для документов с текстом. Нужно сэкономить место на цифровых PDF.
redo Повторный OCR всех страниц с заменой существующих текстовых слоёв. Получаешь документы с плохим OCR от сканеров.
force Растеризация документа и OCR с нуля. Уничтожает исходный текст. Крайний случай. Создаёт файлы большего размера с менее чётким текстом.

Для большинства установок skip — правильный выбор. Обрабатывает и сканы, и цифровые документы, не тратя CPU на повторную обработку уже индексированных документов.

Если получаешь документы от сканера с собственным (плохим) OCR, переключи режим на redo. Учти, что redo несовместим с PAPERLESS_OCR_CLEAN и PAPERLESS_OCR_DESKEW.

Тип вывода OCR

PAPERLESS_OCR_OUTPUT_TYPE: pdfa (по умолчанию) создаёт файлы PDF/A — архивный стандарт. Они самодостаточны и содержат встроенные шрифты. Оставь эту настройку, если нет веской причины менять.

После изменения любых настроек OCR перезапусти контейнер webserver:

docker compose restart webserver

Существующие документы не обрабатываются повторно. Чтобы повторно запустить OCR для документа, используй действие «Redo OCR» в веб-интерфейсе.

Как работает автоматическая разметка в Paperless-ngx?

Paperless-ngx поддерживает шесть алгоритмов сопоставления для тегов, корреспондентов и типов документов. Когда документ поступает, каждый тег сверяет содержимое со своим шаблоном. Если шаблон совпадает, тег применяется автоматически.

Алгоритм Как работает Когда использовать Пример
Any Совпадает, если любое слово из поля совпадения встречается. Широкие категории. invoice receipt bill
All Совпадает, если все слова встречаются (в любом порядке). Более точное сопоставление без привязки к позиции. electricity quarterly
Exact Точное совпадение фразы по порядку. Названия компаний, номера счетов. Acme Corp
Regular Expression Полноценное сопоставление по регулярному выражению. Структурированные данные: даты, номера, суммы. Invoice\s+#?\d{4,}
Fuzzy Приблизительное совпадение с настраиваемым порогом. Нестабильный OCR, мелкие опечатки. Stadtwerke (совпадает с Stadtwenke)
Auto ML-классификатор, обучающийся на твоих ручных назначениях. После ~50+ документов, размеченных вручную. (учится на твоих исправлениях)

Создание тегов с правилами сопоставления

В веб-интерфейсе перейди в Manage > Tags и создай тег. Установи алгоритм сопоставления и поле Match. Несколько практических примеров:

  • Тег: Invoice, Алгоритм: Any, Match: invoice rechnung facture счёт (ловит счета на разных языках)
  • Тег: Bank, Алгоритм: Regular Expression, Match: IBAN\s*[A-Z]{2}\d{2} (совпадает с любым номером IBAN)
  • Тег: Medical, Алгоритм: All, Match: patient diagnosis (требует оба слова)
  • Корреспондент: Electric Company, Алгоритм: Exact, Match: Springfield Energy Inc

Обучение Auto-классификатора

Алгоритм Auto использует ML-классификатор, который Paperless-ngx переобучает автоматически. Для обучения:

  1. Вручную разметь как минимум 50 документов по категориям
  2. Убедись, что эти документы не во входящих (классификатор игнорирует документы из входящих)
  3. Классификатор переобучается по расписанию (по умолчанию: каждый час через document_create_classifier)
  4. Новые документы начинают получать автоматические назначения

Можно запустить переобучение вручную:

docker compose exec webserver document_create_classifier

Классификатор улучшается с каждым исправлением. Каждое исправление попадает в следующий цикл обучения.

Как настроить приём документов в Paperless-ngx?

Paperless-ngx принимает документы по трём каналам: ручная загрузка через веб-интерфейс, отслеживаемая папка на диске и получение email по IMAP.

Загрузка через веб-интерфейс

Перетащи файлы в веб-интерфейс. Работает сразу после установки. Поддерживаемые форматы: PDF, PNG, JPEG, TIFF и (при включённом Tika) DOCX, XLSX, ODT и другие офисные форматы.

Отслеживаемая папка (директория consume)

Директория ./consume, смонтированная в Compose-файле, отслеживается через inotify. Положи файл — и Paperless-ngx подхватит его за секунды.

cp /tmp/scan-001.pdf /opt/paperless-ngx/consume/

Файл исчезает из директории consume после завершения обработки. Чтобы организовать приём по тегам, включи теги по подпапкам:

PAPERLESS_CONSUMER_RECURSIVE: true
PAPERLESS_CONSUMER_SUBDIRS_AS_TAGS: true

С этой настройкой файл в consume/invoices/scan.pdf автоматически получит тег invoices.

Приём email (IMAP)

Приём email настраивается целиком в веб-интерфейсе в разделе Manage > Mail. Добавляешь почтовые аккаунты и правила.

Добавить почтовый аккаунт:

  1. Перейди в Manage > Mail > Mail Accounts
  2. Укажи IMAP-сервер, порт (993 для TLS), имя пользователя и пароль
  3. Укажи папку для мониторинга (например, INBOX)

Добавить правило:

  1. Перейди в Manage > Mail > Mail Rules
  2. Выбери почтовый аккаунт
  3. Выбери, что принимать: только вложения или всё письмо как PDF
  4. Задай действие для обработанных писем: пометить как прочитанное, переместить в папку или удалить
  5. Опционально назначь тег, корреспондента или тип документа

Типичная настройка: пересылай документы на выделенный email (например, scan@tvoidomen.ru), настрой Paperless-ngx на мониторинг этого ящика и приём вложений. Оригиналы после обработки перемещаются в IMAP-папку «Archived».

Как организовать пути хранения и имена файлов?

Paperless-ngx хранит документы в двух местах: том media содержит файлы на диске, а PostgreSQL управляет метаданными. Переменная PAPERLESS_FILENAME_FORMAT определяет именование архивных файлов в директории media.

Compose-файл выше использует:

PAPERLESS_FILENAME_FORMAT: "{created_year}/{correspondent}/{title}"

Это создаёт структуру вида:

media/documents/archive/
  2026/
    Springfield Energy Inc/
      Electricity Bill January 2026.pdf
    Dr. Smith/
      Lab Results March 2026.pdf

Доступные переменные шаблона: {created_year}, {created_month}, {created_day}, {correspondent}, {document_type}, {title}, {tag_list} и {owner_username}. Если переменная пуста (корреспондент не назначен), Paperless-ngx заменяет её на none.

После изменения формата имён примени его к существующим документам:

docker compose exec webserver document_renamer

Какая стратегия резервного копирования лучше для Paperless-ngx?

Рабочий бэкап покрывает базу данных PostgreSQL, файлы документов (оригиналы + архивы + миниатюры) и метаданные Paperless-ngx (теги, корреспонденты, правила сопоставления). Экспортёр документов фиксирует всё это в переносимом формате. Дополни его дампом базы данных для более быстрого восстановления только БД.

Дамп базы данных

docker compose exec db pg_dump -U paperless paperless | gzip > /opt/paperless-ngx/backups/db-$(date +%Y%m%d).sql.gz

Экспортёр документов

Экспортёр создаёт полный снапшот: документы, метаданные и манифест. Это каноническая процедура бэкапа.

docker compose exec webserver document_exporter ../export -c -d

Флаг -c сравнивает контрольные суммы (экспортирует только изменённые файлы). Флаг -d удаляет из экспорта файлы, которых больше нет в Paperless-ngx. Вместе они поддерживают директорию экспорта как зеркало текущего состояния.

Автоматизация через cron

Создай скрипт бэкапа:

cat > /opt/paperless-ngx/backup.sh << 'SCRIPT'
#!/bin/bash
set -euo pipefail

BACKUP_DIR="/opt/paperless-ngx/backups"
EXPORT_DIR="/opt/paperless-ngx/export"

mkdir -p "$BACKUP_DIR"

# Database dump
docker compose -f /opt/paperless-ngx/docker-compose.yml exec -T db \
  pg_dump -U paperless paperless | gzip > "$BACKUP_DIR/db-$(date +%Y%m%d).sql.gz"

# Document exporter
docker compose -f /opt/paperless-ngx/docker-compose.yml exec -T webserver \
  document_exporter ../export -c -d

# Remove database dumps older than 30 days
find "$BACKUP_DIR" -name "db-*.sql.gz" -mtime +30 -delete

echo "[$(date -Is)] Backup completed" >> "$BACKUP_DIR/backup.log"
SCRIPT

chmod 700 /opt/paperless-ngx/backup.sh

Запланируй через cron (существующие записи crontab сохраняются):

(crontab -l 2>/dev/null; echo "0 3 * * * /opt/paperless-ngx/backup.sh") | crontab -

Это добавляет ежедневную задачу в 03:00.

Удалённая синхронизация

Директория экспорта и дампы базы должны покинуть сервер. Используй rsync или rclone для отправки на второй сервер. Для S3-совместимого хранилища:

rclone sync /opt/paperless-ngx/export remote:paperless-backup/export
rclone sync /opt/paperless-ngx/backups remote:paperless-backup/db-dumps

Для другого сервера по SSH:

rsync -az --delete /opt/paperless-ngx/export/ backup-server:/backups/paperless/export/
rsync -az /opt/paperless-ngx/backups/ backup-server:/backups/paperless/db-dumps/

Подробнее о стратегиях бэкапа Docker-томов — в нашем руководстве по бэкапу Docker-томов.

Как восстановить бэкап Paperless-ngx?

Восстановление требует чистой установки Paperless-ngx. Подними стек, затем импортируй.

Восстановление только базы данных (быстрее, при повреждении БД или миграции):

gunzip < /opt/paperless-ngx/backups/db-20260320.sql.gz | \
  docker compose exec -T db psql -U paperless paperless

Полное восстановление из экспортёра (документы, теги, корреспонденты, всё):

docker compose exec webserver document_importer ../export

Запускай импортёр на пустой базе данных. Он воссоздаёт все метаданные и связывает файлы из директории экспорта.

Регулярно проверяй восстановление. Бэкап, который ты ни разу не восстанавливал — это не бэкап.

Как укрепить безопасность контейнеров Paperless-ngx?

Compose-файл выше уже покрывает основы: файлы секретов вместо паролей в открытом виде, привязка порта к localhost и маппинг пользователя. Вот дополнительные меры.

Убрать лишние capabilities

Добавь параметры безопасности к сервису webserver:

webserver:
  # ... существующая конфигурация ...
  security_opt:
    - no-new-privileges:true

Это не позволяет процессу контейнера получать дополнительные привилегии через setuid-бинарники.

Установить лимиты ресурсов

Не дай вышедшему из-под контроля OCR-процессу сожрать все ресурсы сервера:

webserver:
  # ... существующая конфигурация ...
  deploy:
    resources:
      limits:
        memory: 2g
        cpus: "2.0"
      reservations:
        memory: 512m

Подробнее о лимитах ресурсов — в нашем руководстве по лимитам ресурсов Docker Compose.

Скрыть информацию о версии

Настрой reverse proxy на удаление заголовка Server. Для Nginx:

server_tokens off;

Сам Paperless-ngx не раскрывает версию в HTTP-заголовках, но reverse proxy может это делать.

Как обновить Paperless-ngx?

Скачай последние образы и пересоздай контейнеры. Образ Paperless-ngx запускает миграции базы данных автоматически при старте.

cd /opt/paperless-ngx
docker compose pull
docker compose up -d

После обновления проверь логи на предмет миграций:

docker compose logs webserver --tail 30

Ищи строки вида Applying documents.XXXX_migration_name... OK. Если миграции падают, контейнер останавливается. Проверяй примечания к релизу перед крупными обновлениями версий.

Всегда запускай скрипт бэкапа перед обновлением.

Что-то пошло не так?

Контейнер остаётся unhealthy: Проверь логи через docker compose logs <service> --tail 50. Частые причины: несовпадение пароля PostgreSQL (перегенерируй .db_password и пересоздай том базы данных), отказ соединения с Redis (broker ещё не запустился).

OCR выдаёт мусор: Неправильный язык. Проверь, что PAPERLESS_OCR_LANGUAGE соответствует твоим документам. Для многоязычных документов добавь все нужные коды языков через +.

Документы не появляются после загрузки: Проверь лог consumer:

docker compose logs webserver --tail 50 | grep -i consumer

Частая причина: проблема с правами доступа. Директория consume должна быть доступна для записи пользователю с UID 1000 (или значением USERMAP_UID):

chown -R 1000:1000 /opt/paperless-ngx/consume

Автоматическая разметка не работает: Классификатору нужно минимум ~50 вручную размеченных документов. Проверь, обучен ли классификатор:

docker compose exec webserver document_create_classifier

Дисковое пространство быстро растёт: Проверь, какие документы самые большие:

docker compose exec webserver document_sanity_checker

Также проверь режим OCR. Режим force создаёт архивные файлы значительно большего размера, чем skip.

Приём email не работает: Проверь IMAP-данные в веб-интерфейсе. Запусти ручное получение, чтобы увидеть ошибки:

docker compose exec webserver mail_fetcher

Другие руководства по self-hosting — см. наше руководство по Immich для управления фотографиями на том же стеке.


Авторское право 2026 Virtua.Cloud. Все права защищены. Данный контент является оригинальным произведением команды Virtua.Cloud. Воспроизведение, повторная публикация или распространение без письменного разрешения запрещены.

Готовы попробовать?

Разверните свой сервер за секунды. Linux, Windows или FreeBSD.

Смотреть тарифы VPS