LUNAROPS · OPERATIONAL UPLINK 100% UPTIME 1,247d POSTS 893 JEFF.MOON@LUNAROPS.DEV UTC --:--:--

Paperless-ngx: Building a Document Management System That Actually Works

homelabself-hostedpaperlessdockerocrdocument-management

Paper has a way of piling up — utility bills, insurance documents, tax records, medical forms. Scanning them into a folder structure solves the physical problem but not the organizational one: you still end up with scan_2024_03_final_REAL.pdf in a directory tree you half-remember.

Paperless-ngx solves this. It’s a document management system that ingests PDFs and images, runs OCR to make them full-text searchable, and lets you tag, classify, and find anything instantly. This guide covers deploying it in Docker Compose, building an automated scanning pipeline, and configuring the features that make it genuinely useful.

What Paperless-ngx Does

Paperless-ngx is a fork of the original Paperless project, actively maintained with a modern React UI. Its core loop is simple:

  1. Consume — watch an inbox folder (or receive email) for new documents
  2. OCR — extract text with Tesseract so PDFs become searchable
  3. Classify — auto-assign tags, correspondents, document types, and storage paths via rules
  4. Store — archive the original file in a structured layout

The result is a web interface where you can search for “electricity bill 2023” and find the exact document in milliseconds, regardless of what you named the file.

Architecture

Paperless-ngx runs as several cooperating containers:

  • webserver — Django application + React frontend
  • worker — Celery task queue for OCR and classification
  • broker — Redis for the task queue
  • db — PostgreSQL for metadata storage
  • gotenberg (optional) — converts Office documents to PDF
  • tika (optional) — extracts text from complex document types

For most home users, the base four containers are sufficient.

Docker Compose Setup

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# docker-compose.yml
version: "3.8"

services:
  broker:
    image: docker.io/library/redis:7
    restart: unless-stopped
    volumes:
      - redisdata:/data

  db:
    image: docker.io/library/postgres:16
    restart: unless-stopped
    environment:
      POSTGRES_DB: paperless
      POSTGRES_USER: paperless
      POSTGRES_PASSWORD: ${PAPERLESS_DB_PASSWORD}
    volumes:
      - pgdata:/var/lib/postgresql/data

  webserver:
    image: ghcr.io/paperless-ngx/paperless-ngx:latest
    restart: unless-stopped
    depends_on:
      - db
      - broker
    ports:
      - "8000:8000"
    volumes:
      - data:/usr/src/paperless/data
      - media:/usr/src/paperless/media
      - ./export:/usr/src/paperless/export
      - ./consume:/usr/src/paperless/consume
    env_file: .env
    environment:
      PAPERLESS_REDIS: redis://broker:6379
      PAPERLESS_DBHOST: db
      PAPERLESS_DBNAME: paperless
      PAPERLESS_DBUSER: paperless
      PAPERLESS_DBPASS: ${PAPERLESS_DB_PASSWORD}
      PAPERLESS_TIKA_ENABLED: 0
      PAPERLESS_TIKA_GOTENBERG_ENDPOINT: http://gotenberg:3000
      PAPERLESS_TIKA_ENDPOINT: http://tika:9998

  gotenberg:
    image: docker.io/gotenberg/gotenberg:8
    restart: unless-stopped
    command:
      - "gotenberg"
      - "--chromium-disable-javascript=true"
      - "--chromium-allow-list=file:///tmp/.*"

  tika:
    image: docker.io/apache/tika:latest
    restart: unless-stopped

volumes:
  data:
  media:
  pgdata:
  redisdata:

Note: The ./consume directory is where you drop new documents — Paperless-ngx watches it and processes anything that appears there. The ./export directory is for the built-in backup/export command.

Environment File

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# .env
PAPERLESS_DB_PASSWORD=change-this-strong-password

# Secret key for Django — generate with:
# python3 -c 'import secrets; print(secrets.token_hex(32))'
PAPERLESS_SECRET_KEY=your-secret-key-here

# Admin user created on first startup
PAPERLESS_ADMIN_USER=admin
PAPERLESS_ADMIN_PASSWORD=change-this-too
PAPERLESS_ADMIN_MAIL=admin@example.com

# URL Paperless is served from (important for email links)
PAPERLESS_URL=https://paperless.yourdomain.com

# OCR language(s) — add multiple separated by +
# Full list: https://tesseract-ocr.github.io/tessdoc/Data-Files-in-different-versions.html
PAPERLESS_OCR_LANGUAGE=eng

# Time zone
PAPERLESS_TIME_ZONE=America/New_York

# How many threads to use for OCR (default: number of CPU cores)
PAPERLESS_TASK_WORKERS=2
PAPERLESS_THREADS_PER_WORKER=1

# Convert all consumed files to PDF/A for long-term archival
PAPERLESS_OCR_OUTPUT_TYPE=pdfa

# Keep original files unchanged alongside the processed version
PAPERLESS_OCR_MODE=skip_noarchive

# Filename format for stored documents
PAPERLESS_FILENAME_FORMAT={created_year}/{correspondent}/{title}

First Start

1
2
3
4
5
# Start the stack
docker compose up -d

# Create the superuser if not using PAPERLESS_ADMIN_* env vars
docker compose exec webserver python manage.py createsuperuser

Visit http://localhost:8000 and log in. The dashboard will be empty — let’s start feeding it documents.

The Consume Pipeline

Paperless-ngx processes documents placed in the consume directory. Understanding the full pipeline helps you tune it for your needs.

Consume Directory Watching

Drop any PDF, image (JPG, PNG, TIFF), or even a ZIP of images into ./consume/ and within seconds Paperless-ngx detects and begins processing it:

  1. File is detected by inotify (or polling on non-Linux systems)
  2. File is moved to a temporary processing directory
  3. OCR runs if the document lacks a text layer (or always, depending on PAPERLESS_OCR_MODE)
  4. Classification rules execute against the document
  5. Document is stored in the media directory
  6. Metadata is written to PostgreSQL
  7. The consume file is deleted (or moved to PAPERLESS_CONSUMPTION_DIR_TRASH_DIR)

Directory-Based Tag Assignment

You can pre-assign tags based on the subdirectory a file is placed in:

1
2
3
4
consume/
├── tax/           # documents dropped here get tagged "tax"
├── medical/       # tagged "medical"
└── receipts/      # tagged "receipts"

Enable this with:

1
2
PAPERLESS_CONSUMER_RECURSIVE=true
PAPERLESS_CONSUMER_SUBDIRS_AS_TAGS=true

This is useful when scanning from a device (like a Brother scanner) that can send to specific directories based on a scan profile.

Email Consumption

Paperless-ngx can pull documents from an IMAP mailbox — useful for e-bills and statements sent as PDF attachments:

1
2
# In .env
PAPERLESS_EMAIL_TASK_CRON=*/10 * * * *  # check every 10 minutes

Configure email accounts under Settings → Mail in the UI. Each mail rule specifies:

  • Which folder to watch
  • Which senders/subjects to match
  • What to do with the email after processing (delete, mark read, move)
  • Tags to assign to matched documents

For example: match all emails from billing@electricity-company.com, attach the PDF, apply tag “utilities”, and mark the email as read.

Organizing with Tags, Correspondents, and Document Types

Paperless-ngx has four classification dimensions:

Dimension Example Purpose
Tags tax, important, 2024 Flexible multi-label classification
Correspondent IRS, Blue Cross, State Farm Who sent the document
Document Type Invoice, Statement, Contract What kind of document
Storage Path Finance/Tax/{year} Where on disk to store it

The power comes from automation rules that assign these based on content matching.

Automation Rules

Navigate to Settings → Document Classification to create rules. Each rule has:

  • Name: descriptive label
  • Order: rules run in order; lower numbers first
  • Sources: apply to consumed documents, API uploads, or both
  • Filter: match on filename, correspondent, document type, tag, or full-text content
  • Assignment: what to set when the rule matches

Example: Auto-Tag Utility Bills

Rule: Electric Company Statements

  • Filter: match “from correspondents” → Pacific Gas & Electric
  • Assign tag: utilities
  • Assign document type: Statement
  • Assign storage path: Utilities/Electric/{created_year}

Rule: IRS Documents

  • Filter: match “if content contains” → Internal Revenue Service
  • Assign correspondent: IRS
  • Assign tag: tax
  • Assign document type: Government
  • Assign storage path: Tax/{created_year}

Example: Date Extraction

Paperless-ngx can extract the document date from content rather than using the file date:

1
PAPERLESS_DATE_ORDER=MDY  # US format: month/day/year

Alternatively, embed the date in the filename using the format YYYY-MM-DD:

2024-01-15 Electric Bill.pdf

Paperless-ngx will parse this automatically.

Custom Consumption Scripts

For advanced pre-processing, place scripts in the consume directory or use the PAPERLESS_PRE_CONSUME_SCRIPT and PAPERLESS_POST_CONSUME_SCRIPT environment variables:

1
2
PAPERLESS_PRE_CONSUME_SCRIPT=/usr/local/bin/pre-consume.sh
PAPERLESS_POST_CONSUME_SCRIPT=/usr/local/bin/post-consume.sh

The post-consume script receives the document ID as its first argument, allowing you to trigger webhooks, send notifications, or integrate with other systems:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
#!/usr/bin/env bash
# post-consume.sh — send a Pushover notification when a document is added
DOCUMENT_ID="$1"
DOCUMENT_TITLE="$2"  # passed as second arg

curl -s \
  --form-string "token=${PUSHOVER_TOKEN}" \
  --form-string "user=${PUSHOVER_USER}" \
  --form-string "message=New document added: ${DOCUMENT_TITLE}" \
  https://api.pushover.net/1/messages.json

Scanner Integration

Brother Scanners (Scan to FTP/Network)

Many Brother scanners support scanning to a network folder. Point it at your consume directory via SMB or NFS:

1
2
3
4
5
6
# On your homelab server — share the consume directory via Samba
[paperless-consume]
path = /path/to/consume
browseable = yes
writable = yes
valid users = scanner

Create scan profiles on the scanner for different destinations (e.g., “Tax Docs” → consume/tax/, “Medical” → consume/medical/).

iPhone/Android with Scanning Apps

Apps like Adobe Scan, Microsoft Lens, or Genius Scan can save directly to a network folder or cloud location. The workflow:

  1. Scan document with phone app
  2. App saves to Nextcloud/Dropbox folder
  3. Rclone or Nextcloud sync copies to consume directory
  4. Paperless-ngx processes automatically

Dedicated Scanning Hardware

A dedicated document scanner (ScanSnap, Fujitsu ix500, Brother ADS series) with automatic document feeder makes bulk scanning practical. Configure it to save to the consume directory directly over SMB.

For the ScanSnap series, the ScanSnap Cloud service can route documents to Dropbox/Google Drive, which you then sync to your consume directory.

Storage and File Organization

Filename Format

The PAPERLESS_FILENAME_FORMAT variable controls how archived files are named. Use these variables:

Variable Description
{title} Document title
{correspondent} Correspondent name
{document_type} Document type
{created} Full creation date (YYYY-MM-DD)
{created_year} Year only
{created_month} Month only
{added} Date added to Paperless-ngx
{asn} Archive serial number
{tags} Comma-separated tags

A useful format for tax documents:

1
PAPERLESS_FILENAME_FORMAT={created_year}/{correspondent}/{document_type}/{created}-{title}

This produces paths like:

2024/IRS/Tax Return/2024-04-15-1040 Tax Return.pdf

Archive Serial Numbers

Enable ASNs to give each document a permanent physical label:

1
PAPERLESS_FILENAME_FORMAT={asn}-{title}

Print the ASN on a sticky label and attach it to the physical document before filing. Later, searching by ASN instantly retrieves the digital version — useful for documents you need to keep physical copies of.

Storage Path Objects

Create named storage paths under Settings → Storage Paths. These support the same variables as PAPERLESS_FILENAME_FORMAT and can be assigned via automation rules:

  • Finance: Finance/{created_year}/{correspondent}/{title}
  • Medical: Medical/{created_year}/{correspondent}/{title}
  • Home: Home/{correspondent}/{created_year}/{title}

Documents without an assigned storage path use PAPERLESS_FILENAME_FORMAT as the fallback.

OCR Configuration

Language Configuration

For multilingual households or businesses:

1
2
# English + Spanish + French
PAPERLESS_OCR_LANGUAGE=eng+spa+fra

OCR language data must be installed. In the Docker image, additional languages are available as packages:

1
2
3
4
5
# In a custom Dockerfile extending paperless-ngx
RUN apt-get update && apt-get install -y \
    tesseract-ocr-deu \   # German
    tesseract-ocr-fra \   # French
    tesseract-ocr-spa     # Spanish

OCR Mode

Mode Behavior Use case
skip Skip OCR if text layer exists Fastest; use for already-searchable PDFs
skip_noarchive Skip OCR but still create archive copy Good default
redo Re-OCR everything regardless Fix existing documents
force Force OCR even with text layer Correct bad existing OCR
1
PAPERLESS_OCR_MODE=skip_noarchive

Image Preprocessing

For documents with skewed text or poor quality:

1
2
3
4
5
6
7
8
# Deskew pages before OCR
PAPERLESS_OCR_DESKEW=true

# Auto-rotate pages based on text orientation
PAPERLESS_OCR_ROTATE_PAGES=true

# Clean up noise in scanned images
PAPERLESS_OCR_CLEAN=clean  # or 'clean-final' for more aggressive

PDF/A Output

Converting documents to PDF/A ensures long-term archival compatibility:

1
PAPERLESS_OCR_OUTPUT_TYPE=pdfa-2

PDF/A embeds all fonts, color profiles, and metadata — documents remain readable without the original software that created them.

Backup Strategy

Document Export

The built-in export command creates a portable archive of all documents and metadata:

1
2
3
4
5
# Export everything to the ./export directory
docker compose exec webserver document_exporter ../export

# Export with checksums for verification
docker compose exec webserver document_exporter ../export --no-archive --no-thumbnail

The export includes:

  • All original files
  • A manifest.json with all metadata
  • Can be re-imported to a fresh Paperless-ngx instance

Database Backup

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#!/usr/bin/env bash
# backup-paperless.sh
set -euo pipefail

BACKUP_DIR="/backup/paperless"
DATE=$(date +%Y-%m-%d_%H-%M-%S)

mkdir -p "${BACKUP_DIR}"

# Backup PostgreSQL
docker compose exec -T db pg_dump \
  -U paperless \
  -d paperless \
  --format=custom \
  > "${BACKUP_DIR}/db_${DATE}.dump"

# Backup media files (the actual documents)
rsync -av --delete \
  /path/to/paperless/media/ \
  "${BACKUP_DIR}/media/"

# Backup configuration
cp /path/to/paperless/.env "${BACKUP_DIR}/env_${DATE}.bak"

# Remove backups older than 30 days
find "${BACKUP_DIR}" -name "db_*.dump" -mtime +30 -delete

echo "Backup complete: ${BACKUP_DIR}"

Add to cron:

1
0 2 * * * /usr/local/bin/backup-paperless.sh >> /var/log/paperless-backup.log 2>&1

Offsite Replication with Rclone

1
2
3
4
5
# Sync backups to S3 or Backblaze B2
rclone sync /backup/paperless b2:my-paperless-backup \
  --transfers 4 \
  --checksum \
  --log-file /var/log/rclone-paperless.log

Restoring from Backup

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# Start a fresh stack with empty volumes
docker compose up -d db broker

# Restore the database
docker compose exec -T db pg_restore \
  -U paperless \
  -d paperless \
  --clean \
  < /backup/paperless/db_2024-01-15_02-00-00.dump

# Restore media files
rsync -av /backup/paperless/media/ /path/to/paperless/media/

# Start the rest of the stack
docker compose up -d

Traefik Integration

For HTTPS via Traefik:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# docker-compose.yml (webserver service addition)
services:
  webserver:
    # ... existing config ...
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.paperless.rule=Host(`paperless.yourdomain.com`)"
      - "traefik.http.routers.paperless.entrypoints=websecure"
      - "traefik.http.routers.paperless.tls.certresolver=letsencrypt"
      - "traefik.http.services.paperless.loadbalancer.server.port=8000"
    # Remove the ports section when using Traefik

Remove the ports: mapping from the webserver service — Traefik handles routing.

Advanced Configuration

Custom Classifier Training

Paperless-ngx uses a Naive Bayes classifier to automatically suggest correspondents, document types, and tags based on document content. It trains automatically as you manually classify documents.

To improve accuracy:

  • Manually classify all existing documents consistently
  • Use specific correspondent names rather than generic ones (“Pacific Gas & Electric” not “Utility”)
  • Give the classifier time — it improves significantly after 50-100 classified documents

Trigger retraining manually:

1
docker compose exec webserver python manage.py document_create_classifier

API Access

The REST API enables programmatic access:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# Get auth token
curl -X POST http://localhost:8000/api/token/ \
  -H "Content-Type: application/json" \
  -d '{"username": "admin", "password": "yourpassword"}'

# List recent documents
curl -H "Authorization: Token YOUR_TOKEN" \
  "http://localhost:8000/api/documents/?ordering=-created&page_size=10" | jq '.results[].title'

# Upload a document via API
curl -X POST \
  -H "Authorization: Token YOUR_TOKEN" \
  -F "document=@/path/to/document.pdf" \
  -F "title=Electric Bill January 2024" \
  -F "correspondent=1" \
  http://localhost:8000/api/documents/post_document/

The API is useful for:

  • Integrating with home automation (Home Assistant can upload documents automatically)
  • Bulk uploading historical archives
  • Building custom mobile upload workflows

Webhook Notifications (via Post-Consume Script)

Send a notification to a Home Assistant webhook when a new document arrives:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
#!/usr/bin/env bash
# /usr/local/bin/paperless-post-consume.sh
DOCUMENT_ID="$1"
DOCUMENT_TITLE="$2"
DOCUMENT_TAGS="$3"

# Notify Home Assistant
curl -s -X POST \
  "http://homeassistant.local:8123/api/webhook/paperless-new-document" \
  -H "Content-Type: application/json" \
  -d "{\"document_id\": \"${DOCUMENT_ID}\", \"title\": \"${DOCUMENT_TITLE}\"}"
1
2
# In .env
PAPERLESS_POST_CONSUME_SCRIPT=/usr/local/bin/paperless-post-consume.sh

Full-Text Search Tuning

Paperless-ngx uses PostgreSQL’s full-text search. For better multilingual search, ensure you’ve set the right search stemming:

1
2
3
PAPERLESS_OCR_LANGUAGE=eng
# PostgreSQL uses the language setting to determine stemming rules
# English uses 'english' dictionary by default

For non-English primary languages, the full-text search configuration in PostgreSQL may need adjustment for optimal stemming.

Performance Tuning

Worker Configuration

OCR is CPU-intensive. Tune based on your hardware:

1
2
3
4
5
6
7
# For a 4-core machine running other services
PAPERLESS_TASK_WORKERS=2
PAPERLESS_THREADS_PER_WORKER=2

# For a dedicated server with 8 cores
PAPERLESS_TASK_WORKERS=4
PAPERLESS_THREADS_PER_WORKER=2

TASK_WORKERS controls how many documents are processed concurrently. Each worker can use multiple threads for page-parallel OCR.

Redis Memory

For large document collections, Redis may need more memory:

1
2
3
4
services:
  broker:
    image: redis:7
    command: redis-server --maxmemory 256mb --maxmemory-policy allkeys-lru

PostgreSQL Tuning

For collections over 10,000 documents:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
services:
  db:
    image: postgres:16
    command: >
      postgres
      -c shared_buffers=256MB
      -c effective_cache_size=1GB
      -c work_mem=16MB
      -c maintenance_work_mem=128MB
      -c max_connections=20

Migrating Existing Documents

Bulk Import

For importing an existing folder of PDFs:

1
2
3
4
5
# Copy files to the consume directory — Paperless-ngx processes them in order
cp -r /old/document/archive/* /path/to/paperless/consume/

# Watch progress
docker compose logs -f webserver | grep -E "(Consuming|OCR|Success)"

For very large archives, rate-limit the copy to avoid overwhelming the OCR queue:

1
2
3
4
# Copy 50 files, wait, then continue
find /old/archive -name "*.pdf" | head -50 | xargs -I{} cp {} /path/to/consume/
sleep 300  # wait 5 minutes for processing
# repeat...

From Evernote

Export notebooks from Evernote as .enex files, then use the evernote2paperless tool:

1
2
pip install evernote2paperless
evernote2paperless exported-notebook.enex --output /path/to/consume/

From DevonThink

Export as PDFs from DevonThink, then bulk-import. DevonThink exports preserve original file names which Paperless-ngx uses as initial titles before classification.

From a Folder Structure

If you have an existing Year/Category/Document.pdf structure, use a script to pre-tag based on path:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#!/usr/bin/env python3
"""
pre-tag-import.py — Import from folder structure with tags based on directory name
"""
import os
import shutil
from pathlib import Path

SOURCE = Path("/old/documents")
CONSUME = Path("/paperless/consume")

# Map directory names to subdirectories in consume (which become tags)
CATEGORY_MAP = {
    "Tax": "tax",
    "Medical": "medical",
    "Insurance": "insurance",
    "Bank": "finance",
    "Utilities": "utilities",
}

for src_file in SOURCE.rglob("*.pdf"):
    # Find which category this file belongs to
    for part in src_file.parts:
        if part in CATEGORY_MAP:
            dest_dir = CONSUME / CATEGORY_MAP[part]
            dest_dir.mkdir(parents=True, exist_ok=True)
            dest = dest_dir / src_file.name
            shutil.copy2(src_file, dest)
            print(f"Copied {src_file}{dest}")
            break
    else:
        # No matching category, put in root consume dir
        shutil.copy2(src_file, CONSUME / src_file.name)

Maintenance

Updating Paperless-ngx

1
2
3
4
5
6
7
8
# Pull new image
docker compose pull webserver

# Restart (migrations run automatically on startup)
docker compose up -d

# Watch logs for migration output
docker compose logs -f webserver | head -50

Always read the release notes before major version upgrades — some versions require manual migration steps.

Sanity Checks

1
2
3
4
5
6
7
8
9
# Check document count
docker compose exec webserver python manage.py document_sanity_checker

# Verify all documents have OCR text
docker compose exec db psql -U paperless -c \
  "SELECT count(*) FROM documents_document WHERE content = '' OR content IS NULL;"

# Rebuild search index if search seems broken
docker compose exec webserver python manage.py document_index reindex

Cleaning Up Orphaned Files

Occasionally files in the media directory may not correspond to database records:

1
2
# This command reports inconsistencies but doesn't fix them
docker compose exec webserver python manage.py document_sanity_checker --no-color 2>&1 | grep -i "warn\|error"

Security Considerations

Network Exposure

Paperless-ngx contains sensitive documents — do not expose it directly to the internet without authentication. Options:

  1. VPN-only access: Only accessible from Tailscale/WireGuard network
  2. Traefik with forward auth: Require Authentik/Authelia SSO before reaching Paperless-ngx
  3. Cloudflare Access: Put it behind Cloudflare Zero Trust

Admin Panel Security

The Django admin panel at /admin/ has broader database access than the Paperless-ngx UI. Restrict it:

1
2
PAPERLESS_DISABLE_REGULAR_LOGIN=false  # Keep Paperless login enabled
# Consider restricting /admin/ via Traefik middleware to trusted IPs only

Document Sensitivity Labels

Use tags like confidential, sensitive, or medical to identify high-sensitivity documents. Paperless-ngx doesn’t currently support per-document ACLs (beyond multi-user ownership in newer versions), but tags help you track what needs extra care.

Multi-User Support

Paperless-ngx v1.14+ supports proper multi-user access with ownership and permissions:

  • Documents can be owned by a specific user
  • Share documents with other users or groups
  • Set default visibility (owner-only vs. any logged-in user)

Configure under Settings → User Management.

The Workflow in Practice

After setup, the day-to-day workflow becomes:

  1. Receive paper document → scan with dedicated scanner or phone app → drops into consume directory automatically
  2. Receive email with PDF → Paperless-ngx mail rule picks it up → processed automatically
  3. Download PDF statement → save to consume directory or upload via web UI
  4. Find any document later → full-text search finds it in under a second

The classification rules do the heavy lifting. After training the classifier with a few hundred documents, new documents arrive pre-tagged with the right correspondent, type, and storage path — you just verify and click Save.

Practical Tips

Start with classification rules early: The more rules you define upfront, the less manual tagging you’ll do on bulk imports.

Use consistent correspondent names: “Pacific Gas & Electric” and “PG&E” are different correspondents to Paperless-ngx. Pick one and stick to it.

Tag with years for tax season: A 2024-taxes tag makes pulling all relevant documents for your accountant trivial.

Create a to-review tag: New documents that need attention get this tag automatically. Reviewing and removing it becomes a weekly habit.

Archive serial numbers for important documents: Anything you might need to physically retrieve (deeds, contracts, birth certificates) should have an ASN label so you can find both the physical and digital copies instantly.

Schedule regular exports: The built-in exporter creates a portable archive that can be read without Paperless-ngx. Run it monthly as insurance against data loss or migration needs.

Going paperless isn’t just about saving space — it’s about having instant access to any document ever received, fully text-searchable, with metadata that makes sense. Paperless-ngx makes that achievable on commodity homelab hardware with a scanning workflow that takes less time than filing the physical document ever did.

Comments