Docker (preferred deploy)

Docker Compose is the preferred way to run paperful as an operator. Developers still use uv locally (see CONTRIBUTING).

The image is a one-shot CLI (not a daemon). Zotero stays on the host; paperful in the container talks to it over the local API (:23119). Durable data — config, custom playbook packs, out/, and state/ — lives outside the git root by default.

Prerequisites

  • Docker with Compose v2

  • Zotero running on the host, local API enabled (Settings → Advanced → Allow other applications on this computer to communicate with Zotero)

  • For attach / fix-metadata --apply: complete the Zotero “Always allow” dialog once on the host (key is stored under state/)

  • For Scholar / EZProxy sessions: run paperful session login on the host (headed browser), then reuse the mounted state/sessions/ from the container

Quick start

git clone https://github.com/glen-w/Paperful.git
cd Paperful
cp .env.example .env

Keep your current repo-local setup (no data move):

cp .env.example .env
# edit .env: PAPERFUL_DATA=.
docker compose build
docker compose run --rm paperful doctor
docker compose run --rm paperful run --collection interesting --dry-run

Or migrate into a sibling data directory (recommended long-term):

cp .env.example .env   # PAPERFUL_DATA=../paperful-data
mkdir -p ../paperful-data/packs ../paperful-data/out ../paperful-data/state
cp config.example.toml ../paperful-data/config.toml
# edit email / ezproxy_base / grey_playbooks_dir = "packs" as needed
# optional: move existing out/ and state/ into ../paperful-data/
docker compose build
docker compose run --rm paperful doctor

PAPERFUL_DATA in .env defaults to ../paperful-data. Compose mounts that tree at /data inside the container. Use relative out_dir / state_dir / grey_playbooks_dir in config (e.g. "out", "state", "packs") so they resolve under the mounted data dir. Absolute host paths (e.g. /Users/...) will not land on the volume.

Environment and override

File

Role

.env.example

Copy to .envPAPERFUL_DATA, PAPERFUL_ZOTERO_HOST

compose.yaml

Base service (build, Zotero host, data volume)

compose.override.example.yaml

Optional local Compose tweaks

.env and compose.override.yaml are gitignored so your machine-local paths never land in the repo. Set PAPERFUL_DATA=. to keep config/out/state in the repo; use ../paperful-data (default) to keep packs and outputs outside the git root.

PAPERFUL_ZOTERO_HOST defaults to host.docker.internal so Docker Desktop (macOS/Windows) can reach host Zotero. Compose also adds extra_hosts: host.docker.internal:host-gateway for Linux Docker Engine. paperful always sends Host: localhost:23119 — Zotero’s local API requires that header even when the TCP peer is host.docker.internal.

Custom playbook packs

Put extra grey-playbook TOML files in packs/ (under the data dir) and set in config:

grey_playbooks_dir = "packs"

Merge order: builtin pack → packs/*.toml → inline [[grey_playbooks]] (same name wins later). See Configuration.

Common commands

docker compose run --rm paperful doctor
docker compose run --rm paperful collections
docker compose run --rm paperful run --collection interesting --dry-run
docker compose run --rm paperful run --collection interesting
docker compose run --rm paperful report
make docker-build
make docker-doctor

Pass any CLI flag after the service name; the image ENTRYPOINT is paperful.

Image contents

  • Python 3.12, paperful + htmlpdf (Playwright Chromium)

  • Poppler (pdftotext)

  • Non-root user paperful (uid 1000)

If bind-mounted out/ / state/ are not writable, fix ownership on the host (chown -R 1000:1000 ) or run with a matching user override.

Sessions and attach

Headed Chromium login and Zotero’s authorize dialog need the host GUI. Typical flow:

  1. On the host (uv or a previous install): paperful session login ezproxy and/or scholar; approve attach once so state/zotero-local-api-key.json exists.

  2. Ensure that state/ is the same tree the container mounts.

  3. Run fetch/attach from Docker as above.

Develop with uv

For hacking on the package itself:

uv sync --group dev
uv run pytest
uv run paperful doctor

See Commands and Architecture.