Setup
Cruxwire is a single container that talks to your Ollama instance and keeps its state on a local volume. Run it on your own Mac or PC with Docker Desktop, or on a homelab box. If you can run docker compose up, you can run Cruxwire.
Step 1
Two things, whether you run it on your own computer or a homelab box.
Docker Desktop on your Mac or PC, or any container host: a NAS, mini-PC, VM, or Raspberry Pi class box. Docker Desktop already bundles Compose, so docker compose up works out of the box. The app has no build step and no Python dependencies, it's pure standard library inside the image.
A reachable Ollama endpoint over HTTP, with a chat model and an embedding model pulled. It can run natively on the same machine (on a Mac, the right setup, so Ollama can use the GPU) or anywhere on your network; you point Cruxwire at it with OLLAMA_HOST. For usable speed, run it on an NVIDIA GPU or an Apple Silicon Mac with enough memory; CPU-only works but is slow.
# chat: score / summarize / categorize
ollama pull qwen3:8b
# embeddings: clustering / taste / semantic search
ollama pull nomic-embed-text
Step 2
Clone the repo, point it at your Ollama instance, and bring it up.
# 1: get the source
git clone https://github.com/philoking/cruxwire
cd cruxwire
# 2: configure .env (mainly OLLAMA_HOST and TZ)
cp .env.example .env
# point OLLAMA_HOST at your Ollama; set TZ for your zone
# 3: build and run
docker compose up -d --build
services:
cruxwire:
build: .
container_name: cruxwire
restart: unless-stopped
ports:
- "${PORT:-8090}:8090"
environment:
OLLAMA_HOST: ${OLLAMA_HOST:-http://localhost:11434}
PORT: 8090
TZ: ${TZ:-America/Los_Angeles}
volumes:
- cruxwire-data:/data
env_file:
- path: .env # overrides: OLLAMA_HOST, OLLAMA_MODEL, TZ, …
required: false
volumes:
cruxwire-data:
# point this at your Ollama. Same machine via Docker Desktop?
# use host.docker.internal, not localhost.
OLLAMA_HOST=http://localhost:11434
# timezone for the scheduler's active-hours window (set yours)
TZ=America/Los_Angeles
# already the recommended models; change only to use others
OLLAMA_MODEL=qwen3:8b
EMBED_MODEL=nomic-embed-text
# ranking, ingestion, schedule, and retention all ship with
# sensible defaults; leave them or tune later in Settings.
http://<host>:8090/On first run Cruxwire seeds its feed list from a sample and the pipeline generates the first digest, give it about a minute. Manage feeds in the UI's Feeds screen, or trigger a refresh with curl -X POST http://<host>:8090/refresh.
Two things, not one. Point Cruxwire at the host with OLLAMA_HOST=http://host.docker.internal:11434 (inside the container, localhost is the container itself), and make sure Ollama itself is listening on 0.0.0.0:11434, not just loopback, or the container's connection is refused even when the hostname resolves. The Docker Desktop notes in the docs walk through both, with per-platform commands.
Step 3 · important
Cruxwire's trust model is simple: the network is trusted, nothing more.
Every endpoint, including the ones that change settings, feeds, and categories or trigger pipeline runs, is reachable by anyone who can reach the port. Do not expose port 8090 directly to the internet. If you want remote access, put it behind a reverse proxy that adds auth: Caddy/nginx with basic auth, Authelia, Tailscale, Cloudflare Access, and similar.
The pipeline fetches every feed you add and, for TL;DRs, the linked article pages. Treat your feed list as trusted input, and don't give the container network access to internal services it has no reason to reach.
The mutating endpoints have no rate limiting or CSRF protection. The trust model is "the network is trusted", keep it on a homelab LAN, a tailnet, or localhost.
Runtime data, read history, Read Later, learned source preferences, is stored unencrypted on the Docker volume. Back it up, and protect it, accordingly.
Tuning
Infrastructure wiring lives in docker-compose.yaml / .env; everything else is editable live in the Settings UI and applies on the next run without a restart.
| Variable / setting | Default | Purpose |
|---|---|---|
| OLLAMA_HOST | http://localhost:11434 | Ollama base URL for chat and embeddings. |
| Chat / embedding model | qwen3:8b / nomic-embed-text | Which Ollama models to use. Managed in Settings → Models. |
| Merge similarity | 0.74 | Cosine threshold to collapse same-topic articles into one card. |
| Lookback | 36 h | How far back fresh items are first discovered. |
| Retention floor / ceiling | 25 / 60 | Keep at least / at most this many unread stories. |
| Schedule | 06–22, every 2 h | When pipeline runs fire (active-hours window + interval). |
| History retention | 3 days | How long read History is kept before it ages out. Read Later is never aged. |
| PORT | 8090 | Container HTTP port. |
The repo's TUNING.md documents every adjustable knob, how they interact, and recipes for common situations ("it goes dry on weekends", "I'm seeing duplicate cards", "surface more of what I read"). Categories, their labels, colors, and the interest descriptions the scorer ranks against, are data you edit in categories.json or the Settings UI.
Mutable data lives on the cruxwire-data volume (/data): state.json, feeds.json, digest.json, settings.json, runs.json, categories.json, and article embeddings. Rebuilds preserve it; back up the volume to keep your feed list, read history, and learned preferences.
No dependencies means you can run the server directly against local files with a handful of env vars pointed at your Ollama host. To iterate on the layout without a live feed setup, point the feeds file at a nonexistent path and drop in a real digest.json.
The repository has the full SPEC, the tuning guide, and sample feeds and categories to start from.