---
title: "Paperless-NGX — docker exec Root-Owned Temp Dirs Break Mail Fetcher"
aliases: [paperless-docker-exec-permission, paperless-scratch-dir, paperless-mail-fetcher-permission]
tags: [paperless-ngx, docker, permissions, celery, homelab, selfhosted]
sources:
  - "daily/2026-05-01.md"
created: 2026-05-01
updated: 2026-05-01
---

# Paperless-NGX — docker exec Root-Owned Temp Dirs Break Mail Fetcher

Running `docker exec paperless-ngx ...` without a `-u paperless` flag executes as root. Python's `tempfile.mkdtemp()` creates temp directories with mode 700 owned by the current user (root). The Paperless celery worker runs as UID 1000 and cannot read or write those root-owned directories — resulting in a `PermissionError` that looks like a genuine mail fetcher bug but is actually a testing artefact.

## Key Points

- **`docker exec` without `-u` runs as root** — even if the container normally runs as a non-root user
- **`tempfile.mkdtemp()` creates dirs with mode 700** — owned by whoever called it; root-owned dirs are inaccessible to the celery worker (UID 1000)
- **The PermissionError is a false positive** — automatic celery-beat runs work fine because they run as the correct user; only manual `docker exec` testing triggers it
- **`PAPERLESS_SCRATCH_DIR`** (not `PAPERLESS_TEMP_DIR`) is the correct env var for mail attachment temp dirs — must point to a volume with mode 1777
- **Removing `USERMAP_UID`/`USERMAP_GID`** env vars avoids UID remapping conflicts

## Details

### The False Positive

The standard debugging approach of running `docker exec paperless-ngx python manage.py mail_fetcher` triggers the bug:

```bash
# ❌ Runs as root — creates root-owned 700 temp dirs → PermissionError
docker exec paperless-ngx python manage.py mail_fetcher

# ✅ Correct — runs as the service user
docker exec -u paperless paperless-ngx python manage.py mail_fetcher
```

The automatic celery-beat task runs as the paperless user and does not hit this issue.

### The Real Fix

```yaml
# docker-compose.yml
services:
  paperless-ngx:
    environment:
      - PAPERLESS_SCRATCH_DIR=/paperless-tmp   # mail attachment temp dir
      # Remove or don't set USERMAP_UID / USERMAP_GID
    volumes:
      - paperless-tmp:/paperless-tmp           # must be mode 1777

volumes:
  paperless-tmp:
    driver: local
    driver_opts:
      type: none
      o: bind,uid=1000,gid=1000
      device: /opt/paperless/tmp
```

For the host directory, ensure permissions allow the container user:
```bash
mkdir -p /opt/paperless/tmp
chmod 1777 /opt/paperless/tmp
```

### Env Var Distinction

| Env Var | Purpose | Notes |
|---------|---------|-------|
| `PAPERLESS_SCRATCH_DIR` | Mail attachment temp processing | Must be writable by UID 1000 |
| `PAPERLESS_TEMP_DIR` | General temp dir (not mail) | Different use case |
| `PAPERLESS_DATA_DIR` | Database and index files | Persistent storage |
| `PAPERLESS_MEDIA_ROOT` | Imported documents | Persistent storage |

### Diagnosing PermissionError

```bash
# Check if the PermissionError is real or a test artefact
# 1. Run with correct user
docker exec -u paperless paperless-ngx python manage.py mail_fetcher
# If this works → the error was only triggered by root-exec testing

# 2. Check temp dir ownership
docker exec paperless-ngx ls -la /paperless-tmp/
# Should show: drwxrwxrwt (mode 1777) owned by root, writable by all

# 3. Check celery worker user
docker exec paperless-ngx id
# Should show: uid=1000(paperless)
```

### Historical ProcessedMail Tracebacks

After fixing the PermissionError, the Paperless UI may still show historical `ProcessedMail` traceback entries from before the fix. These are cosmetic — they record past failed attempts. They will not block new mail processing and can be ignored or cleared.

## Related Concepts

- [[wiki/concepts/docker-compose-restart-no-code-reload]] — other Docker testing footguns where manual exec gives misleading results
- [[wiki/concepts/sudo-git-clone-root-ownership]] — same class of bug: running a command as root creates files inaccessible to the service user
- [[wiki/concepts/celery-queue-worker-specialization]] — Celery worker user identity matters for file system access

## Sources

- [[daily/2026-05-01.md]] — Paperless-NGX mail fetcher PermissionError traced to `docker exec` running as root; `PAPERLESS_SCRATCH_DIR=/paperless-tmp` + mode 1777 volume + removing USERMAP env vars was the correct fix; automatic celery-beat run completed successfully with 5 documents imported
