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
- Open
http://your-server:7880in a browser - Click “Sign up” and enter the invite password you set in
DOCSPELL_SERVER_BACKEND_SIGNUP_NEW__INVITE__PASSWORD - Create your account with a collective name (your workspace), username, and password
- 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:
| Mode | Behavior |
|---|---|
open | Anyone can sign up — not recommended for internet-facing servers |
invite | Users need the invite password to register |
closed | No 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 key | Environment variable |
|---|---|
docspell.server.bind.address | DOCSPELL_SERVER_BIND_ADDRESS |
docspell.server.app-name | DOCSPELL_SERVER_APP__NAME |
docspell.server.full-text-search.enabled | DOCSPELL_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:
| Data | Location | Priority |
|---|---|---|
| PostgreSQL database | docspell_db volume | Critical — all metadata, tags, user accounts |
| Solr index | docspell_solr volume | Rebuildable — can re-index from database |
| Uploaded documents | ./docspell-docs directory | Critical — 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
| Resource | Minimum | Recommended |
|---|---|---|
| RAM | 4 GB | 6 GB |
| CPU | 2 cores | 4 cores |
| Disk | 5 GB (app) + document storage | 10 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.
Related
Get self-hosting tips in your inbox
Get the Docker Compose configs, hardware picks, and setup shortcuts we don't put in articles. Weekly. No spam.
Comments