How to Self-Host Docspell with Docker Compose

What Is Docspell?

Docspell is a self-hosted personal document organizer that ingests PDFs, images, and email attachments, runs OCR, and makes everything searchable. It splits into two components — a REST server for the web UI and API, and joex for background processing (OCR, machine learning, email import). Unlike simpler alternatives, Docspell supports multi-user “collectives” for shared document access and can poll IMAP mailboxes to automatically extract and file email attachments.

Prerequisites

  • A Linux server (Ubuntu 22.04+ recommended)
  • Docker and Docker Compose installed (guide)
  • 4 GB of free RAM (JVM-based — needs more than Python alternatives)
  • 5 GB of free disk space (plus storage for documents)
  • A domain name (optional, for remote access)

Docker Compose Configuration

Docspell requires five services: the REST server (web UI), joex (job executor for OCR and processing), PostgreSQL, Apache Solr (full-text search), and optionally a consumedir watcher for auto-uploading files from a directory.

Create a docker-compose.yml file:

services:
  restserver:
    image: ghcr.io/docspell/restserver:v0.43.0
    container_name: docspell-restserver
    hostname: docspell-restserver
    restart: unless-stopped
    ports:
      - "7880:7880"
    environment:
      TZ: "UTC"
      # Bind to all interfaces inside the container
      DOCSPELL_SERVER_BIND_ADDRESS: "0.0.0.0"
      # Internal URL (other services use this to reach restserver)
      DOCSPELL_SERVER_INTERNAL__URL: "http://docspell-restserver:7880"
      # CHANGE THIS — secret for admin API endpoints
      DOCSPELL_SERVER_ADMIN__ENDPOINT_SECRET: "change-this-admin-secret"
      # Leave blank for auto-generated auth tokens
      DOCSPELL_SERVER_AUTH_SERVER__SECRET: ""
      # Database connection — must match PostgreSQL credentials below
      DOCSPELL_SERVER_BACKEND_JDBC_URL: "jdbc:postgresql://docspell-db:5432/docspell"
      DOCSPELL_SERVER_BACKEND_JDBC_USER: "docspell"
      DOCSPELL_SERVER_BACKEND_JDBC_PASSWORD: "change-this-db-password"
      # Full-text search via Solr
      DOCSPELL_SERVER_FULL__TEXT__SEARCH_ENABLED: "true"
      DOCSPELL_SERVER_FULL__TEXT__SEARCH_SOLR_URL: "http://docspell-solr:8983/solr/docspell"
      # Signup mode: open, closed, or invite
      DOCSPELL_SERVER_BACKEND_SIGNUP_MODE: "invite"
      # CHANGE THIS — invite password for new user registration
      DOCSPELL_SERVER_BACKEND_SIGNUP_NEW__INVITE__PASSWORD: "change-this-invite-password"
      # Integration endpoint for consumedir auto-upload
      DOCSPELL_SERVER_INTEGRATION__ENDPOINT_ENABLED: "true"
      DOCSPELL_SERVER_INTEGRATION__ENDPOINT_HTTP__HEADER_ENABLED: "true"
      # CHANGE THIS — must match the consumedir header value below
      DOCSPELL_SERVER_INTEGRATION__ENDPOINT_HTTP__HEADER_HEADER__VALUE: "change-this-integration-secret"
    depends_on:
      docspell-db:
        condition: service_healthy
      docspell-solr:
        condition: service_healthy

  joex:
    image: ghcr.io/docspell/joex:v0.43.0
    container_name: docspell-joex
    hostname: docspell-joex
    restart: unless-stopped
    environment:
      TZ: "UTC"
      DOCSPELL_JOEX_APP__ID: "joex1"
      DOCSPELL_JOEX_PERIODIC__SCHEDULER_NAME: "joex1"
      DOCSPELL_JOEX_SCHEDULER_NAME: "joex1"
      DOCSPELL_JOEX_BASE__URL: "http://docspell-joex:7878"
      DOCSPELL_JOEX_BIND_ADDRESS: "0.0.0.0"
      # Database connection — MUST match restserver credentials
      DOCSPELL_JOEX_JDBC_URL: "jdbc:postgresql://docspell-db:5432/docspell"
      DOCSPELL_JOEX_JDBC_USER: "docspell"
      DOCSPELL_JOEX_JDBC_PASSWORD: "change-this-db-password"
      # Full-text search — MUST match restserver Solr config
      DOCSPELL_JOEX_FULL__TEXT__SEARCH_ENABLED: "true"
      DOCSPELL_JOEX_FULL__TEXT__SEARCH_SOLR_URL: "http://docspell-solr:8983/solr/docspell"
      # HTML to PDF converter
      DOCSPELL_JOEX_CONVERT_HTML__CONVERTER: "weasyprint"
    depends_on:
      docspell-db:
        condition: service_healthy
      docspell-solr:
        condition: service_healthy

  consumedir:
    image: docspell/dsc:v0.11.0
    container_name: docspell-consumedir
    restart: unless-stopped
    command:
      - dsc
      - "-d"
      - "http://docspell-restserver:7880"
      - "watch"
      - "--delete"
      - "-ir"
      - "--not-matches"
      - "**/.*"
      - "--header"
      # CHANGE THIS — must match restserver integration header value
      - "Docspell-Integration:change-this-integration-secret"
      - "/opt/docs"
    volumes:
      - ./docspell-docs:/opt/docs
    depends_on:
      - restserver

  docspell-db:
    image: postgres:16-alpine
    container_name: docspell-db
    restart: unless-stopped
    environment:
      POSTGRES_USER: "docspell"
      POSTGRES_PASSWORD: "change-this-db-password"
      POSTGRES_DB: "docspell"
    volumes:
      - docspell_db:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U docspell"]
      interval: 10s
      timeout: 5s
      retries: 5

  docspell-solr:
    image: solr:9
    container_name: docspell-solr
    restart: unless-stopped
    volumes:
      - docspell_solr:/var/solr
    command:
      - bash
      - -c
      - "precreate-core docspell; exec solr-foreground"
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8983/solr/docspell/admin/ping"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 30s

volumes:
  docspell_db:
  docspell_solr:

Three values must match across services:

  • Database password — same in restserver, joex, and PostgreSQL
  • Solr URL — same in restserver and joex
  • Integration header — same in restserver and consumedir

Start the stack:

docker compose up -d

Initial Setup

  1. Open http://your-server:7880 in a browser
  2. Click “Sign up” and enter the invite password you set in DOCSPELL_SERVER_BACKEND_SIGNUP_NEW__INVITE__PASSWORD
  3. Create your account with a collective name (your workspace), username, and password
  4. Log in and start uploading documents

The collective concept is Docspell’s multi-user model — users within the same collective share documents. Create separate collectives for separate document sets.

Configuration

Signup Modes

Control who can register accounts:

ModeBehavior
openAnyone can sign up — not recommended for internet-facing servers
inviteUsers need the invite password to register
closedNo new signups — manage users via admin API

Email Import (IMAP)

Docspell can poll IMAP mailboxes and automatically import email attachments. Configure through the web UI: User Settings → Email Settings → IMAP. Provide your IMAP server, credentials, folder, and polling interval. Docspell downloads attachments from new emails and processes them through the OCR pipeline.

Admin Endpoint

Access admin operations at http://your-server:7880/api/v1/admin/ using the DOCSPELL_SERVER_ADMIN__ENDPOINT_SECRET. This endpoint handles user management, re-indexing, and system health checks.

Advanced Configuration

Addon System

Docspell supports addons for extending processing — but enabling them requires binding the Docker socket into the joex container:

  joex:
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - /tmp:/tmp
    environment:
      DOCSPELL_JOEX_ADDONS_EXECUTOR__CONFIG_RUNNER: "docker,trivial"

This grants joex container management access. Only enable this if you trust the addons you install.

JVM Memory Tuning

For processing large documents or high volumes, increase joex’s JVM heap:

  joex:
    command:
      - -J-Xmx3G

The default (~1 GB) works for personal use. Bump to 2-3 GB for larger document libraries.

Environment Variable Naming

Docspell uses a specific naming convention: config keys with dots become underscores, and hyphens become double underscores. Examples:

Config keyEnvironment variable
docspell.server.bind.addressDOCSPELL_SERVER_BIND_ADDRESS
docspell.server.app-nameDOCSPELL_SERVER_APP__NAME
docspell.server.full-text-search.enabledDOCSPELL_SERVER_FULL__TEXT__SEARCH_ENABLED

Reverse Proxy

Nginx Proxy Manager or Caddy in front of port 7880. Only the restserver port needs to be exposed — joex, Solr, and PostgreSQL stay internal.

For Caddy:

docspell.example.com {
    reverse_proxy localhost:7880
}

See the Reverse Proxy Setup guide for detailed configuration.

Backup

Critical data to back up:

DataLocationPriority
PostgreSQL databasedocspell_db volumeCritical — all metadata, tags, user accounts
Solr indexdocspell_solr volumeRebuildable — can re-index from database
Uploaded documents./docspell-docs directoryCritical — original files
# Database backup
docker exec docspell-db pg_dump -U docspell docspell > docspell_backup.sql

# Volume backup
docker run --rm -v docspell_db:/data -v $(pwd):/backup alpine tar czf /backup/docspell-db-volume.tar.gz /data

See the Backup Strategy guide for automated backup workflows.

Troubleshooting

Documents Not Being Processed

Symptom: Uploaded documents stay in “waiting” state. Fix: Check that the joex container is running and can connect to PostgreSQL and Solr. Verify that database credentials match between restserver and joex. Check joex logs: docker logs docspell-joex.

Consumedir Not Uploading Files

Symptom: Files placed in ./docspell-docs are not imported. Fix: The integration header value in the consumedir command must exactly match DOCSPELL_SERVER_INTEGRATION__ENDPOINT_HTTP__HEADER_HEADER__VALUE in the restserver config. Check consumedir logs: docker logs docspell-consumedir.

Solr Full-Text Search Not Working

Symptom: Search returns no results despite processed documents. Fix: Verify the Solr core exists: curl http://localhost:8983/solr/docspell/admin/ping. If the core is missing, restart the Solr container — the precreate-core docspell command runs on startup. Trigger a re-index via the admin API.

High Memory Usage

Symptom: Joex container consumes 2+ GB of RAM during processing. Fix: This is expected for JVM applications during OCR processing. Limit joex heap with command: ["-J-Xmx2G"] to cap memory usage. For lower-memory systems, reduce parallel processing jobs in joex configuration.

Resource Requirements

ResourceMinimumRecommended
RAM4 GB6 GB
CPU2 cores4 cores
Disk5 GB (app) + document storage10 GB + document storage

The JVM-based architecture means Docspell is significantly heavier than Python alternatives like Paperless-ngx. Plan for 4 GB RAM minimum across all five containers.

Verdict

Docspell fills a specific niche: multi-user document management with email import and Solr-powered search. If you need shared document workspaces (collectives) or automatic IMAP attachment import, Docspell handles both well. The OCR pipeline is solid and the web UI is functional if not flashy.

For single-user document management, Paperless-ngx is the better choice — it’s lighter, simpler, has better mobile apps, and a much larger community. Docspell’s five-service stack and JVM memory requirements are only justified if you actually need its collaboration or email features.

FAQ

How does Docspell compare to Paperless-ngx?

Docspell adds multi-user collectives and IMAP email import that Paperless-ngx lacks, but requires 4 GB RAM vs 2 GB and five Docker services vs three. See our Paperless-ngx vs Docspell comparison.

Can Docspell replace Google Drive?

For document storage and search, yes. Docspell excels at organizing, tagging, and searching scanned documents. It does not handle file sync, collaborative editing, or real-time sharing — for that, see Nextcloud or Seafile.

Does Docspell support multiple languages for OCR?

Yes. Tesseract supports 100+ languages. Configure additional language packs in the joex container. The default image includes English and German.

Comments