-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathdocker-compose.local.yml
More file actions
262 lines (252 loc) · 11.6 KB
/
Copy pathdocker-compose.local.yml
File metadata and controls
262 lines (252 loc) · 11.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
name: memroos-local
# MemroOS — Fully Local Self-Hosted Stack
# =======================================
# This is the DEFAULT for a local install (install.sh --local).
#
# It is the demo stack (bundled Ollama + embedded Chroma vector store, no cloud
# accounts) PLUS a local Neo4j so Graph Memory works — i.e. every memory tier
# runs on this one host with zero external dependencies:
#
# - Knowledge store → git-backed markdown in the repo (content/)
# - Vector memory → mem0 + embedded Chroma (SQLite), local Ollama embeddings
# - Graph memory → local Neo4j (this file's addition over the demo stack)
# - Orchestration → local LangGraph service
# - LLM / embeddings → bundled Ollama container (qwen2.5:3b + nomic-embed-text)
#
# For the lighter no-graph variant use docker-compose.demo.yml; for a
# production stack pointed at managed Qdrant/Neo4j use docker-compose.yml.
services:
memroos:
env_file: .env
runtime: runc
dns:
- 1.1.1.1
- 8.8.8.8
build:
context: .
dockerfile: docker/Dockerfile.memroos
ports:
# Bind to loopback by default — a reachable fresh install on a
# multi-tenant host would otherwise expose the staging stack on
# all interfaces. Override the host-IP portion (e.g.
# MEMROOS_BIND=0.0.0.0) to expose for a local VM test.
- "${MEMROOS_BIND:-127.0.0.1}:${MEMROOS_PORT:-3000}:3000"
environment:
MEMROOS_A2A_PROFILE: ${MEMROOS_A2A_PROFILE:-local-dev}
MEMROOS_PUBLIC_BASE_URL: ${MEMROOS_PUBLIC_BASE_URL:-http://localhost:3000}
MEMROOS_A2A_ENDPOINT_BASE_URL: ${MEMROOS_A2A_ENDPOINT_BASE_URL:-http://localhost:3000}
MEMROOS_OPERATOR_API_KEY: ${MEMROOS_OPERATOR_API_KEY:-demo-key}
# This server's OWN dashboard login. Defaults are seeded on first
# boot only if the env vars are unset; install.sh generates a
# random MEMROOS_JWT_SECRET and a random MEMROOS_ADMIN_PASSWORD
# on first install (see install.sh::run_docker_install) so the
# reachable install does NOT ship the well-known 'memroos-local'
# values that the prior version of this compose had. The literal
# fallbacks below remain ONLY for the offline / no-installer path
# (e.g., running docker compose up by hand) and the user MUST
# override these or bind the published ports to a non-routable
# interface (default MEMROOS_BIND=127.0.0.1 above) before exposing.
MEMROOS_JWT_SECRET: ${MEMROOS_JWT_SECRET:-memroos-local-dev-jwt-secret-change-me-32chars}
MEMROOS_ADMIN_EMAIL: ${MEMROOS_ADMIN_EMAIL:-admin@memroos.local}
MEMROOS_ADMIN_PASSWORD: ${MEMROOS_ADMIN_PASSWORD:-change-me-on-first-install}
MEM0_URL: http://mem0:3201
ORCHESTRATION_SERVICE_URL: http://orchestration:3210
# Graph memory: point the app at the bundled local Neo4j.
# Hardcoded (not ${...}) on purpose: the repo .env ships cloud (Neo4j Aura)
# placeholders, and this stack must stay fully self-contained on one host.
NEO4J_HTTP_URL: http://neo4j:7474
NEO4J_DATABASE: neo4j
NEO4J_USERNAME: neo4j
# NEO4J_PASSWORD is set by install.sh to a random 24-hex value
# (same value as MEMROOS_NEO4J_AUTH below). The app talks to Neo4j
# via NEO4J_USERNAME/NEO4J_PASSWORD; the Neo4j container's own
# AUTH comes from MEMROOS_NEO4J_AUTH (composed in install.sh as
# 'neo4j/<same-password>') so app + DB stay in sync.
# The literal fallback matches the same string the Neo4j container
# uses for MEMROOS_NEO4J_AUTH so 'docker compose ... down' (which
# runs BEFORE install writes .env on a clean CI checkout) still
# renders; install-regression --fast + --full assert the rotation
# via install.sh regenerated the value on first install.
NEO4J_PASSWORD: ${NEO4J_PASSWORD:-memroos-local}
SQLITE_DB_PATH: /data/conversations.db
CLAUDE_MEMORY_PATH: /data/claude-memory
KNOWLEDGE_BASE_PATH: /knowledge
# Agents: AGENT_CONFIGS_PATH inside the container.
# Default: <knowledge>/agent-configs (subdir convention — no override needed).
# Independent layout: set MEMROOS_AGENT_CONFIGS_CONTAINER_PATH=/agent-configs
# in .env AND set AGENT_CONFIGS_HOST_PATH to a host directory; the
# compose bind-mounts that host directory at /agent-configs read-only.
# The legacy .env key AGENT_CONFIGS_PATH (host-side) is still tolerated
# by being ignored here — the in-container value is governed by this var.
AGENT_CONFIGS_PATH: ${MEMROOS_AGENT_CONFIGS_CONTAINER_PATH:-/knowledge/agent-configs}
APO_PROPOSALS_PATH: /root/.openclaw/skills/proposals
MEMROOS_DATA_RESIDENCY: ${MEMROOS_DATA_RESIDENCY:-0}
LOCAL_JUDGE_PROVIDER: ${LOCAL_JUDGE_PROVIDER:-ollama}
LOCAL_JUDGE_ENDPOINT: ${LOCAL_JUDGE_ENDPOINT:-http://ollama:11434/v1}
LOCAL_JUDGE_MODEL: ${LOCAL_JUDGE_MODEL:-qwen2.5:3b}
AUDIT_RETENTION_DAYS: ${AUDIT_RETENTION_DAYS:-365}
volumes:
- memroos-data:/data
- ${KNOWLEDGE_BASE_PATH:-./docs}:/knowledge:ro
# Independent Agents mount (only active when AGENT_CONFIGS_PATH is set
# in .env to a path outside the knowledge repo). The mount path is
# declared as a literal at /agent-configs so it does not collide with
# the /knowledge mount above.
- ${AGENT_CONFIGS_HOST_PATH:-/dev/null}:/agent-configs:ro
- ${APO_PROPOSALS_PATH:-./data/apo-proposals}:/root/.openclaw/skills/proposals:ro
depends_on:
mem0:
condition: service_healthy
orchestration:
condition: service_healthy
neo4j:
condition: service_healthy
healthcheck:
test: ["CMD-SHELL", "node -e \"fetch('http://127.0.0.1:3000/login').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))\""]
interval: 30s
timeout: 5s
retries: 5
mem0:
build:
context: .
dockerfile: docker/Dockerfile.memory
ports:
- "${MEMROOS_BIND:-127.0.0.1}:${MEM0_PORT:-3201}:3201"
environment:
# Local mode: no Qdrant required (embedded Chroma vector store).
# Pinned empty so the repo .env's cloud Qdrant placeholders can't leak in.
QDRANT_URL: ""
QDRANT_API_KEY: ""
OLLAMA_BASE_URL: http://ollama:11434
ANONYMIZED_TELEMETRY: "False"
CHROMA_PRODUCT_TELEMETRY_IMPL: chroma_noop_telemetry.NoopTelemetry
CHROMA_TELEMETRY_IMPL: chroma_noop_telemetry.NoopTelemetry
ORT_LOG_SEVERITY_LEVEL: "3"
volumes:
- mem0-logs:/app/services/memory/logs
- mem0-history:/root/.mem0
- ./services/memory/mem0-config.demo.yaml:/app/services/memory/mem0-config.yaml:ro
depends_on:
ollama-pull:
# With COMPOSE_PROFILES=cloud the local ollama services are
# filtered out, so this dep must be optional. Without cloud
# profile, mem0 still waits for ollama-pull.
condition: service_completed_successfully
required: false
healthcheck:
test: ["CMD-SHELL", "python - <<'PY'\nimport urllib.request\nurllib.request.urlopen('http://127.0.0.1:3201/health', timeout=3)\nPY"]
interval: 30s
timeout: 5s
retries: 5
ollama:
# Local Ollama is OPTIONAL on a thin-client deployment. By default
# it is included (set in the project's `up` flow). To skip the
# 2.7GB CUDA image and point the app at a remote Ollama via
# LOCAL_JUDGE_ENDPOINT, run on a host with:
# COMPOSE_PROFILES=cloud docker compose -f docker-compose.yml \
# -f docker-compose.local.yml up -d
# The `cloud` profile (set via COMPOSE_PROFILES) skips ollama
# services entirely. mem0/memroos/orchestration continue to depend
# on the local ollama-pull BUT with `required: false` so the dep
# becomes a no-op when the service isn't present.
profiles: ["local"] # included only when COMPOSE_PROFILES=local
image: ollama/ollama:latest
ports:
- "${MEMROOS_BIND:-127.0.0.1}:${OLLAMA_PORT:-11434}:11434"
volumes:
- ollama-data:/root/.ollama
healthcheck:
test: ["CMD-SHELL", "ollama list >/dev/null 2>&1"]
interval: 10s
timeout: 5s
retries: 12
ollama-pull:
# Same profile gating as ollama.
profiles: ["local"]
image: ollama/ollama:latest
entrypoint: ["/bin/sh", "-lc"]
command:
- "ollama pull qwen2.5:3b && ollama pull nomic-embed-text"
environment:
OLLAMA_HOST: http://ollama:11434
depends_on:
ollama:
condition: service_healthy
neo4j:
image: neo4j:5-community
ports:
- "${MEMROOS_BIND:-127.0.0.1}:${NEO4J_HTTP_PORT:-7474}:7474"
- "${MEMROOS_BIND:-127.0.0.1}:${NEO4J_BOLT_PORT:-7687}:7687"
environment:
# NEO4J_AUTH is the ONE credential var the neo4j image handles specially.
# Do NOT set NEO4J_PASSWORD here: the image maps every other NEO4J_* env to
# a server setting under strict validation, and there is no 'PASSWORD'
# setting, so the server refuses to start. Pinned literal to match the app
# wiring above and stay independent of the repo .env cloud placeholders.
# Falls back to a known-default only when the host env doesn't supply
# one; install.sh writes MEMROOS_NEO4J_AUTH on first install so the
# reachable stack rotates this on every fresh boot.
NEO4J_AUTH: ${MEMROOS_NEO4J_AUTH:-neo4j/memroos-local}
# Also pass MEMROOS_NEO4J_AUTH so the healthcheck shell escape
# \${MEMROOS_NEO4J_AUTH#neo4j/} resolves at run-time. Without
# this, the healthcheck authenticates with an empty password and
# Neo4j stays unhealthy, which blocks the dependent memroos
# service from ever starting.
MEMROOS_NEO4J_AUTH: ${MEMROOS_NEO4J_AUTH:-neo4j/memroos-local}
volumes:
- neo4j-data:/data
- neo4j-logs:/logs
healthcheck:
# Use the same per-install password MEMROOS_NEO4J_AUTH carries (it
# is 'neo4j/<pass>'; strip the 'neo4j/' prefix).
test: ["CMD-SHELL", "cypher-shell -a bolt://127.0.0.1:7687 -u neo4j -p \"$${MEMROOS_NEO4J_AUTH#neo4j/}\" 'RETURN 1' >/dev/null"]
interval: 30s
timeout: 10s
retries: 10
start_period: 45s
orchestration:
build:
context: .
dockerfile: docker/Dockerfile.orchestration
ports:
- "${MEMROOS_BIND:-127.0.0.1}:${ORCHESTRATION_PORT:-3210}:3210"
environment:
ORCHESTRATION_DB_PATH: /data/orchestration.db
ORCHESTRATION_RETRY_LIMIT: ${ORCHESTRATION_RETRY_LIMIT:-2}
volumes:
- orchestration-data:/data
healthcheck:
test: ["CMD-SHELL", "python - <<'PY'\nimport urllib.request\nurllib.request.urlopen('http://127.0.0.1:3210/health', timeout=3)\nPY"]
interval: 30s
timeout: 5s
retries: 5
connmem:
build:
context: .
dockerfile: docker/Dockerfile.connmem
ports:
- "${MEMROOS_BIND:-127.0.0.1}:${CONNMEM_PORT:-3290}:3290"
environment:
CONNMEM_LEDGER_PATH: /data/connmem-ledger.db
volumes:
- connmem-data:/data
healthcheck:
# Single line, no heredoc: this is YAML *flow* style (inside [...]), where a
# double-quoted scalar spanning lines folds its newlines into spaces. The
# heredoc that used to live here therefore reached /bin/sh as one line, so
# the shell parsed the "(" itself and the check failed with
# 'Syntax error: "(" unexpected' every 30s — connmem reported unhealthy
# for days while /health was returning 200 the whole time.
test: ["CMD-SHELL", "python -c \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:3290/health', timeout=3)\""]
interval: 30s
timeout: 5s
retries: 5
volumes:
memroos-data:
connmem-data:
mem0-logs:
mem0-history:
ollama-data:
neo4j-data:
neo4j-logs:
orchestration-data: