نصب paperless-ngx روی VPS با Docker Compose
راهنمای راهاندازی paperless-ngx روی VPS با Docker Compose؛ پشته رسمی Postgres، تنظیم PAPERLESS_URL، پوشه consume، زبانهای OCR، HTTPS و پشتیبانگیری.
آنچه میسازید
Paperless-ngx روی یک VPS، پوشهای از اسناد کاغذی اسکنشده را به یک بایگانی قابل جستوجو تبدیل میکند. یک PDF را در فهرست تحت نظارت قرار میدهید؛ سرور OCR (تشخیص نوری نویسهها) را روی آن اجرا میکند، متن را استخراج میکند، تاریخ و مکاتبهکننده را حدس میزند و سند را بایگانی میکند. نصب شامل یک فایل Docker Compose با چهار سرویس است. پس از آن، همهچیز به پیکربندی مربوط میشود. این راهنما بیشتر حجم خود را به همین بخش اختصاص میدهد، زیرا نصبها معمولاً در این مرحله دچار مشکل میشوند.
Paperless-ngx نسخه منشعب و تحت نگهداری جامعه از پروژه اصلی Paperless است. این نرمافزار رایگان و خودمیزبان است و اسناد شما را بهصورت فایلهای ساده روی دیسک ذخیره میکند؛ بنابراین هرگز دسترسی به بایگانی خود را از دست نمیدهید. اجرای آن روی یک VPS، بهجای رایانهای در خانه، باعث میشود اسکنهای شما از هر مکانی قابل دسترسی باشند، بدون اینکه لازم باشد پورتی را روی روتر خانگی خود باز کنید. همچنین برای یک نمونه خصوصی Nextcloud برای فایلهایی که کاغذی نیستند گزینه مناسبی است.
پشته در عمل چه چیزی را اجرا میکند
فایل رسمی compose چهار کانتینر را راهاندازی میکند. دانستن وظیفه هرکدام، خواندن logها را آسانتر میکند.
webserver: خود image مربوط به paperless-ngx است. رابط وب، API، consumer که پوشه ورودی شما را monitor میکند، و workerهای وظیفه Celery را که OCR را انجام میدهند، اجرا میکند.db: PostgreSQL است. metadata، tagها، correspondentها و جدولهای index جستوجوی full-text را نگهداری میکند. فایلهای PDF شما در آن ذخیره نمیشوند.broker: Valkey، یک key-value store سازگار با Redis است. این سرویس صف وظایف بین فرایند وب و workerها است.gotenbergوtika: اختیاری هستند و فقط در گونههای compose مربوط به-tikaوجود دارند. این سرویسها اسناد Office شامل.docx،.xlsxو.odtرا به PDF تبدیل میکنند تا paperless بتواند آنها را index کند.
تا July 2026، فایل compose مربوط به postgres، docker.io/library/postgres:18 و docker.io/valkey/valkey:9-alpine را ثابت میکند و application را از ghcr.io/paperless-ngx/paperless-ngx:latest دریافت میکند.
پیشنیازها
- یک KVM VPS با Ubuntu 24.04 و دسترسی
sudoداشته باشید و Docker بههمراه افزونه Compose از قبل نصب شده باشد. اگر این بخش برایتان جدید است، ابتدا مبانی Docker Compose برای VPS را مطالعه کنید و سپس به این راهنما برگردید. - یک نام دامنه داشته باشید که رکورد A آن به VPS اشاره کند. Paperless روی نام میزبانیای که از قبل به آن معرفی نشده باشد سرویس ارائه نمیدهد؛ بنابراین این مورد زودتر از چیزی که انتظار دارید اهمیت پیدا میکند.
- حافظه محدودیت اصلی است. PostgreSQL، Valkey، gunicorn و یک worker مربوط به OCR در Tesseract، همگی بهصورت همزمان، برای استفاده سبک در 2 GB جا میگیرند. اگر قصد دارید انباشتۀ شامل صدها اسکن را وارد کنید، 4 GB اختصاص دهید؛ زیرا OCR روی یک PDF چندصفحهای بزرگ، جهش مصرف حافظهای ایجاد میکند که باعث میشود kernel، worker را با out-of-memory killer خاتمه دهد.
- دیسک: آرشیو شما دو بار ذخیره میشود؛ یک بار بهصورت فایل اصلی و بار دیگر بهصورت PDF آرشیوشده با OCR. بنابراین تقریباً دو برابر حجم اسکنهای خود فضا در نظر بگیرید.
دریافت فایلهای رسمی 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 است. این نام به پیشوند همه کانتینرها و volumeها تبدیل میشود؛ بنابراین آن را حذف نکنید و بعد تعجب نکنید که چرا docker compose down -v نمیتواند دادههای شما را پیدا کند.
نخست docker compose down -v را پیش از اولین راهاندازی پیکربندی کنید
2 تنظیم اختیاری نیستند. کلید محرمانه را با دستوری که پروژه مستند کرده است تولید کنید:
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 ارائه میشود. این مقدار کوکیهای نشست را امضا میکند. اگر آن را تغییر ندهید، هر کسی که مقدار پیشفرض را بداند میتواند یک نشست جعلی ایجاد کند. آن را پیش از اولین راهاندازی تنظیم کنید، زیرا تغییر آن در آینده باعث خروج همه کاربران از حسابشان میشود.
PAPERLESS_URL همان تنظیمی است که 1 ساعت در زمان شما صرفهجویی میکند. 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 کپی میکنید برای consumer قابل خواندن نیستند و گزارش بهجای import، خطای مجوز را نشان میدهد.
راهاندازی پشته و ایجاد نخستین کاربر
docker compose pull
docker compose up -d
docker compose run --rm webserver createsuperuser
docker compose logs -f webservercreatesuperuser نام کاربری، ایمیل و گذرواژه را درخواست میکند. ورود پیشفرضی وجود ندارد؛ بنابراین رد کردن این مرحله شما را به صفحه ورود میبرد، اما هیچ اطلاعاتی در آن پذیرفته نمیشود. پیش از باز کردن مرورگر، منتظر خط گزارششده در لاگ بمانید که اعلام میکند سرور روی port 8000 در حال گوشدادن است. نخستین راهاندازی همچنین migrationهای پایگاهداده را اجرا میکند که 1 یا 2 دقیقه زمان میبرد.
پیش از استفاده از domain، سرویس را بهصورت محلی بررسی کنید:
curl -I http://127.0.0.1:8000redirect از 302 به /accounts/login/ نشان میدهد که پشته سالم است.
HTTPS را در جلوی آن قرار دهید
فایل Compose پیشفرض 8000:8000 را منتشر میکند و آن را به همه رابطهای شبکه متصل میکند. در یک VPS عمومی، این کار کل بایگانی اسناد شما را از طریق HTTP ساده در اختیار هر کسی قرار میدهد که نشانی را پیدا کند. خط پورت را تغییر دهید تا فقط به loopback متصل شود:
ports:
- "127.0.0.1:8000:8000"سپس TLS (امنیت لایه انتقال) را در یک reverse proxy خاتمه دهید و درخواستها را به 127.0.0.1:8000 ارسال کنید. اگر این تنها برنامه روی سرور است، هر proxy دارای کارخواه ACME (محیط مدیریت خودکار گواهی) مناسب است. اگر چندین container را پشت یک تنظیم گواهی واحد اجرا میکنید، از الگوی Traefik reverse proxy برای چند برنامه Docker Compose پیروی کنید و سرویس webserver را بدون هیچ پورت منتشرشدهای به شبکه proxy متصل کنید.
صرفنظر از proxy مورد استفاده، باید X-Forwarded-Proto: https را ارسال کند. بدون این مقدار، Django تصور میکند درخواست از طریق HTTP رسیده است، بررسی مبدأ در فرم ورود شکست میخورد و در صفحهای که از نظر ظاهری درست است، CSRF verification failed. Request aborted. دریافت میکنید. بخش دیگر این اصلاح، تنظیم PAPERLESS_URL روی نشانی دقیق https:// است که در مرورگر وارد میکنید.
همچنین حد اندازه بارگذاری proxy را افزایش دهید. یک اسکن 40 MB از طریق proxy که بدنه درخواستها را به 1 MB محدود میکند، پیش از آنکه paperless آن را دریافت کند رد میشود و مرورگر یک خطای عمومی بارگذاری گزارش میکند.
فهرست consume چگونه کار میکند
فایل compose، ./consume را از فهرست compose بهصورت bind mount داخل container متصل میکند. هر فایلی را که در آن قرار دهید، وارد میشود و سپس از فهرست حذف میشود، زیرا فایل اکنون در volume رسانه و تحت مدیریت paperless قرار دارد.
cp ~/scan-2026-07-14.pdf ~/paperless/consume/
docker compose logs -f webserverباید ببینید که consumer نام فایل را دریافت میکند، OCR را اجرا میکند و با خطی پایان مییابد که اضافهشدن سند را گزارش میدهد. این چرخه برای یک اسکن یکصفحهای چند ثانیه طول میکشد و برای یک سند طولانی ممکن است 1 دقیقه یا بیشتر زمان ببرد.
2 تنظیم نحوه یافتن فایلها را تغییر میدهند. PAPERLESS_CONSUMER_RECURSIVE=true باعث میشود paperless در زیرفهرستها جستوجو کند و PAPERLESS_CONSUMER_SUBDIRS_AS_TAGS=true نام هر زیرفهرست را به یک tag تبدیل میکند. بنابراین، قرار دادن یک فایل در consume/invoices/2026/، tagهای invoices و 2026 را به آن اختصاص میدهد. این سادهترین سیستم بایگانی است که میتوانید بسازید.
تشخیص فایلها بخش دیگر این فرایند است. بهصورت پیشفرض، PAPERLESS_CONSUMER_POLLING_INTERVAL برابر با 0 است؛ یعنی paperless از اعلانهای filesystem در kernel استفاده میکند که بلافاصله فعال میشوند. این اعلانها از filesystem شبکه عبور نمیکنند. اگر فهرست consume شما یک share از نوع NFS یا SMB باشد تا یک اسکنر شبکه بتواند در آن بنویسد، هیچ فایلی شناسایی نمیشود. برای رفع این مشکل، interval را روی یک عدد مثبت برحسب ثانیه تنظیم کنید تا paperless بهجای آن، فهرست را اسکن کند.
زبانهای OCR و هزینه آنها
PAPERLESS_OCR_LANGUAGE کد سهحرفی Tesseract را میپذیرد و مقدار پیشفرض آن eng است. زبانها را با علامت جمع ترکیب کنید؛ مانند deu+eng. سپس Tesseract هر زبان را امتحان میکند و بهترین نتیجه را نگه میدارد. بنابراین، هر زبان اضافی زمان CPU صرفشده برای هر صفحه را افزایش میدهد. در یک VPS با vCPU اشتراکی، این موضوع میتواند تفاوت بین پایان اسکن در ده ثانیه و پایان آن در یک دقیقه باشد. فقط زبانهایی را فهرست کنید که اسناد شما واقعاً با آنها نوشته شدهاند.
این image شامل زبانهای انگلیسی، آلمانی، ایتالیایی، اسپانیایی و فرانسوی است. برای هر زبان دیگر، آن زبان را به PAPERLESS_OCR_LANGUAGES بهصورت فهرستی با جداکننده فاصله اضافه کنید؛ برای مثال PAPERLESS_OCR_LANGUAGES=tur ces، سپس سرویس را restart کنید. container بستههای داده Tesseract را هنگام startup دانلود میکند؛ بنابراین نخستین boot پس از این تغییر کندتر خواهد بود.
از پایگاهداده و رسانه پشتیبان تهیه کنید
کپیکردن volumeهای Docker در حالی که PostgreSQL در حال اجراست، پشتیبانی ایجاد میکند که ممکن است قابل بازیابی نباشد. Paperless ابزار export مخصوص خود را دارد که اسناد و یک manifest با قالب JSON شامل همه فرادادهها را در mount اتصالیافته ./export مینویسد:
docker compose exec webserver document_exporter ../export --delete --no-progress-bar--delete فایلهای exportشدهای را که دیگر با یک سند فعلی مطابقت ندارند حذف میکند تا پوشه بهجای رشد بیپایان، یک mirror باقی بماند. --no-progress-bar هنگام اجرای این فرایند از cron، خروجی را پاکیزه نگه میدارد.
بازیابی با document_importer در همان پوشه و روی یک stack تازه انجام میشود. بنابراین، تنها چیزی که باید ایمن نگه دارید، پوشه export است. آن را طبق یک برنامه زمانی، با پشتیبانگیریهای restic رمزنگاریشده و حذفتکراری از VPS شما به محل دیگری ارسال کنید و ابتدا export را اجرا کنید تا restic هرگز یک archive نیمهنوشته را ثبت نکند.
برای بررسی پشتیبان، مطمئن شوید export/manifest.json وجود دارد و تعداد فایلها با تعداد اسناد نمایشدادهشده در رابط کاربری مطابقت دارد. پشتیبانی که هرگز فهرست فایلهای آن را بررسی نکردهاید، پشتیبان محسوب نمیشود.
FAQ
چرا پس از متصل کردن دامنه به برنامه، همه صفحهها پاسخ «درخواست نامعتبر (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 با حسابی که مالک فایل است مطابقت ندارند؛ بنابراین آنها را اصلاح کنید و کانتینر را دوباره ایجاد کنید. نبودن هرگونه خط در گزارش یعنی رویداد فایل هرگز دریافت نشده است. این وضعیت در اشتراکهای شبکه رخ میدهد، زیرا اعلانهای kernel از آنها عبور نمیکنند. مقدار PAPERLESS_CONSUMER_POLLING_INTERVAL را مثلاً روی 30 تنظیم کنید تا paperless در عوض پوشه را هر 30 ثانیه اسکن کند.
آیا میتوانم paperless-ngx را بهجای PostgreSQL با SQLite اجرا کنم؟
بله، docker-compose.sqlite.yml پشتیبانی میشود و حافظه کمتری مصرف میکند؛ بنابراین برای یک VPS کوچک مناسب است. پیامد این انتخاب با بزرگتر شدن بایگانی مشخص میشود: جستوجوی کاملمتن و ویرایش گروهی برچسبها هنگام رسیدن تعداد اسناد به هزاران مورد، بهطور محسوسی کندتر میشوند. مهاجرت در آینده به export و import نیاز دارد؛ بنابراین اگر انتظار دارید بایگانی همچنان بزرگتر شود، از همین حالا PostgreSQL را انتخاب کنید.
یک بایگانی از اسکنها واقعاً به چه مقدار فضای دیسک نیاز دارد؟
تقریباً دو برابر اندازه فایلهای منبع. Paperless نسخه اصلی را بدون تغییر نگه میدارد و یک PDF دوم با OCR و لایه متن قابل جستوجو، بهعلاوه thumbnailهای کوچک، ذخیره میکند. یک اسکن متنی 200 KB همچنان حجم کمی دارد. یک اسکن رنگی 30 MB از یک قرارداد طولانی، حدود 60 MB فضا اشغال میکند. اگر پوشه export را روی همان دیسک نگه میدارید، فضای آن را نیز اضافه کنید؛ در این حالت همان بایگانی سه برابر فضا اشغال میکند.
آیا به کانتینرهای Tika و Gotenberg نیاز دارم؟
فقط اگر میخواهید فایلهای Word، Excel یا OpenDocument در کنار PDFهای شما index شوند. آنها این قالبها را به PDF تبدیل میکنند تا paperless بتواند آنها را با OCR پردازش و جستوجو کند. این کانتینرها همچنین دو کانتینر در حال اجرا و چند صد مگابایت حافظه به سیستم اضافه میکنند؛ بنابراین اگر همه فایلهایی که بایگانی میکنید از قبل PDF یا تصویر هستند، آنها را روی یک سیستم کوچک اجرا نکنید.