Docker¶
Default deploy. Run Rollup web + digest subprocesses in one container while sharing the same config, state, output, and mail paths as a host CLI would use.
For local development, packaging extras, or a bare-metal CLI without Compose, see the host Python / venv path in the README and EXAMPLES.md.
Prerequisites¶
Docker Desktop (or Docker Engine + Compose v2)
File sharing enabled for
~/email,~/Documents,~/.config/rollup, and this repoThunderbird mbox under the mail mount (default
~/email/gmail)
Quick start¶
Create host paths first so Compose bind-mounts files (not empty directories):
mkdir -p ~/.config/rollup ~/Documents/rollup-outputs ./state ./logs
touch ~/.config/rollup/config.toml ~/.config/rollup/env
chmod 600 ~/.config/rollup/env
cp docker-compose.override.yml.example docker-compose.override.yml
# edit override paths if your mail/config/output dirs differ
docker compose up -d --build
open http://localhost:8765
Configure sticky paths in Settings, then run digests from Run Studio. Optional sticky config details: CONFIG.md.
The override bind-mounts your host paths:
Host |
Container |
Purpose |
|---|---|---|
|
|
Sticky config (Settings / Reddit page writes here) |
|
|
LinkedIn cookies (never commit) |
|
|
Digests |
|
|
|
|
|
Run logs |
|
|
mbox (read-only) |
Edit docker-compose.override.yml if your paths differ.
Secrets¶
LinkedIn session cookies (and optional Reddit OAuth) live in ~/.config/rollup/env (mode 600), loaded at startup via ROLLUP_ENV_FILE. The container sets ROLLUP_ENV_FILE=/data/config/env and mounts your host file read-only.
Do not put secrets in TOML, .env, or git. See CONFIG.md and CRON.md.
CLI flags vs TOML paths¶
Compose passes rollup --config /data/config/config.toml web … with explicit path flags pointing at /data/*. CLI flags win over TOML, so output_dir = "~/Documents/rollup-outputs" in config still works on the host while Docker uses the mounted paths.
Non-path settings (folder themes, LinkedIn searches, Reddit subs, efforts) load from the mounted config.toml.
Network¶
Source |
Credentials |
Egress needed |
|---|---|---|
Mail mbox |
— |
No |
|
Yes (when enabled; skipped when listing cache is fresh) |
|
None (optional |
Yes (when |
|
Webpage articles |
None |
Yes (on fetch runs for pending URLs) |
Ollama on host |
— |
|
Default mail-only digest makes no network calls.
Ollama on the host¶
Enable LLM in Settings and point Ollama at http://host.docker.internal:11434/api/generate (or use sticky config). docker-compose.yml adds host.docker.internal:host-gateway.
Do not run both web UIs¶
Native rollup web and Docker web share ./state/rollup.db and the digest lock. Run one at a time.
Doctor check¶
docker compose exec rollup rollup doctor \
--root /data/mail/Newsletters.sbd \
--mail-root /data/mail \
--state-dir /data/state \
--output-dir /data/output \
--log-dir /data/logs
Dev / CI smoke test (fixtures)¶
No real mail required:
docker compose -f docker-compose.yml -f docker-compose.dev.yml up --build
curl -fsS http://127.0.0.1:8765/rollups
Uses tests/fixtures as mail root and ephemeral volumes for state/output/logs.
Bind flag¶
Docker uses --allow-non-loopback-bind so Flask can listen on 0.0.0.0 inside the container. Compose publishes 127.0.0.1:8765:8765 so the host port is loopback-only. Host-header validation still requires loopback Host: headers — access via http://localhost:8765.
To listen on all host interfaces (LAN), override in docker-compose.override.yml:
services:
rollup:
ports:
- "8765:8765"
Host Python (advanced) stays loopback-only by default:
rollup web --host 127.0.0.1