Wiki

Docker Compose Configuration

# docker-compose.yaml
networks:
  paperless:
    external: false

volumes:
  /Daten/docker/paperless:

services:
  init-fs:
    image: busybox:latest
    container_name: paperless-init
    # Mount the exact same root volume as your paperless container
    volumes:
      - C:/Daten/docker/paperless:/paperless
    # Command: Create folders AND fix permissions (UID 1000 is standard for paperless)
    command: >
      sh -c "mkdir -p /paperless/data /paperless/media /paperless/consume /paperless/export /paperless/trash /paperless/log /paperless/static
      && chown -R 1000:1000 /paperless/data /paperless/media /paperless/consume /paperless/export /paperless/trash /paperless/log /paperless/static"
  paperless:
    image: ghcr.io/paperless-ngx/paperless-ngx:latest
    restart: unless-stopped
    depends_on:
      init-fs:
        condition: service_completed_successfully
      redis:
        condition: service_started
      gotenberg:
        condition: service_started
      tika:
        condition: service_started
    ports:
      - "8000:8000"
    networks:
      - paperless
    volumes:
      # Single mount point - backup this one folder
      - /Daten/docker/paperless:/paperless
    environment:
      # === Path Configuration (all under /paperless) ===
      PAPERLESS_DATA_DIR: /paperless/data
      PAPERLESS_MEDIA_ROOT: /paperless/media # Defaults to "../media/", relative to the "src" directory.
      PAPERLESS_CONSUMPTION_DIR: /paperless/consume
      PAPERLESS_LOGGING_DIR: /paperless/log # default to PAPERLESS_DATA_DIR/log/ # PAPERLESS_DATA_DIR/../log/
      PAPERLESS_STATICDIR: /paperless/static # Defaults to "../static/", relative to the "src" directory.
      PAPERLESS_EMPTY_TRASH_DIR: /paperless/trash
      PAPERLESS_EXPORT_DIR: /paperless/export

      # === Localisation ===
      PAPERLESS_FILENAME_FORMAT: ${PAPERLESS_FILENAME_FORMAT} # "{{ correspondent }}/{{ custom_fields|get_cf_value('Dossier Nr', 'nicht zugewiesen') }}_{{ doc_pk }}"
      PAPERLESS_TIME_ZONE: ${PAPERLESS_TIME_ZONE}
      PAPERLESS_DATE_PARSER_LANGUAGES: en+fr+de-CH
      PAPERLESS_OCR_LANGUAGE: deu+eng+fra
      PAPERLESS_MAX_IMAGE_PIXELS: "89478485" # allows up to 250 MB Images
      PAPERLESS_OCR_MAX_IMAGE_PIXELS: "89478485" # allows up to 250 MB Images


      # === Proxy settings ===
      PAPERLESS_USE_X_FORWARD_HOST: "true" # true when proxied
      PAPERLESS_USE_X_FORWARD_PORT: "true" # true when proxied
      PAPERLESS_URL: ${PAPERLESS_PUBLIC_URL} # set public URL of service
      PAPERLESS_TRUSTED_PROXIES: ${PAPERLESS_TRUSTED_PROXIES} # <comma-separated-list> ip of proxy
      # PAPERLESS_ALLOWED_HOSTS: # allow only specific host
      PAPERLESS_CSRF_TRUSTED_ORIGINS: ${PAPERLESS_PUBLIC_URL}
      PAPERLESS_COOKIE_SECURE: true
      PAPERLESS_SECURE_PROXY_SSL_HEADER: HTTP_X_FORWARDED_PROTO,https

      # === Security ===
      PAPERLESS_ADMIN_USER: ${PAPERLESS_ADMIN_USER} # overwrite the admin
      PAPERLESS_ADMIN_PASSWORD: ${PAPERLESS_ADMIN_PASSWORD}  # set the admin password
      PAPERLESS_ADMIN_MAIL: ${PAPERLESS_ADMIN_MAIL} # overwrite default: root@localhost
      PAPERLESS_SECRET_KEY: ${PAPERLESS_SECRET_KEY}  # from .env file NEED TO CHANGE

      # === Database ===
      PAPERLESS_DBHOST: ""  # empty = sqlite at data/db.sqlite3, or = PostgreSQL / MariaDB
      # PAPERLESS_DBPASS=<password> # Only when using postgres, default is sqlite, NEED TO CHANGE, default paperless

      # === External services ===
      PAPERLESS_REDIS: redis://redis:6379 # defaults to redis://localhost:6379
      PAPERLESS_TIKA_ENABLED: 1 # True for parsing of .doc, xlsx ...
      PAPERLESS_TIKA_ENDPOINT: "http://tika:9998" # Defaults to "http://localhost:9998".
      PAPERLESS_TIKA_GOTENBERG_ENDPOINT: "http://gotenberg:3000" # Defaults to "http://localhost:3000".

      # === Email ===
      PAPERLESS_EMAIL_TASK_CRON: "*/10 * * * *" # default every 10min: */10 * * * *
      PAPERLESS_MAIL_IMAP_CHARSET: "US-ASCII"
      PAPERLESS_OAUTH_CALLBACK_BASE_URL: ${PAPERLESS_OAUTH_CALLBACK_BASE_URL}
      PAPERLESS_OUTLOOK_OAUTH_CLIENT_ID: ${PAPERLESS_OUTLOOK_OAUTH_CLIENT_ID}
      PAPERLESS_OUTLOOK_OAUTH_CLIENT_SECRET: ${PAPERLESS_OUTLOOK_OAUTH_CLIENT_SECRET}
      # PAPERLESS_EMAIL_CERTIFICATE_LOCATION: /paperless/certs/mail.crt

  redis:
    image: redis:latest
    volumes:
      - /Daten/docker/paperless/redis:/data
    networks:
      - paperless
  gotenberg:
    image: docker.io/gotenberg/gotenberg:8.20
    restart: unless-stopped
    networks:
      - paperless
    # The gotenberg chromium route is used to convert .eml files. We do not
    # want to allow external content like tracking pixels or even javascript.
    command:
      - "gotenberg"
      - "--chromium-disable-javascript=true"
      - "--chromium-allow-list=file:///tmp/.*"
  tika:
    image: docker.io/apache/tika:latest
    restart: unless-stopped
    networks:
      - paperless
  caddy:
    image: caddy:2
    container_name: caddy
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /Daten/docker/paperless/config/Caddyfile:/etc/caddy/Caddyfile
      - /Daten/docker/paperless/config/caddy-data:/data
      - /Daten/docker/paperless/config/caddy-config:/config
      - /Daten/docker/paperless/config/certs/paperless.crt:/etc/ssl/paperless.crt:ro
      - /Daten/docker/paperless/config/certs/paperless.key:/etc/ssl/paperless.key:ro
    networks:
      - paperless
# Caddyfile
https://paperless.staudtag.local {
    tls /etc/ssl/paperless.crt /etc/ssl/paperless.key
    reverse_proxy webserver:8000 {
        header_up X-Forwarded-Proto {scheme}
        header_up Host {host}
    }
}

Resulting Host Structure

/Daten/docker/paperless/
├── data/           # DB, index, model
│   └── db.sqlite3
├── media/          # Your documents
│   ├── documents/
│   │   ├── originals/
│   │   └── archive/
│   └── thumbnails/
├── consume/        # Import drop folder
├── log/            # Logs
├── trash/          # Soft-deleted docs
├── export/         # Exports
└── redis/          # Redis persistence

Current Mode Dev STAUDT