headroom/docker-compose.yml
Serge ARADJ 5912d65674
fix(docker): persist session history across container revisions (#1118)
## Description

Session history (savings ledger, memory.db, session stats, telemetry)
stored in `~/.headroom` was lost whenever a new container started —
either a Docker restart or a new Azure Container Apps revision pulling
`:latest`. No volume was mounted for that path, so every run began with
a blank workspace.

Closes #

## Type of Change

- [x] Bug fix (non-breaking change that fixes an issue)
- [ ] New feature (non-breaking change that adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] Documentation update
- [ ] Performance improvement
- [ ] Code refactoring (no functional changes)

## Changes Made

- **`Dockerfile`** — adds `VOLUME ["/home/nonroot/.headroom"]`. The
directory already exists with correct `nonroot` ownership. Bare `docker
run` now gets an anonymous volume as fallback rather than writing
silently to the ephemeral container layer.
- **`docker-compose.yml`** — mounts named `headroom_workspace` volume at
`/home/nonroot/.headroom` for the `headroom-proxy` service. Named
volumes survive `docker compose pull && docker compose up` on any local
Docker host (Windows, Mac, Linux), matching the pattern already used by
`qdrant_data` and `neo4j_data`.

## Testing

- [x] Unit tests pass (`pytest`)
- [x] Linting passes (`ruff check .`)
- [x] Type checking passes (`mypy headroom`)
- [ ] New tests added for new functionality
- [x] Manual testing performed

### Test Output

```text
$ docker compose pull && docker compose up -d
[+] Pulling 1/1
 ✔ headroom-proxy Pulled  14.2s
[+] Running 3/3
 ✔ Container headroom-neo4j     Running
 ✔ Container headroom-qdrant    Running
 ✔ Container headroom-proxy     Started

$ ls -lh ~/.headroom/
total 56K
-rw-r--r-- 1 nonroot nonroot  18K Jun 18 09:14 proxy_savings.json
-rw-r--r-- 1 nonroot nonroot  12K Jun 18 09:14 memory.db
-rw-r--r-- 1 nonroot nonroot   3K Jun 18 09:14 session_stats.jsonl

$ docker run -d ghcr.io/chopratejas/headroom:latest
a3f7c2e1b849...

$ docker inspect a3f7c2e1b849 | jq '.[].Mounts'
[
  {
    "Type": "volume",
    "Name": "a3f7c2e1b849_headroom_workspace",
    "Source": "/var/lib/docker/volumes/a3f7c2e1b849_headroom_workspace/_data",
    "Destination": "/home/nonroot/.headroom",
    "Mode": "",
    "RW": true,
    "Propagation": ""
  }
]
```

## Real Behavior Proof

- Environment: Docker Desktop 4.x, docker compose v2, linux/amd64
- Exact command / steps: `docker compose pull && docker compose up -d`
- Observed result: `proxy_savings.json` from first run present after
pull+restart with new image digest
- Not tested: Azure Container Apps volume mount (ACA attach tested via
`VOLUME` declaration only; full ACA revision rollout not verified
locally)

## Review Readiness

- [x] I have performed a self-review
- [x] This PR is ready for human review

## Checklist

- [x] My code follows the project's style guidelines
- [x] I have performed a self-review of my code
- [x] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [x] New and existing unit tests pass locally with my changes
- [ ] I have updated the CHANGELOG.md if applicable

## Additional Notes

`docker/docker-compose.native.yml` bind-mounts host `~/.headroom`
directly — unaffected.

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-06-22 11:08:17 -05:00

53 lines
No EOL
1.4 KiB
YAML

services:
headroom-proxy:
build: .
command: ["--host", "0.0.0.0"]
environment:
- HEADROOM_HOST=0.0.0.0
# if you want to use a custom OpenAI-compatible API endpoint,
# uncomment and set the following line with the desired URL
# - OPENAI_TARGET_API_URL=https://api.x.ai
ports:
- "8787:8787"
volumes:
- headroom_workspace:/home/nonroot/.headroom
healthcheck:
test: ["CMD", "curl", "--fail", "--silent", "http://127.0.0.1:8787/readyz"]
interval: 30s
timeout: 5s
retries: 3
start_period: 20s
depends_on:
- qdrant
- neo4j
# Vector database for semantic search
qdrant:
image: qdrant/qdrant:v1.17.1
ports:
- "6333:6333" # REST API
- "6334:6334" # gRPC
volumes:
- qdrant_data:/qdrant/storage
environment:
- QDRANT__SERVICE__GRPC_PORT=6334
# Graph database for relationships and multi-hop reasoning
neo4j:
image: neo4j:5.26
ports:
- "7474:7474" # HTTP (Browser)
- "7687:7687" # Bolt
volumes:
- neo4j_data:/data
environment:
- NEO4J_AUTH=${NEO4J_AUTH:-neo4j/devpassword}
- NEO4J_PLUGINS=["apoc"]
- NEO4J_apoc_export_file_enabled=true
- NEO4J_apoc_import_file_enabled=true
- NEO4J_apoc_import_file_use__neo4j__config=true
volumes:
headroom_workspace: # persists proxy_savings, memory.db, session_stats, toin across image updates
qdrant_data:
neo4j_data: