TrueNAS custom apps cannot build: the main compose's 'build: .' (plus relative .env/bind mounts) fails app 'up'. Added a GitHub Actions workflow publishing ghcr.io/fez5587/momentum-app on every master push, and deploy/docker/truenas-compose.yml — a pull-only variant with dataset host-paths, inline environment (secrets as <FILL-ME> placeholders, current validated strategy knobs pre-filled), and the same healthchecks. Validated with docker compose config. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Running the Momentum stack in Docker Compose
One image, six services: postgres, loop, dashboard (:8765), qabot
(Telegram), transcripts, scheduler (20:00 ET weekday housekeeping), plus
on-demand jobs and an optional monitoring (Grafana) profile.
docker compose build
docker compose up -d
docker compose ps # healthchecks: loop heartbeat, dashboard HTTP, pg_isready
docker compose logs -f loop
Secrets live in .env (gitignored, never in the image). The compose file
overrides DATABASE_URL to the in-stack postgres so the stack is
self-contained.
Migrating the production DB into the stack
The live DB today is native Postgres 18 on 192.168.1.5. To move it into the compose volume:
# 1. dump the live DB (~4 GB as of 2026-07; events retention keeps it bounded)
pg_dump -h 192.168.1.5 -U admin -Fc momentum > momentum.dump
# 2. start ONLY postgres, restore into it
docker compose up -d postgres
docker compose exec -T postgres pg_restore -U momentum -d momentum --no-owner < momentum.dump
# 3. bring up the rest
docker compose up -d
Until then you can run the app services against the LAN DB (transition mode):
comment out the DATABASE_URL line in the x-app-env anchor and .env's
value (192.168.1.5) flows through.
Where to host it
Recommended: the DB box (192.168.1.5) or any always-on Linux host — NOT the WSL machine. The week of 2026-07-06 produced three trading-hours outages from WSL restarts and networking-mode changes (see memory: wsl-virtioproxy-networking). A dedicated Linux host removes that whole class. WSL + Docker works for development/validation but inherits WSL's fragility.
Cutover checklist (from the systemd stack):
docker compose build && docker compose up -don the target host- Verify: dashboard on :8765,
docker compose psall healthy, loop log shows ingest/watch/execute cycles, Telegram bot answers - Diff Alpaca open orders — a crash-restart on cutover can resubmit resting entries (memory: momentum-live-loop-architecture)
- Disable the WSL systemd units:
systemctl --user disable --now momentum-loop momentum-dashboard momentum-qabot momentum-transcripts momentum-premarket-eval.timer - Keep
momentum-testpgon the dev machine (tests only, not production)
Notes:
- TZ inside containers is America/New_York (market-clock logic is ET).
data/(logs, learned_params.json, transcripts) lives in theapp-datanamed volume, shared by all app services.- Ollama stays external (192.168.1.9,
OLLAMA_*in .env); enrichment fails safe when it's down. - Do the cutover OUTSIDE market hours.
Config layering: what goes where
.env.example is the committed, sanitized inventory of every variable
(regenerated 2026-07-09). Three layers:
- Secrets — keep in
.envin a protected space (root-ownedchmod 600, or a Docker secret):ALPACA_API_KEY/ALPACA_SECRET_KEY,TELEGRAM_BOT_TOKEN,YOUTUBE_API_KEY,POSTGRES_PASSWORD/DATABASE_URL, Discord webhooks, Schwab keys. The compose file reads them viaenv_file; they are never baked into the image. - Strategy knobs — fine to promote into the compose
environment:block if you want them versioned with the deployment:TRADING_*,TRIGGER_*(gap band, squeeze exception, quality floor, caps),WATCHER_*,LIVE_*.environment:overridesenv_file, so promoted values win. - Infra addresses:
OLLAMA_HOST(external box, see below),ALPACA_DATA_FEED=sip, dashboard host/port — either layer works.
External Ollama (news enrichment)
Ollama is the ONE external dependency: OLLAMA_HOST=http://192.168.1.9:11434
reaches the LAN box from inside containers via normal outbound NAT — no
compose changes needed. If you ever run Ollama on the docker HOST itself, use
http://host.docker.internal:11434 and add
extra_hosts: ["host.docker.internal:host-gateway"] to the app anchor.
Enrichment is fully fail-safe: if Ollama is down, trading is unaffected
(news ranking just loses the catalyst-type boost).
NOTE (2026-07-09): the live .env contains two commented-out PowerShell lines
embedding the real Alpaca credentials — harmless there, but they nearly leaked
into the committed .env.example (caught by the regeneration leak check).
Consider deleting them from .env.