Files
BountyBoard/docker-compose.yml
T
etalon 0676f53280 chore: bind-mount docker volumes under the repo (./volumes), gitignore them
Replace the named volumes mongo-data and work-data with bind mounts to
./volumes/mongo and ./volumes/work so all runtime state lives under the repo
tree. Existing data was migrated; the old named volumes were removed. The
${HOME}/.claude host-credential mounts are intentionally left as-is (must not
live in the repo). Added /volumes/ to .gitignore.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-13 12:15:46 +02:00

97 lines
3.3 KiB
YAML

services:
mongo:
image: mongo:7
restart: unless-stopped
# modest cache so the whole stack fits comfortably on small hosts
command: ["mongod", "--wiredTigerCacheSizeGB", "0.5"]
# WiredTiger needs far more FDs than the 1024 some hosts default to;
# running out crashes mongod with a WT_PANIC during file creation
ulimits:
nofile:
soft: 64000
hard: 64000
# Mongo is reachable only on the compose network; never published to the
# host (§12).
volumes:
# data lives in-repo (gitignored) so all state is under one tree
- ./volumes/mongo:/data/db
healthcheck:
test: ["CMD", "mongosh", "--quiet", "--eval", "db.adminCommand('ping').ok"]
interval: 10s
timeout: 5s
retries: 5
start_period: 15s
app:
build: .
restart: unless-stopped
env_file: .env
environment:
MONGO_URI: mongodb://mongo:27017
# in-network address handed to the external services for callbacks
# and signed attachment URLs (§5.2 example: http://app:8787/…)
APP_INTERNAL_URL: http://app:8787
# NOTE: published on all interfaces to match this host's pattern (gitea,
# outline, wekan are exposed the same way; docker-published ports bypass
# UFW). The spec-default loopback-only binding is the commented line —
# restore it once the app sits behind the reverse proxy exclusively.
ports:
- "${APP_PORT:-8787}:8787"
# - "127.0.0.1:${APP_PORT:-8787}:8787"
depends_on:
mongo:
condition: service_healthy
healthcheck:
test: ["CMD", "wget", "-qO-", "http://127.0.0.1:8787/healthz"]
interval: 10s
timeout: 5s
retries: 3
start_period: 10s
# --- placeholder external services (docker compose --profile mocks up) ---
# Two fully independent services: separate builds, ports, tokens (§5).
atomizer-mock:
build: ./services/atomizer
profiles: ["mocks"]
restart: unless-stopped
environment:
PORT: "8090"
ATOMIZER_TOKEN: ${ATOMIZER_TOKEN}
ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY:-}
ANTHROPIC_OPENAI_BASE_URL: ${ANTHROPIC_OPENAI_BASE_URL:-https://api.anthropic.com/v1}
ANTHROPIC_MODEL: ${ANTHROPIC_MODEL:-claude-sonnet-4-6}
LLM_API_STYLE: ${LLM_API_STYLE:-openai}
ports:
- "127.0.0.1:8090:8090"
healthcheck:
test: ["CMD", "wget", "-qO-", "http://127.0.0.1:8090/healthz"]
interval: 10s
timeout: 5s
retries: 3
start_period: 5s
work-performer:
build: ./services/work-performer
profiles: ["mocks"]
restart: unless-stopped
environment:
PORT: "8091"
WORK_PERFORMER_TOKEN: ${WORK_PERFORMER_TOKEN}
PUBLIC_BASE_URL: http://work-performer:8091
# host Claude Code config/secrets (§9.2); mounted into the node user's
# home because claude refuses --dangerously-skip-permissions as root
volumes:
- ${HOME}/.claude:/home/node/.claude
- ${HOME}/.claude.json:/home/node/.claude.json
- ./volumes/work:/work
ports:
- "127.0.0.1:8091:8091"
extra_hosts:
- "host.docker.internal:host-gateway"
healthcheck:
test: ["CMD-SHELL", "node -e \"fetch('http://127.0.0.1:8091/healthz').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))\""]
interval: 10s
timeout: 5s
retries: 3
start_period: 5s