Container guide¶
Duecare ships three container surfaces, each for a different audience. Pick one — they don't conflict.
┌──────────────────────────────────────────────────────────────────┐
│ │
│ 1. Production 2. Development 3. Topology │
│ image + compose dev compose examples │
│ │
│ ./Dockerfile ./Dockerfile.dev ./examples/ │
│ ./docker-compose.yml ./docker-compose.dev deployment/ │
│ .yml │
│ │
│ Used by: Used by: Used by: │
│ - Cloud Run / EKS - core developers - operators │
│ - Render / Fly - PR contributors picking a │
│ - Lightsail VPS - on-host iteration topology │
│ - the topology │
│ examples (which │
│ reuse this image) │
│ │
└──────────────────────────────────────────────────────────────────┘
Surface 1 — production image + compose¶
The repo root ships a multi-stage, multi-arch Dockerfile that builds the 17
service/runtime package wheels and produces a slim runtime image. The 18th uv
workspace member, duecare-llm-kit, stays outside that production image so its
NumPy/pandas/Matplotlib reporting stack does not increase deployment size.
# Build (single arch, for local testing)
docker build -t duecare-llm:latest .
# Build (multi-arch, push to a registry)
docker buildx build --platform linux/amd64,linux/arm64 \
--tag ghcr.io/tayloramareltech/duecare-llm:latest --push .
# Run via compose (chat + classifier + Ollama)
cp .env.example .env # optional — sensible defaults work
docker compose up -d
Then: - chat playground: http://localhost:8080 - classifier: http://localhost:8081 - Ollama: http://localhost:11434
The image is also published by CI to
ghcr.io/tayloramareltech/duecare-llm:latest (multi-arch, signed
with cosign). All cloud-deployment configs in infra/ pull from
there — there's exactly one canonical image.
Default model: gemma4:e2b (Apache 2.0, ~1.5 GB INT8). Override
via DUECARE_OLLAMA_MODEL=gemma4:e4b in .env for higher quality,
or gemma4:e2b-int4 for faster cold starts.
Surface 2 — development compose (hot reload)¶
Dockerfile.dev + docker-compose.dev.yml bind-mount the repo into
the container so source changes reload the chat server without a
rebuild. Includes ruff / mypy / pytest in the image.
docker compose -f docker-compose.dev.yml up # foreground
docker compose -f docker-compose.dev.yml exec dev bash # shell
docker compose -f docker-compose.dev.yml exec dev pytest -x
docker compose -f docker-compose.dev.yml exec dev ruff check src/
Use this surface for: - Day-to-day Python development on the harness / packages - Running the full pytest suite without dirtying the host venv - Iterating on the chat-playground UI while the model is loaded
Don't use this for production — it runs as root, includes dev tools that bloat the image, and bind-mounts the repo (security risk in a multi-tenant cluster).
Surface 3 — topology examples (deployment cookbook)¶
examples/deployment/ ships runnable, narrated examples for the
five deployment shapes documented in
docs/deployment_topologies.md:
local-all-in-one/— single Docker Compose stack (Topology A)local-cli/— single Python CLI, no Docker (Topology A)ngo-office-edge/— Mac mini / NUC + mDNS (Topology B)server-and-clients/— cloud server + 8 client patterns (Topology C)hybrid-edge-llm-cloud-rag/— phone Gemma + cloud knowledge (Topology E)
Each example reuses the same ghcr.io/tayloramareltech/duecare-llm
image. They differ in compose topology, env vars, and accompanying
documentation — not in the underlying binary.
Use this surface for: - Operators evaluating which topology fits their org - Showing judges + auditors a concrete deployment matching the writeup's claims
Surface 4 — Kubernetes via Helm¶
For production K8s clusters, use the Helm chart at
infra/helm/duecare/:
helm install duecare ./infra/helm/duecare \
--namespace duecare --create-namespace \
-f my-values.yaml
The chart includes:
- Chat + classifier deployments
- Service + Ingress
- HPA (CPU + memory)
- PodDisruptionBudget (maxUnavailable: 1)
- NetworkPolicy (default-deny, opt-in)
- ServiceMonitor for Prometheus Operator
Values doc inline in
infra/helm/duecare/values.yaml.
Cloud-specific entry points in infra/{aws,gcp,azure,eks,gke,aks}/.
Surface 5 — observability stack¶
Optional companion stack for monitoring:
Brings up Prometheus + Loki + OpenTelemetry Collector + Grafana with auto-provisioned datasources + a "Duecare overview" dashboard.
Wire your Duecare server to it via:
DUECARE_METRICS_ENABLED=true
DUECARE_OTEL_ENDPOINT=http://otel-collector:4318
DUECARE_LOKI_ENDPOINT=http://loki:3100
See infra/observability/README.md.
Comparison matrix¶
| Surface | Where used | Hot reload | Tools in image | Production-safe |
|---|---|---|---|---|
| 1. Production image + compose | cloud + topology examples | no | no | yes |
| 2. Dev compose | local development | yes | yes | no |
| 3. Topology examples | operator-facing | (compose-relative) | (image-relative) | yes |
| 4. Helm chart | production K8s | no | no | yes |
| 5. Observability stack | beside any of the above | n/a | n/a | yes (single-binary Loki for small scale; swap for managed Loki at large scale) |
What's NOT shipped here¶
- Helm umbrella chart that combines
infra/helm/duecare/+ the observability stack. Use Helmfile or ArgoCDApplications to orchestrate. - A
Tiltfilefor the dev compose — thedocker compose -f docker-compose.dev.ymlflow already gives sub-second iteration with bind-mounts. Tilt is overkill for a 3-container dev loop. - A devcontainer.json — exists at
.devcontainer/for Codespaces / VS Code Remote Containers, separate from this Docker Compose surface. The Android sibling repo (TaylorAmarelTech/duecare-journey-android) has its own equivalent local-setup doc.
Common operations¶
# Smoke test the production image
make docker-build && make docker-up
curl http://localhost:8080/healthz # → "ok"
curl http://localhost:8080/metrics # → Prometheus exposition
# Run the test suite inside the dev container
docker compose -f docker-compose.dev.yml run --rm dev pytest
# Tail logs
docker compose logs -f --tail=100
# Tear everything down + reclaim disk
docker compose down --volumes
docker compose -f docker-compose.dev.yml down --volumes
docker compose -f infra/observability/docker-compose.yml down --volumes