Remote & on-site IT support across Australia & New Zealand · 24/7 emergency line

Self-Hosted Paperless-ngx & Local AI

Paperless-ngx is a self-hosted document inbox that:

  • Ingests scans and PDFs (via upload, a “consume” folder, or even apps)
  • Runs OCR so documents become searchable
  • Organizes using tagscorrespondents, and document types
  • Lets you search your archive like a personal document index

If you want to keep personal paperwork (tax docs, medical, contracts, receipts) under your control, Paperless is the way to go.

Core Services
  • Paperless-ngx – the document scanning and index system
  • Postgres – database backend
  • Redis – queue/cache
  • Gotenberg – document conversion (Office/Excel → PDF, etc.)
  • Tika – text extraction helpers
  • Ollama – local model runtime
  • Open WebUI – model management + testing UI + chat
  • Paperless-AI – metadata suggestions (tags/titles/etc.)
  • Paperless-GPT – vision-model OCR + metadata suggestions
  • Dozzle – lightweight log viewer, highly recommended for container troubelshooting
Document Flow

Paperless-ngx ↔ (Paperless-AI / Paperless-GPT) ↔ Ollama

  • Paperless stores and indexes your docs.
  • Ollama runs the LLMs locally.
  • Paperless-AI / Paperless-GPT are add-ons that call Ollama and write results back to Paperless.
  • AI can enhance correspondents, tags, and text recognition (OCR)
Prerequisites
Ports Used in This Guide

This is the exact port map used in the stack below:

ServicePortURL
Paperless-ngx8000http://<server-ip>:8000
Paperless-AI3000http://<server-ip>:3000
Open WebUI3001http://<server-ip>:3001
Paperless-GPT3002http://<server-ip>:3002
Dozzle (logs)8080http://<server-ip>:8080
Install Nvida GPU
# Set Ubuntu version for Nvidia driver, there are no driver for 25.10 yet, use 24.04 instead
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | 
  sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg

echo "deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://nvidia.github.io/libnvidia-container/stable/deb/amd64 /" | 
  sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list

sudo apt update -y && sudo apt install -y nvidia-container-toolkit

# Configure Docker for Nvidia
sudo nvidia-ctk runtime configure --runtime=docker

# Restart Docker
sudo systemctl restart docker

# Check Docker see runtime
docker info | grep -i nvidia

# Check to see if Ubuntu see Nvidia GPU
lspci | grep -i nvidia

# Install the recommended driver, e.g.:
sudo apt install -y nvidia-driver-580-open

# Reboot Ubuntu
sudo reboot now

# Test Docker container for Nvidia driver
docker run -it --rm --gpus all ubuntu nvidia-smi
Create the Folder Structure
cd /opt

sudo mkdir -p 
  paperless/{data,media,export,consume,hooks,trash} 
  postgres/data 
  redis/data 
  gotenberg 
  tika 
  paperless-ai/data 
  open-webui/data 
  ollama/{data,models} 
  paperless-gpt/prompts 
  dozzle/data

sudo chown yourlogin:yourgroup 
  /opt/dozzle 
  /opt/gotenberg 
  /opt/ollama 
  /opt/open-webui 
  /opt/paperless 
  /opt/paperless-ai 
  /opt/paperless-gpt 
  /opt/postgres 
  /opt/redis 
  /opt/tika -R
Docker Compose File

Create a docker-compose.yml file in /opt and place the contents of the compose file:

# Docker Compose for Paperless-ngx
#version: "3.8"

services:
  broker:
    image: docker.io/library/redis:8
    command:
      - /bin/sh
      - -c
      - redis-server
    container_name: paperless-redis
    hostname: redis
#    mem_limit: 512m
#    mem_reservation: 256m
#    cpu_shares: 768
    security_opt:
      - no-new-privileges:true
    read_only: true
    user: 108:110
    healthcheck:
      test: ["CMD-SHELL", "redis-cli ping || exit 1"]
    volumes:
      - ./redis/data:/data
    environment:
      TZ: Australia/Sydney
    restart: unless-stopped

  # postgres database for paperless-ngx
  db:
    image: postgres:18
    container_name: paperless-db
    hostname: postgres
    user: 108:110
    security_opt:
      - no-new-privileges:true
    healthcheck:
#      test: ["CMD-SHELL", "pg_isready -U paperless"]
      test: ["CMD", "pg_isready", "-q", "-d", "paperless", "-U", "paperless"]
      timeout: 45s
      interval: 10s
      retries: 10
    restart: unless-stopped
    volumes:
      - ./postgres/data:/var/lib/postgresql
    environment:
      TZ: Australia/Sydney
      POSTGRES_DB: paperless
      POSTGRES_USER: paperless
      POSTGRES_PASSWORD: database_passwd

  paperless:
    # paperless-ngx main service
    image: ghcr.io/paperless-ngx/paperless-ngx:latest
    container_name: paperless-web
    hostname: paperless-ngx
    mem_limit: 10g
    cpu_shares: 1024
    security_opt:
      - no-new-privileges:true
    healthcheck:
      test: ["CMD", "curl", "-fs", "-S", "--max-time", "2", "http://localhost:8000"]
      interval: 30s
      timeout: 10s
      retries: 5
    restart: unless-stopped
    depends_on:
      broker:
        condition: service_healthy
      db:
        condition: service_healthy
      tika:
        condition: service_started
      gotenberg:
        condition: service_started
    ports:
      - "8010:8000"  # Web UI
    volumes:
      - ./data:/usr/src/paperless/data    # Application data
      - ./media:/usr/src/paperless/media  # Stored documents
      - ./export:/usr/src/paperless/export # Exported documents
      - ./consume:/usr/src/paperless/consume # Drop folder for new docs
      - ./trash:/usr/src/paperless/trash # Trash bin
      - ./hooks:/usr/src/paperless/scripts
    environment:
      PAPERLESS_REDIS: redis://redis:6379
      PAPERLESS_DBENGINE: postgres
      PAPERLESS_DBHOST: paperless-db
      PAPERLESS_DBNAME: paperless
      PAPERLESS_DBUSER: paperless
      PAPERLESS_DBPASS: database_passwd

      # Admin credentials for first login
      PAPERLESS_ADMIN_USER: web_username
      PAPERLESS_ADMIN_PASSWORD: web_passwd
      PAPERLESS_ADMIN_MAIL: admin_email

      USERMAP_UID: 108
      USERMAP_GID: 110

      # OCR language(s) - use ISO 639 codes
      # Install additional Tesseract language packs if needed
      PAPERLESS_OCR_LANGUAGE: eng
      # Multiple languages: PAPERLESS_OCR_LANGUAGE: eng+deu+fra

      # OCR mode: skip (only OCR non-searchable PDFs), redo, force
      PAPERLESS_OCR_MODE: skip

      # Time zone for correct date parsing
      PAPERLESS_TIME_ZONE: Australia/Sydney

      # URL - important for correct links in the UI
      PAPERLESS_URL: https://docs.trendsit.com
      PAPERLESS_CSRF_TRUSTED_ORIGINS: https://docs.trendsit.com
      PAPERLESS_ALLOWED_HOST: "localhost,10.254.254.30"

      # Secret key for Django security
      PAPERLESS_SECRET_KEY: your_own_secretkey

      # Worker settings
      PAPERLESS_TASK_WORKERS: 2
      PAPERLESS_THREADS_PER_WORKER: 2
      PAPERLESS_OCR_ROTATE_PAGES_THRESHOLD: 6

      # IMAP email consumption (optional)
      # PAPERLESS_EMAIL_TASK_CRON: "*/10 * * * *"

      PAPERLESS_EMPTY_TRASH_DIR: ../trash

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

      # Enable barcode processing for automatic document splitting
      PAPERLESS_CONSUMER_ENABLE_BARCODES: "true"
      PAPERLESS_CONSUMER_ENABLE_ASN_BARCODE: true     # If you would like QR-ASN-Codes 
#      PAPERLESS_CONSUMER_BARCODE_SCANNER: ZXING       ' ZXING as Scanner

      # Tika and Gotenberg for Office document support
      PAPERLESS_TIKA_ENABLED: 1
      PAPERLESS_TIKA_GOTENBERG_ENDPOINT: http://gotenberg:3000
      PAPERLESS_TIKA_ENDPOINT: http://tika:9998

  # Gotenberg converts Office documents to PDF
  gotenberg:
    image: docker.io/gotenberg/gotenberg:latest
    container_name: paperless-gotenberg
    hostname: gotenberg
    security_opt:
      - no-new-privileges:true
    user: 108:110
    command:
      - "gotenberg"
      - "--chromium-disable-javascript=true"
      - "--chromium-allow-list=file:///tmp/.*"
    environment:
      TZ: Australia/Sydney
    restart: unless-stopped

  # Tika extracts text from Office documents
  tika:
    image: docker.io/apache/tika:latest
    container_name: paperless-tika
    hostname: tika
    security_opt:
      - no-new-privileges:true
    user: 108:110
    environment:
      TZ: Australia/Sydney
    restart: unless-stopped

    # open-webui service for LLM interaction
  open-webui:
    image: ghcr.io/open-webui/open-webui:latest
    container_name: open-webui
    restart: unless-stopped
    environment:
      TZ: Australia/Sydney
      OLLAMA_BASE_URL: http://ollama:11434
    depends_on:
      - ollama
    ports:
      - "3001:8080"
    volumes:
      - ./open-webui/data:/app/backend/data

  # ollama service for local LLMs
  ollama:
    image: ollama/ollama:latest
    container_name: ollama
    environment:
      TZ: Australia/Sydney
      OLLAMA_KEEP_ALIVE: 15m
#      NVIDIA_DRIVER_CAPABILITIES=all # comment out if you aren't using NVIDIA GPUs
#      NVIDIA_VISIBLE_DEVICES=all # comment out if you aren't using NVIDIA GPUs
      OLLAMA_HOST: 0.0.0.0:11434
      OLLAMA_MODELS: ollama-models
    volumes:
      - ./ollama/data/:/root/.ollama
      - ./ollama/models:/ollama-models
    restart: unless-stopped
#    deploy:
#      resources:
#        reservations:
#          devices:
#            - driver: nvidia
#              count: all
#              capabilities: [gpu]

  # paperless-ai service
  paperless-ai:
    image: clusterzx/paperless-ai:latest
    container_name: paperless-ai
    restart: unless-stopped
    depends_on:
      - ollama
      - paperless
    ports:
      - "3000:3000"
    environment:
      TZ: Australia/Sydney
      PAPERLESS_API_URL: http://paperless:8000/api
      PAPERLESS_API_TOKENL: your_paperless_token # change to your paperless API token
      PAPERLESS_USERNAME: web_username # change to your paperless username
      AI_PROVIDER: ollama
      OLLAMA_API_URL: http://ollama:11434
      OLLAMA_MODEL: llama3.2:3b
      RAG_SERVICE_URL: http://localhost:8000
      RAG_SERVICE_ENABLED: true
      SCAN_INTERVAL: 30
      PAPERLESS_URL: http://paperless:8000
    volumes:
      - ./paperless-ai/data:/app/data

  # paperless-gpt service
  paperless-gpt:
    image: icereed/paperless-gpt:latest
    container_name: paperless-gpt
    restart: unless-stopped
    depends_on:
      - ollama
      - paperless
    ports:
      - "3002:8080"
    environment:
      TZ: Australia/Sydney
      PAPERLESS_BASE_URL: "http://paperless:8000"
      PAPERLESS_API_TOKEN: your_api_token  # change to your paperless API token
      # PAPERLESS_PUBLIC_URL: "http://paperless.mydomain.com" # Optional

      LLM_PROVIDER: "ollama"
      LLM_MODEL: "llama3.2:3b"
      OLLAMA_HOST: "http://ollama:11434"
      OLLAMA_CONTEXT_LENGTH: "8192" # Sets Ollama NumCtx (context window)
      TOKEN_LIMIT: 1000 # Recommended for smaller models

      LLM_LANGUAGE: "English" # Optional, default: English

      OCR_PROVIDER: "llm" # Default OCR provider
      VISION_LLM_PROVIDER: "ollama" # openai, ollama, mistral, or anthropic
      VISION_LLM_MODEL: "minicpm-v:8b" # minicpm-v (ollama) or gpt-4o (openai) or claude-sonnet-4-5 (anthropic/claude)

      AUTO_OCR_TAG: "paperless-gpt-ocr-auto"
      AUTO_TAG: "paperless-gpt-auto"
      MANUAL_TAG: "paperless-gpt-manual"
      PDF_OCR_TAGGING: "true"
      PDF_OCR_COMPLETE_TAG: "paperless-gpt-ocr-complete"
      PDF_UPLOAD: "false"

      LOG_LEVEL: "DEBUG"
    volumes:
      - ./paperless-gpt/prompts:/app/prompts

  # optional but helpful log viewer
  dozzle:
    image: amir20/dozzle:latest
    restart: unless-stopped
    container_name: dozzle
    environment:
      TZ: Australia/Sydney
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./dozzle/data:/data
    ports:
      - "8080:8080"
Start Everything

Inside of the folder with your compose file:

docker compose up -d
Open Firewall Ports
sudo ufw allow 8000/tcp
sudo ufw allow 3000/tcp
sudo ufw allow 3001/tcp
sudo ufw allow 3002/tcp
sudo ufw allow 8080/tcp
sudo ufw allow 11434/tcp

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top