איך להפעיל 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 הרשמי מפעיל ארבעה קונטיינרים. הבנת התפקיד של כל אחד מהם מקלה על קריאת הלוגים.
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 מסוג KVM עם Ubuntu 24.04 והרשאות
sudo, וכן Docker עם תוסף Compose שכבר מותקן. אם החלק הזה חדש עבורכם, התחילו ב־יסודות Docker Compose עבור VPS וחזרו לכאן. - שם דומיין עם רשומת A המפנה אל ה־VPS. Paperless מסרב לפעול בשם מארח שלא הוגדר בו, ולכן דרישה זו חשובה מוקדם מהצפוי.
- הזיכרון הוא המגבלה העיקרית. PostgreSQL, Valkey, gunicorn ותהליך worker אחד של Tesseract OCR יכולים לפעול יחד בתוך 2 GB בשימוש קל. הקצו 4 GB אם אתם מתכננים לייבא צבר של מאות סריקות, משום ש־OCR על קובץ PDF גדול ורב־עמודים יוצר את קפיצת צריכת הזיכרון שעלולה לגרום ל־kernel out-of-memory killer להפסיק worker.
- אחסון: הארכיון נשמר פעמיים — הקובץ המקורי וקובץ PDF ארכיוני שעבר OCR. לכן הקצו בערך פי 2 מנפח הסריקות.
קבלת קובצי 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. שם זה הופך לקידומת של כל container ושל כל volume. לכן אל תמחק אותו ותתהה לאחר מכן מדוע 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. אם הערכים אינם תואמים, הקבצים שתעתיקו לתיקיית הצריכה לא יהיו ניתנים לקריאה עבור הצרכן, וביומן תופיע שגיאת הרשאות במקום ייבוא.
הפעלת המחסנית ויצירת המשתמש הראשון
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 (אבטחת שכבת התעבורה) ב-reverse proxy והעבירו את התעבורה אל 127.0.0.1:8000. אם זהו היישום היחיד בשרת, כל proxy עם לקוח ACME (סביבת ניהול אוטומטי של אישורים) יתאים. אם אתם מפעילים כמה קונטיינרים מאחורי תצורת אישור אחת, פעלו לפי התבנית של 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 ממפה באמצעות bind את ./consume מתוך תיקיית compose אל תוך הקונטיינר. כל קובץ שתציבו שם ייובא ולאחר מכן יימחק מהתיקייה, משום שהקובץ נשמר כעת ב-volume של המדיה בניהול 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 משותף, ההבדל יכול להיות בין סריקה שמסתיימת בתוך עשר שניות לבין סריקה שמסתיימת בתוך דקה. יש לציין רק את השפות שבהן המסמכים שלכם כתובים בפועל.
ה-image כולל אנגלית, גרמנית, איטלקית, ספרדית וצרפתית. עבור כל שפה אחרת, הוסיפו את השפה ל-PAPERLESS_OCR_LANGUAGES כרשימה מופרדת ברווחים, למשל PAPERLESS_OCR_LANGUAGES=tur ces, והפעילו מחדש. ה-container מוריד את חבילות הנתונים של Tesseract בעת האתחול, ולכן האתחול הראשון לאחר שינוי זה אטי יותר.
גיבוי מסד הנתונים והמדיה
העתקת אמצעי האחסון של Docker בזמן ש-PostgreSQL פועל יוצרת גיבוי שאולי לא ניתן לשחזר. Paperless כולל כלי ייצוא משלו, שכותב את המסמכים וכן מניפסט JSON של כל המטא-נתונים אל נקודת העיגון ./export מסוג bind:
docker compose exec webserver document_exporter ../export --delete --no-progress-bar--delete מסיר קבצים שיוצאו ואינם תואמים עוד למסמך קיים, כך שהתיקייה נשארת עותק משקף במקום לגדול ללא הגבלה. --no-progress-bar שומר על פלט נקי כאשר הפעולה מופעלת באמצעות cron.
השחזור מתבצע באמצעות document_importer מול אותה תיקייה ב-stack חדש, ולכן תיקיית הייצוא היא הפריט היחיד שעליכם לשמור עליו. שלחו אותה לאתר מרוחק לפי לוח זמנים, באמצעות גיבויי restic מוצפנים ומסולקים מכפילויות מה-VPS שלכם, והפעילו תחילה את הייצוא כדי ש-restic לעולם לא ילכוד ארכיון שנכתב באופן חלקי.
אמתו גיבוי באמצעות בדיקה שהקובץ export/manifest.json קיים ושמספר הקבצים תואם למספר המסמכים בממשק. גיבוי שמעולם לא ביצעתם לו רשימה אינו גיבוי.
FAQ
מדוע כל עמוד מחזיר "Bad Request (400)" לאחר שהפניתי אליו את הדומיין שלי?
Django דוחה את הכותרת Host, משום שהדומיין שלך אינו מופיע ב-ALLOWED_HOSTS. הגדר את PAPERLESS_URL=https://paperless.example.com בקובץ docker-compose.env, ללא לוכסן בסוף, ולאחר מכן הרץ את docker compose up -d כדי ליצור מחדש את הקונטיינר. עריכת קובץ הסביבה בלבד אינה מספיקה, משום שהקונטיינר הפועל ממשיך להשתמש בסביבה שבה הופעל.
העתקתי קובץ PDF לתיקיית הצריכה ושום דבר לא קרה. מה הבעיה?
בדוק תחילה את docker compose logs webserver. שגיאת הרשאות פירושה שהערכים של USERMAP_UID ושל USERMAP_GID אינם תואמים לחשבון שבבעלותו הקובץ, ולכן יש לתקן אותם וליצור מחדש את הקונטיינר. אם אין שורת log כלל, אירוע הקובץ לא הגיע מעולם. מצב זה מתרחש בשיתופי רשת, משום שהתראות הליבה אינן חוצות אותם. הגדר את PAPERLESS_CONSUMER_POLLING_INTERVAL לערך כגון 30, ו-paperless יסרוק את התיקייה כל 30 שניות במקום זאת.
האם אפשר להפעיל את paperless-ngx עם SQLite במקום PostgreSQL?
כן. docker-compose.sqlite.yml נתמך וצורך פחות זיכרון, ולכן הוא מתאים ל-VPS קטן. הפשרה מורגשת כשהארכיון גדל: חיפוש בטקסט מלא ועריכה מרוכזת של תגיות נעשים איטיים במידה ניכרת כאשר יש אלפי מסמכים. העברה בהמשך מחייבת ייצוא וייבוא, לכן בחר ב-PostgreSQL כבר עכשיו אם אתה מצפה שהארכיון ימשיך לגדול.
כמה שטח דיסק נדרש בפועל לארכיון סריקות?
בערך פי 2 מגודל קובצי המקור. paperless שומר את המקור ללא שינוי ומאחסן PDF נוסף שעבר OCR וכולל שכבת טקסט הניתנת לחיפוש, וכן תמונות ממוזערות קטנות. סריקה של 200 KB המכילה טקסט בלבד נשארת קטנה. סריקה צבעונית של 30 MB של חוזה ארוך תופסת כ-60 MB. יש להוסיף את ספריית הייצוא אם היא נשמרת באותו דיסק; במקרה כזה אותו ארכיון תופס בדיסק פי 3 מנפחו.
האם אני זקוק לקונטיינרים של Tika ושל Gotenberg?
רק אם ברצונך לאנדקס קובצי Word, Excel או OpenDocument לצד קובצי ה-PDF שלך. הם ממירים את הפורמטים האלה ל-PDF, כדי ש-paperless יוכל לבצע בהם OCR ולחפש בהם. הם מוסיפים גם 2 קונטיינרים פעילים ועוד כמה מאות מגה-בייט של זיכרון, לכן אפשר לדלג עליהם במערכת קטנה אם כל מה שאתה מתייק הוא כבר PDF או תמונה.