# How to self-host Paperless-ngx with Docker Compose

> Set up Paperless-ngx with Docker Compose, Postgres and Redis: secret key, OCR languages, consume folder, HTTPS, backups with the exporter and upgrades.

## Key facts

| Fact | Value |
|---|---|
| App | Paperless-ngx (https://appsgit.com/apps/paperless-ngx) |
| Difficulty | beginner |
| Time | about 20 minutes |
| Requirements | 2 vCPU / 2 GB RAM (4 GB recommended for OCR); Docker + Docker Compose v2; Disk space for scanned documents; A domain name (optional, for HTTPS) |
| Last updated | 2026-10-06 |

## What is Paperless-ngx?

Paperless-ngx is a document management system that scans, OCRs, indexes and archives your paper documents so you can search them like email. It is open source under the GPL-3.0 license and is the community-maintained successor to Paperless and Paperless-ng. It learns how to tag, file and date incoming documents automatically, and stores everything as searchable PDF/A.

## Requirements

- A Linux server with 2 vCPU and 2 GB of RAM. OCR is CPU-heavy, so 4 GB and more cores speed up large imports.
- Docker Engine and Docker Compose v2.
- Disk space for originals plus archived PDF/A copies.

## Step 1: Prepare the server

This guide assumes Ubuntu 24.04 with Docker installed. If you need Docker, see the [official install guide](https://docs.docker.com/engine/install/ubuntu/). Create a project folder with the directories Paperless uses for import and export:

```bash
mkdir -p ~/paperless/{consume,export} && cd ~/paperless
id -u && id -g
```

Note your user ID and group ID. Paperless will run with them so you can drop files into `consume` without permission problems.

## Step 2: Create the Docker Compose file

The official stack uses Postgres as the database and Redis (or Valkey) as the task broker. Save this as `docker-compose.yml`:

```yaml
services:
  broker:
    image: docker.io/valkey/valkey:8-alpine
    restart: unless-stopped
    volumes:
      - redisdata:/data

  db:
    image: docker.io/library/postgres:16-alpine
    restart: unless-stopped
    environment:
      POSTGRES_DB: paperless
      POSTGRES_USER: paperless
      POSTGRES_PASSWORD: ${POSTGRES_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
    environment:
      PAPERLESS_REDIS: redis://broker:6379
      PAPERLESS_DBHOST: db
      PAPERLESS_DBENGINE: postgresql
      PAPERLESS_DBPASS: ${POSTGRES_PASSWORD}
      PAPERLESS_SECRET_KEY: ${PAPERLESS_SECRET_KEY}
      PAPERLESS_URL: https://docs.example.com
      PAPERLESS_TIME_ZONE: Europe/London
      PAPERLESS_OCR_LANGUAGE: eng
      USERMAP_UID: 1000
      USERMAP_GID: 1000

volumes:
  data:
  media:
  pgdata:
  redisdata:
```

Create `.env` in the same folder:

```bash
POSTGRES_PASSWORD=CHANGE_ME
PAPERLESS_SECRET_KEY=CHANGE_ME
```

Generate each value with `openssl rand -hex 32`. Set `USERMAP_UID` and `USERMAP_GID` to the numbers from Step 1, and `PAPERLESS_URL` to the exact public URL you will use. For OCR in several languages, join codes with a plus sign, for example `deu+eng`. To pin a release instead of `latest`, use a version tag from the [releases page](https://github.com/paperless-ngx/paperless-ngx/releases).

## Step 3: Start and open the app

```bash
docker compose up -d
docker compose run --rm webserver createsuperuser
```

The second command prompts for an admin username, email and password. Then open `http://YOUR_SERVER_IP:8000` and sign in. To test the pipeline, copy a PDF into `~/paperless/consume`: within a minute Paperless picks it up, runs OCR and shows it in the document list. You can also upload through the web UI or set up mail rules to fetch attachments from an inbox.

## Step 4: Put it behind HTTPS

With [Caddy](https://caddyserver.com/docs/) on the host:

```caddyfile
docs.example.com {
    request_body {
        max_size 200MB
    }
    reverse_proxy 127.0.0.1:8000
}
```

Make sure `PAPERLESS_URL` matches this domain exactly, including `https://`. The UI uses WebSockets for live upload status, which Caddy passes automatically. With Nginx Proxy Manager, enable "Websockets Support". Once HTTPS works, change the port mapping to `"127.0.0.1:8000:8000"`.

## Backups and upgrades

The most reliable backup is the built-in document exporter, which writes originals, archived PDFs and all metadata to the `export` folder:

```bash
docker compose exec -T webserver document_exporter ../export
```

Copy `~/paperless/export` off-site afterwards, encrypted. Also keep your `.env`. The exporter output can be re-imported into a fresh install with `document_importer`.

To upgrade, back up first, read the release notes, then run:

```bash
docker compose pull && docker compose up -d
```

Database migrations run automatically on start.

## Troubleshooting

- **"CSRF verification failed" when logging in:** `PAPERLESS_URL` does not match the address in your browser. Fix it and recreate the container.
- **Files in `consume` are ignored:** check permissions (the folder must be writable by `USERMAP_UID`). On network shares, set `PAPERLESS_CONSUMER_POLLING=30`, because inotify events do not cross NFS or SMB.
- **OCR fails with a language error:** the language code is not installed. Add it to `PAPERLESS_OCR_LANGUAGES` so the container installs it on start.
- **Imports are slow:** add CPU cores, or lower OCR work with `PAPERLESS_OCR_MODE=skip` for documents that already contain text.

## Next steps

Create correspondents, document types and tags, train the automatic matching, add a third-party mobile app for scanning on the go, and schedule the exporter with cron.

## FAQ

### What port does Paperless-ngx use?

The Paperless-ngx web server listens on port 8000. You open it at http://SERVER_IP:8000, or through a reverse proxy on your own domain.

### Is Paperless-ngx free?

Yes. Paperless-ngx is free and open source under the GPL-3.0 license and is maintained by a community team.

### What is PAPERLESS_SECRET_KEY?

It is the key Django uses to sign session cookies and tokens. Set a long random value, keep it stable, and never share it, because anyone who knows it can forge sessions.

### How do I add OCR languages to Paperless-ngx?

Set PAPERLESS_OCR_LANGUAGE to the Tesseract codes your documents use, for example deu+eng. Languages not bundled in the image can be installed at start with PAPERLESS_OCR_LANGUAGES.

### Why do I need PAPERLESS_URL?

Paperless-ngx uses it for CSRF protection and allowed hosts. Without it, logins through a reverse proxy on your domain fail with a CSRF verification error.

### Paperless-ngx vs Docspell?

Both are self-hosted document management systems with OCR. Paperless-ngx has the larger community, a polished web UI and several third-party mobile apps, while Docspell focuses on multi-user collectives and flexible custom metadata.

Prefer not to do it yourself? [appsgit installation help](https://appsgit.com/services/install) installs it on your server for a fixed quote.

---

Canonical page: https://appsgit.com/guides/paperless-ngx
Source: appsgit (https://appsgit.com), the app store for github. Data from the GitHub API, refreshed nightly.
Machine access: JSON API https://appsgit.com/api/v1/apps (OpenAPI: https://appsgit.com/openapi.json), MCP server https://mcp.appsgit.com/mcp, full index https://appsgit.com/llms-full.txt.
