Установка Paperless-ngx на VPS через Docker Compose
Пошаговое руководство по развертыванию Paperless-ngx на удаленном сервере. Настройка Postgres, переменных PAPERLESS_URL, OCR, HTTPS и организация резервного копирования данных.
Что вы создаете
Paperless-ngx на VPS превращает папку со сканированными документами в архив с возможностью поиска. Вы помещаете PDF в отслеживаемую директорию, сервер выполняет OCR (оптическое распознавание символов), извлекает текст, определяет дату и корреспондента, а затем сохраняет файл. Установка состоит из одного файла Docker Compose с четырьмя сервисами. Всё, что следует далее, относится к настройке, и данное руководство уделяет этому основное внимание, так как именно здесь чаще всего возникают ошибки при установке. Это не фотогалерея: OCR и определение корреспондента бесполезны для папки с праздничными JPEG-файлами, поэтому храните их в специализированном фотосервере, а Paperless используйте только для документов.
Paperless-ngx — это поддерживаемый сообществом форк исходного проекта Paperless. Это бесплатное self-hosted-приложение, которое хранит документы в виде обычных файлов на диске, поэтому вы всегда сохраняете доступ к собственному архиву. Размещение на VPS вместо домашнего компьютера позволяет получать доступ к сканам из любой точки без открытия порта на домашнем маршрутизаторе. Кроме того, Paperless-ngx хорошо сочетается с приватным экземпляром Nextcloud для файлов, которые не существуют на бумаге. Тот же принцип применим к компьютеру, к которому подключён сканер: собственный relay RustDesk на этом VPS позволяет управлять этим компьютером удалённо, также без открытия порта на маршрутизаторе.
Что на самом деле запускает стек
Официальный compose-файл запускает четыре контейнера. Понимание функций каждого из них помогает при чтении логов.
webserver: сам образ paperless-ngx. Он запускает веб-интерфейс, API, потребителя, который отслеживает вашу папку для входящих файлов, и воркеры Celery, выполняющие OCR.db: PostgreSQL. В нем хранятся метаданные, теги, корреспонденты и таблицы полнотекстового поискового индекса. Сами PDF-файлы здесь не хранятся.broker: Valkey, хранилище типа «ключ-значение», совместимое с Redis. Оно служит очередью задач между веб-процессом и воркерами.gotenbergиtika: опциональные компоненты, присутствуют только в вариантах compose-tika. Они конвертируют документы Office (.docx,.xlsx,.odt) в формат PDF, чтобы paperless мог их проиндексировать.
По состоянию на июль 2026 года compose-файл для postgres фиксирует версии docker.io/library/postgres:18 и docker.io/valkey/valkey:9-alpine, а приложение загружается из ghcr.io/paperless-ngx/paperless-ngx:latest.
Предварительные требования
- VPS на базе Ubuntu 24.04 с KVM, доступом sudo и установленным Docker с плагином Compose. Если вы с этим еще не работали, начните с основ Docker Compose для VPS и вернитесь сюда.
- Доменное имя с A-записью, указывающей на ваш VPS. Paperless не будет работать с именем хоста, которое не было задано в конфигурации, поэтому это важно сделать раньше, чем вы ожидаете.
- Оперативная память — основной ограничивающий фактор. PostgreSQL, Valkey, gunicorn и воркер Tesseract OCR при одновременной работе требуют около 2 GB для легкой нагрузки. Выделите 4 GB, если планируете импортировать большой архив из сотен сканов, так как OCR для объемных многостраничных PDF вызывает резкий скачок потребления памяти, из-за чего ядро может принудительно завершить процесс (OOM killer).
- Диск: ваш архив хранится в двух экземплярах — исходный файл и PDF с распознанным текстом, поэтому рассчитывайте объем хранилища примерно в два раза больше общего размера ваших сканов.
Получение официальных compose-файлов
Существует интерактивный установщик:
bash -c "$(curl --location --silent --show-error https://raw.githubusercontent.com/paperless-ngx/paperless-ngx/main/install-paperless-ngx.sh)"Он задает вопросы и создает файлы за вас. Ручная настройка состоит из четырех команд и позволяет точно знать расположение всех компонентов, что необходимо для сервера, который вы будете обслуживать.
mkdir -p ~/paperless && cd ~/paperless
curl -fsSL -o docker-compose.yml https://raw.githubusercontent.com/paperless-ngx/paperless-ngx/main/docker/compose/docker-compose.postgres.yml
curl -fsSL -o docker-compose.env https://raw.githubusercontent.com/paperless-ngx/paperless-ngx/main/docker/compose/docker-compose.env
curl -fsSL -o .env https://raw.githubusercontent.com/paperless-ngx/paperless-ngx/main/docker/compose/.envВарианты находятся в одном каталоге: docker-compose.sqlite.yml, docker-compose.mariadb.yml и -tika-версия для каждого из них. Выбирайте postgres для новой установки. SQLite подходит для нескольких сотен документов, но индекс полнотекстового поиска начинает работать медленно значительно раньше, чем в случае с PostgreSQL.
Файл .env содержит одну строку: COMPOSE_PROJECT_NAME=paperless. Это имя становится префиксом для каждого контейнера и тома, поэтому не удаляйте его, чтобы потом не гадать, почему docker compose down -v не может найти ваши данные.
Настройка docker-compose.env перед первым запуском
Два параметра являются обязательными. Сгенерируйте секретный ключ с помощью команды, указанной в документации проекта:
python3 -c "import secrets; print(secrets.token_urlsafe(64))"Затем отредактируйте docker-compose.env:
PAPERLESS_SECRET_KEY=<the long string you just generated>
PAPERLESS_URL=https://paperless.example.com
PAPERLESS_TIME_ZONE=Europe/Berlin
PAPERLESS_OCR_LANGUAGE=deu+eng
USERMAP_UID=1000
USERMAP_GID=1000PAPERLESS_SECRET_KEY поставляется со значением по умолчанию change-me. Оно используется для подписи сессионных cookie, поэтому, если оставить его без изменений, любой, кто знает значение по умолчанию, сможет подделать сессию. Установите его до первого запуска, так как изменение этого параметра позже приведет к выходу из системы всех пользователей.
PAPERLESS_URL — это параметр, который сэкономит вам час времени. Paperless является приложением на Django, а Django проверяет заголовок Host для каждого запроса. Установите PAPERLESS_URL, и система автоматически заполнит ALLOWED_HOSTS, CORS_ALLOWED_HOSTS и CSRF_TRUSTED_ORIGINS. Если оставить его пустым и направить домен на сервер, каждая страница будет возвращать ошибку Bad Request (400), а в логах контейнера появится сообщение DisallowedHost. Указывайте значение без слеша в конце и без пути.
USERMAP_UID и USERMAP_GID определяют пользователя, от имени которого работает контейнер. Установите значения, соответствующие вашей учетной записи, которые можно узнать с помощью id -u и id -g. Если они не совпадают, файлы, скопированные в папку consume, будут недоступны для чтения потребителем, а в логах вместо импорта появится ошибка доступа.
Запуск стека и создание первого пользователя
docker compose pull
docker compose up -d
docker compose run --rm webserver createsuperuser
docker compose logs -f webservercreatesuperuser запрашивает имя пользователя, адрес электронной почты и пароль. Учётных данных по умолчанию не существует, поэтому пропуск этого шага приведёт к странице входа, на которой невозможно авторизоваться. Дождитесь появления в логах сообщения о том, что сервер ожидает подключений на порту 8000, прежде чем открывать браузер. При самом первом запуске также выполняются миграции базы данных, что занимает одну-две минуты.
Проверьте работоспособность локально, прежде чем подключать домен:
curl -I http://127.0.0.1:8000Перенаправление 302 на /accounts/login/ означает, что стек функционирует корректно.
Размещение HTTPS перед приложением
Стандартный файл compose публикует 8000:8000, который привязывается ко всем сетевым интерфейсам. На публичном VPS это делает весь ваш архив документов доступным по обычному HTTP любому, кто узнает адрес. Измените строку порта, чтобы привязать его только к loopback:
ports:
- "127.0.0.1:8000:8000"Затем настройте TLS (transport layer security) на reverse proxy и перенаправляйте трафик на 127.0.0.1:8000. Если это единственное приложение на сервере, подойдет любой прокси с клиентом ACME (automatic certificate management environment). Если вы запускаете несколько контейнеров с общим сертификатом, следуйте шаблону reverse proxy Traefik для нескольких приложений Docker Compose и подключите сервис webserver к сети прокси без публикации портов.
Какой бы прокси вы ни использовали, он должен передавать заголовок X-Forwarded-Proto: https. Без него Django считает, что запрос пришел по HTTP, проверка источника (origin check) на форме входа завершается ошибкой, и вы получаете CSRF verification failed. Request aborted. на странице, которая выглядит корректно. Вторая часть этого решения — установка PAPERLESS_URL в точности на тот https://, который вы вводите в браузере.
Также увеличьте лимит размера загружаемых файлов в настройках прокси. Скан размером 40 MB, проходящий через прокси с ограничением тела запроса в 1 MB, будет отклонен до того, как попадет в paperless, а браузер сообщит об общей ошибке загрузки.
Принцип работы директории consume
Файл compose монтирует ./consume из директории compose внутрь контейнера. Любой файл, помещенный туда, импортируется и затем удаляется из папки, так как файл теперь хранится в томе media под управлением paperless.
cp ~/scan-2026-07-14.pdf ~/paperless/consume/
docker compose logs -f webserverВы увидите, как consumer обнаруживает имя файла, выполняет OCR и завершает работу строкой с отчетом о добавлении документа. Весь цикл занимает несколько секунд для одностраничного скана и может длиться минуту или дольше для объемного документа.
Две настройки меняют способ поиска файлов. PAPERLESS_CONSUMER_RECURSIVE=true заставляет paperless искать файлы во вложенных папках, а PAPERLESS_CONSUMER_SUBDIRS_AS_TAGS=true превращает имя каждой вложенной папки в тег, поэтому при помещении файла в consume/invoices/2026/ ему присваиваются теги invoices и 2026. Это самая простая система архивации, которую вы когда-либо создадите.
Обнаружение — это вторая часть процесса. По умолчанию PAPERLESS_CONSUMER_POLLING_INTERVAL имеет значение 0, что означает, что paperless использует уведомления файловой системы ядра, которые срабатывают мгновенно. Эти уведомления не работают через сетевые файловые системы. Если ваша папка consume является общим ресурсом NFS или SMB, чтобы сетевой сканер мог записывать в нее файлы, обнаружение не сработает. Решение заключается в установке интервала в виде положительного числа секунд, чтобы paperless вместо этого сканировал папку.
Языки OCR и их стоимость
PAPERLESS_OCR_LANGUAGE принимает трехбуквенный код Tesseract, по умолчанию используется eng. Объединяйте языки знаком «плюс», как в deu+eng. Tesseract перебирает каждый из них и сохраняет лучший результат, поэтому каждый дополнительный язык увеличивает время работы процессора для каждой страницы. На VPS с общими vCPU это разница между сканированием, которое завершается за десять секунд, и сканированием, которое длится минуту. Указывайте только те языки, на которых написаны ваши документы.
Образ поставляется с поддержкой английского, немецкого, итальянского, испанского и французского языков. Для любого другого языка добавьте его в PAPERLESS_OCR_LANGUAGES в виде списка, разделенного пробелами, например PAPERLESS_OCR_LANGUAGES=tur ces, и перезапустите контейнер. Контейнер загружает пакеты данных Tesseract при запуске, поэтому первая загрузка после внесения изменений будет медленнее.
Резервное копирование базы данных и медиафайлов
Копирование Docker-томов во время работы PostgreSQL может привести к созданию резервной копии, которую не удастся восстановить. В состав Paperless входит собственный инструмент экспорта, который записывает документы и JSON-манифест со всеми метаданными в bind mount ./export:
docker compose exec webserver document_exporter ../export --delete --no-progress-barФлаг --delete удаляет экспортированные файлы, которые больше не соответствуют текущим документам, поэтому папка остается зеркальной копией и не разрастается бесконечно. Флаг --no-progress-bar обеспечивает чистоту вывода при запуске задачи по cron.
Восстановление выполняется командой document_importer из той же папки на новом стеке, что означает, что каталог экспорта — это единственное, что необходимо хранить в безопасности. Отправляйте его на удаленное хранилище по расписанию с помощью зашифрованных дедуплицированных резервных копий restic с вашего VPS, и запускайте экспорт перед этим, чтобы restic не захватил частично записанный архив.
Проверьте резервную копию: убедитесь, что существует export/manifest.json, а количество файлов соответствует числу документов в интерфейсе. Резервная копия, содержимое которой вы ни разу не проверяли, не является резервной копией. Ещё хуже, если ночной экспорт незаметно начинает завершаться с ошибкой. Поэтому настройте cron job так, чтобы он отправлял свой код завершения на собственный сервер ntfy. Тогда вы узнаете о проблеме в течение недели, когда она возникнет, а не в тот день, когда потребуется восстановление.
FAQ
Почему каждая страница возвращает "Bad Request (400)" после того, как я направил на неё свой домен?
Django отклоняет заголовок Host, так как вашего домена нет в ALLOWED_HOSTS. Укажите PAPERLESS_URL=https://paperless.example.com в docker-compose.env без слеша в конце, затем выполните docker compose up -d для пересоздания контейнера. Редактирование файла окружения само по себе ничего не меняет, так как запущенный контейнер сохраняет переменные окружения, с которыми он был запущен.
Я поместил PDF в папку consume, но ничего не произошло. В чем проблема?
Сначала проверьте docker compose logs webserver. Ошибка прав доступа означает, что USERMAP_UID и USERMAP_GID не совпадают с учетной записью владельца файла; исправьте их и пересоздайте контейнер. Отсутствие записей в логах означает, что событие файловой системы не было получено, что случается при работе с сетевыми дисками, так как уведомления ядра не передаются через них. Установите PAPERLESS_CONSUMER_POLLING_INTERVAL в значение, например, 30, и paperless будет сканировать папку каждые 30 секунд.
Могу ли я запустить paperless-ngx с SQLite вместо PostgreSQL?
Да, docker-compose.sqlite.yml поддерживается и потребляет меньше оперативной памяти, что подходит для небольших VPS. Недостаток проявляется по мере роста архива: полнотекстовый поиск и массовое редактирование тегов заметно замедляются при наличии тысяч документов. Миграция в будущем потребует экспорта и импорта данных, поэтому выбирайте PostgreSQL сразу, если планируете расширять архив.
Сколько дискового пространства на самом деле нужно для архива сканов?
Примерно в два раза больше объема исходных файлов. Paperless сохраняет оригинал без изменений и создает второй PDF с результатами OCR и слоем поиска по тексту, а также небольшие миниатюры. Скан текста размером 200 KB остается небольшим. Цветной скан длинного контракта размером 30 MB займет около 60 MB. Учитывайте также директорию экспорта, если она находится на том же диске: в этом случае один и тот же архив будет занимать место трижды.
Нужны ли мне контейнеры Tika и Gotenberg?
Только если вы хотите, чтобы файлы Word, Excel или OpenDocument индексировались вместе с вашими PDF. Они преобразуют эти форматы в PDF, чтобы paperless мог выполнить OCR и поиск по ним. Они также добавляют два дополнительных запущенных контейнера и требуют несколько сотен мегабайт оперативной памяти, поэтому не используйте их на слабых устройствах, если все ваши документы уже представлены в форматах PDF или изображений.