-
Notifications
You must be signed in to change notification settings - Fork 39
Expand file tree
/
Copy pathdocker-compose.yml
More file actions
627 lines (593 loc) · 24.1 KB
/
Copy pathdocker-compose.yml
File metadata and controls
627 lines (593 loc) · 24.1 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
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
# =============================================================================
# ABI (Agentic Brain Infrastructure) Docker Compose Configuration
# =============================================================================
# This file defines a complete AI agent ecosystem with:
# - Multi-model AI capabilities (chat, embeddings, reasoning)
# - Knowledge graph storage (Apache Jena Fuseki + TDB2)
# - Agent memory persistence (PostgreSQL)
# - Workflow orchestration (Dagster)
# =============================================================================
# models:
# gemma3:
# model: ai/gemma3
# context_size: 8192
# runtime_flags:
# - --memory=4g
# - --cpus=2
# embeddinggemma:
# model: ai/embeddinggemma
# context_size: 2048
# runtime_flags:
# - --memory=2g
# - --cpus=1
# qwen3:
# model: ai/qwen3
# context_size: 32768
# runtime_flags:
# - --memory=12g
# - --cpus=4
x-abi-template: &abi_template
build:
context: .
dockerfile: ./.deploy/docker/images/Dockerfile
restart: unless-stopped
ports:
- 9879:9879 # REST API endpoint - http://localhost:9879
- 8501:8501 # Streamlit web interface - http://localhost:8501
volumes:
# Development volumes for hot-reload
- .:/app
# - ./scripts:/app/scripts
# - ./uv.lock:/app/uv.lock
# - ./config.yaml:/app/config.yaml
# - ./pyproject.toml:/app/pyproject.toml
# - ./Makefile:/app/Makefile
# - ./storage:/app/storage # Local file storage
# - ./src:/app/src # Source code
# - ./libs:/app/libs # Libraries
# - ./README.md:/app/README.md
# - ./.env:/app/.env
# Cached volumes for performance
- venv_cache:/app/.venv # Python virtual environment cache
- huggingface_cache:/root/.cache/huggingface # Docling / HF model weights
environment:
- ENV=local
- LOG_LEVEL=DEBUG
- PYTHONPATH=/app
- POSTGRES_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:${POSTGRES_PORT}/${POSTGRES_DB}
- VECTOR_STORE_ADAPTER=qdrant
- QDRANT_HOST=qdrant
- QDRANT_PORT=${QDRANT_PORT}
# env_file:
# - .env # Load environment variables (API keys, AI_MODE, etc.)
depends_on:
postgres:
condition: service_healthy
rabbitmq:
condition: service_healthy
qdrant:
condition: service_healthy
redis:
condition: service_healthy
fuseki:
condition: service_healthy
minio:
condition: service_healthy
healthcheck:
test: ["CMD-SHELL", "for i in 1 2 3; do curl -fsS http://localhost:9879/ >/dev/null || exit 1; sleep 2; done"]
interval: 10s
timeout: 10s
start_period: 3m
retries: 6
networks:
- abi-network
services:
nexus-web:
build:
context: libs/naas-abi/naas_abi/apps/nexus/
dockerfile: Dockerfile
restart: unless-stopped
ports:
- ${NEXUS_WEB_PORT}:3000
volumes:
- ./libs/naas-abi/naas_abi/apps/nexus/apps/web:/app/apps/web
# Preserve the image's node_modules under the bind mount above so the
# host's missing/stale node_modules doesn't mask the deps installed at
# build time (which is what produces `sh: next: not found`).
- /app/apps/web/node_modules
environment:
- NODE_ENV=development
- PORT=3000
- NEXT_PUBLIC_NEXUS_ENV=local
- NEXUS_API_URL=${NEXUS_API_URL}
- NEXT_PUBLIC_API_URL=${NEXUS_API_URL}
- NEXUS_INTERNAL_API_URL=http://abi:9879
- NEXT_PUBLIC_SITE_URL=https://${PUBLIC_WEB_HOST}
- NEXT_PUBLIC_WS_PATH=/ws/socket.io
networks:
- abi-network
caddy:
image: caddy:2-alpine
pull_policy: always
restart: unless-stopped
ports:
- 80:80
- 443:443
environment:
- PUBLIC_WEB_HOST=${PUBLIC_WEB_HOST:-localhost}
- PUBLIC_API_HOST=${PUBLIC_API_HOST:-api.localhost}
- HEADSCALE_SERVER_URL=${HEADSCALE_SERVER_URL:-headscale.localhost}
volumes:
- ./.deploy/docker/Caddyfile:/etc/caddy/Caddyfile:ro
- caddy_data:/data
- caddy_config:/config
depends_on:
- nexus-web
- abi
networks:
- abi-network
# -----------------------------------------------------------------------------
# ABI Main Service - AI Agent Coordinator
# -----------------------------------------------------------------------------
# The core ABI application that routes between specialized AI agents
# and coordinates multi-model conversations
abi:
<<: *abi_template
command: ["uv", "run", "--no-dev", "python", "-m", "naas_abi_core.apps.api.api"] # Start ABI application
# models:
# - gemma3 # Primary conversational AI model
# - embeddinggemma # Text embedding model for semantic search
# - qwen3 # Advanced reasoning model
mcp-server:
build:
context: .
dockerfile: ./.deploy/docker/images/Dockerfile
restart: unless-stopped
depends_on:
abi:
condition: service_healthy
command:
["uv", "run", "--no-dev", "python", "-m", "naas_abi_core.apps.mcp.mcp_server"]
environment:
- ABI_API_BASE=http://abi:9879
- ABI_API_KEY=abi
- MCP_TRANSPORT=http
ports:
- 8000:8000
networks:
- abi-network
# -----------------------------------------------------------------------------
# Coder - per-user browser coding environments (control plane)
# -----------------------------------------------------------------------------
# Orchestrates isolated VS Code (code-server) workspaces — one branch per
# workspace. abi's `coding_environment` core service drives it over its REST
# API; Nexus embeds the editor via the same-registrable-domain Caddy routes
# (coder.${PUBLIC_WEB_HOST} + *.coder.${PUBLIC_WEB_HOST}), so the editor's
# session cookie is first-party inside the Nexus iframe.
#
# Uses the shared Postgres (the `coder` DB is created by
# initdb/003-create-coder-db.sql). The docker.sock mount lets Coder's Docker
# template provision workspaces as sibling containers; for production prefer a
# docker-socket-proxy or a Kubernetes template over a bind-mounted socket.
coder:
image: ghcr.io/coder/coder:v2.34.1
pull_policy: always
restart: unless-stopped
depends_on:
postgres:
condition: service_healthy
environment:
CODER_PG_CONNECTION_URL: "postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:${POSTGRES_PORT}/coder?sslmode=disable"
CODER_HTTP_ADDRESS: "0.0.0.0:7080"
CODER_ACCESS_URL: "https://coder.${PUBLIC_WEB_HOST:-localhost}"
CODER_WILDCARD_ACCESS_URL: "*.coder.${PUBLIC_WEB_HOST:-localhost}"
# Allow the Nexus web origin to frame Coder app surfaces (Coder's default
# CSP is frame-ancestors 'self').
CODER_ADDITIONAL_CSP_POLICY: "frame-ancestors https://${PUBLIC_WEB_HOST:-localhost}"
CODER_TELEMETRY_ENABLE: "false"
# The admin token Nexus uses to manage Coder is an *admin* token, capped by
# max-admin-token-lifetime (default 168h/7d) — so it expired weekly and
# broke the IDE page. Allow up to ~1y.
CODER_MAX_ADMIN_TOKEN_LIFETIME: "8760h"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- coder_data:/home/coder
networks:
- abi-network
# -----------------------------------------------------------------------------
# Forgejo - git host + in-app code review backend (source_control service)
# -----------------------------------------------------------------------------
# Authoritative git for the monorepo (one branch per workspace). The Nexus
# review UI is built on its REST API (users do not browse Forgejo directly),
# so it is proxied internally at git.${PUBLIC_WEB_HOST}. Background mirroring
# to GitHub is configured per-repo. Uses the shared Postgres (the `forgejo` DB
# is created by initdb/004-create-forgejo-db.sql).
forgejo:
image: codeberg.org/forgejo/forgejo:9
pull_policy: always
restart: unless-stopped
depends_on:
postgres:
condition: service_healthy
environment:
FORGEJO__database__DB_TYPE: "postgres"
FORGEJO__database__HOST: "postgres:${POSTGRES_PORT}"
FORGEJO__database__NAME: "forgejo"
FORGEJO__database__USER: "${POSTGRES_USER}"
FORGEJO__database__PASSWD: "${POSTGRES_PASSWORD}"
FORGEJO__server__DOMAIN: "git.${PUBLIC_WEB_HOST:-localhost}"
FORGEJO__server__ROOT_URL: "https://git.${PUBLIC_WEB_HOST:-localhost}/"
FORGEJO__server__HTTP_PORT: "3000"
FORGEJO__service__DISABLE_REGISTRATION: "true"
FORGEJO__security__INSTALL_LOCK: "true"
volumes:
- forgejo_data:/data
networks:
- abi-network
# -----------------------------------------------------------------------------
# PostgreSQL Database - Agent Memory & Conversation Storage
# -----------------------------------------------------------------------------
# Stores agent conversation history, checkpoints, and persistent memory
# Used by LangGraph for agent state management and memory retrieval
postgres:
image: postgres:17-alpine # Latest stable PostgreSQL
pull_policy: always
restart: unless-stopped
environment:
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB} # Database for agent conversations and memory
ports:
- ${POSTGRES_PORT}:${POSTGRES_PORT} # PostgreSQL database - postgresql://abi_user:abi_password@localhost:${POSTGRES_PORT}/abi_memory
volumes:
- postgres_data:/var/lib/postgresql/data # Persistent data storage
- ./.deploy/docker/postgres/initdb:/docker-entrypoint-initdb.d:ro # Bootstrap additional databases
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
interval: 5s
timeout: 5s
retries: 10
networks:
- abi-network
# -----------------------------------------------------------------------------
# Apache Jena Fuseki (TDB2) - Knowledge Graph Database (RDF/SPARQL)
# -----------------------------------------------------------------------------
# Stores structured knowledge as RDF triples for semantic reasoning
# TDB2 provides transactional storage with SPARQL query/update endpoints
fuseki:
# Staying on stain/jena-fuseki (the older community image) for now — this is
# only about making the *current* store reliable. Apache does NOT publish a
# prebuilt Fuseki image on Docker Hub (the official path is the build-your-own
# jena-fuseki-docker kit), so moving off this is a real migration (build an
# image + back up + dump-and-reload the TDB2 data), tracked separately.
# `pull_policy: always` dropped on purpose: a boot now uses the locally cached
# image instead of requiring a registry pull every start (one less thing that
# can fail a restart). Re-add it if you'd rather always re-pull.
image: stain/jena-fuseki:latest
restart: unless-stopped
ports:
- 3030:3030
volumes:
- fuseki_data:/fuseki
# Cap Docker memory so the JVM can't balloon and get OOM-killed mid-write —
# an unclean kill is what leaves TDB2's node table/journal corrupt. Keep
# mem_limit comfortably above JVM_ARGS -Xmx: TDB2 memory-maps its files
# off-heap, so it needs headroom on top of the Java heap. Tune to host/data.
mem_limit: 4g
environment:
- ADMIN_PASSWORD=${FUSEKI_ADMIN_PASSWORD}
- JVM_ARGS=-Xmx2g
command: >-
bash -c '
mkdir -p /fuseki/configuration /fuseki/databases/ds &&
printf "@prefix : <#> .
@prefix fuseki: <http://jena.apache.org/fuseki#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix ja: <http://jena.hpl.hp.com/2005/11/Assembler#> .
@prefix tdb2: <http://jena.apache.org/2016/tdb#> .
:service rdf:type fuseki:Service ;
fuseki:name \"ds\" ;
fuseki:endpoint [ fuseki:operation fuseki:query ;
fuseki:name \"sparql\" ;
ja:context [ ja:cxtName \"tdb:unionDefaultGraph\" ;
ja:cxtValue true ] ] ;
fuseki:endpoint [ fuseki:operation fuseki:query ;
fuseki:name \"query\" ;
ja:context [ ja:cxtName \"tdb:unionDefaultGraph\" ;
ja:cxtValue true ] ] ;
fuseki:endpoint [ fuseki:operation fuseki:update ;
fuseki:name \"update\" ] ;
fuseki:endpoint [ fuseki:operation fuseki:gsp-r ;
fuseki:name \"get\" ] ;
fuseki:endpoint [ fuseki:operation fuseki:gsp-rw ;
fuseki:name \"data\" ] ;
fuseki:dataset :dataset .
:dataset rdf:type tdb2:DatasetTDB2 ;
tdb2:location \"/fuseki/databases/ds\" .
" > /fuseki/configuration/ds.ttl &&
/jena-fuseki/fuseki-server'
healthcheck:
# Probe the DATASET, not just the web root. `curl /` returns 200 even when
# the `ds` dataset failed to attach or is corrupt, which lets dependents
# start against a dead store and then 500 on the first query. `ASK { ?s ?p
# ?o }` short-circuits at the first triple (cheap) but opens TDB2 storage,
# so a broken dataset correctly marks the container unhealthy. Authenticated
# because the server runs with ADMIN_PASSWORD; `$$` is a literal `$` in the
# container shell (Compose would otherwise treat `$ADMIN_PASSWORD` as a
# host-side variable).
test: ["CMD-SHELL", "curl -fsS -u admin:$$ADMIN_PASSWORD 'http://localhost:3030/ds/query?query=ASK%20%7B%20%3Fs%20%3Fp%20%3Fo%20%7D' >/dev/null || exit 1"]
interval: 10s
timeout: 10s
retries: 10
# Grace window for TDB2 to attach/recover before probes count; the
# interval×retries budget below still covers slower recoveries.
start_period: 20s
networks:
- abi-network
# -----------------------------------------------------------------------------
# YasGUI - SPARQL Query Interface
# -----------------------------------------------------------------------------
# Web-based SPARQL query editor and visualizer
# Allows interactive exploration of the knowledge graph
yasgui:
image: erikap/yasgui:latest
pull_policy: always
restart: unless-stopped
platform: linux/amd64 # Force x86_64 for compatibility with Apple Silicon
ports:
- 3000:80 # YasGUI SPARQL editor - http://localhost:3000
environment:
- DEFAULT_SPARQL_ENDPOINT=http://127.0.0.1:3030/ds/query
depends_on:
- fuseki
networks:
- abi-network
# -----------------------------------------------------------------------------
# Dagster - Workflow Orchestration & Data Pipeline Management
# -----------------------------------------------------------------------------
# Manages data pipelines, asset materialization, and workflow scheduling
# Provides web UI for monitoring and managing data operations
dagster:
build:
context: .
dockerfile: ./.deploy/docker/images/Dockerfile
restart: unless-stopped
ports:
- 3001:3001 # Dagster web UI - http://localhost:3001 (avoiding conflict with YasGUI on 3000)
volumes:
# - ./scripts:/app/scripts
# - ./uv.lock:/app/uv.lock
# - ./config.local.yaml:/app/config.yaml # We mount the config.local.yaml as it's configured to run from within the containers network.
# - ./pyproject.toml:/app/pyproject.toml
# - ./src:/app/src
# - ./README.md:/app/README.md
# - ./storage:/app/storage
# - .env:/app/.env
- .:/app
- venv_cache:/app/.venv
- dagster_home:/app/.dagster # Dagster metadata and run history
- ./.deploy/docker/dagster/dagster.yaml:/app/.dagster/dagster.yaml:ro
environment:
- ENV=local
- PYTHONPATH=/app
- DAGSTER_HOME=/app/.dagster
- AWS_ACCESS_KEY_ID=${MINIO_ROOT_USER}
- AWS_SECRET_ACCESS_KEY=${MINIO_ROOT_PASSWORD}
- DAGSTER_S3_LOG_BUCKET=dagster-logs
- MINIO_ENDPOINT_URL=http://minio:${MINIO_PORT}
- MINIO_REGION_NAME=us-east-1
- DAGSTER_PG_HOSTNAME=postgres
- DAGSTER_PG_USERNAME=${POSTGRES_USER}
- DAGSTER_PG_PASSWORD=${POSTGRES_PASSWORD}
- DAGSTER_PG_DB=${POSTGRES_DB}
command:
- /bin/bash
- -c
- 'uv run dagster dev --host 0.0.0.0 --port 3001 -m naas_abi_core.apps.dagster.dagster'
networks:
- abi-network
# -----------------------------------------------------------------------------
# MinIO - S3-compatible Object Storage for Dagster Compute Logs
# -----------------------------------------------------------------------------
minio:
image: minio/minio:RELEASE.2025-09-07T16-13-09Z
pull_policy: always
restart: unless-stopped
command: server /data --console-address :9001
environment:
MINIO_ROOT_USER: ${MINIO_ROOT_USER}
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD}
ports:
- ${MINIO_PORT}:${MINIO_PORT} # S3 API endpoint - http://localhost:${MINIO_PORT}
- 9001:9001 # MinIO console - http://localhost:9001
volumes:
- minio_data:/data
healthcheck:
test: ["CMD-SHELL", "curl -fsS http://localhost:9000/minio/health/live >/dev/null || exit 1"]
interval: 10s
timeout: 5s
retries: 10
networks:
- abi-network
# mc container to initialize the bucket on startup
minio-init:
image: minio/mc:RELEASE.2025-08-13T08-35-41Z
pull_policy: always
depends_on:
minio:
condition: service_healthy
entrypoint: >-
/bin/sh -c "
until /usr/bin/mc alias set ${MINIO_ROOT_USER} http://minio:${MINIO_PORT} ${MINIO_ROOT_USER} ${MINIO_ROOT_PASSWORD}; do sleep 1; done &&
/usr/bin/mc mb -p ${MINIO_ROOT_USER}/dagster-logs || true &&
/usr/bin/mc mb -p ${MINIO_ROOT_USER}/abi || true
"
networks:
- abi-network
# -----------------------------------------------------------------------------
# RabbitMQ - Message Bus (AMQP)
# -----------------------------------------------------------------------------
rabbitmq:
image: rabbitmq:3-management
pull_policy: always
restart: unless-stopped
# Pin the node name (rabbit@rabbitmq). Without a stable hostname RabbitMQ
# derives its Mnesia dir from the random container id, so durable queues are
# lost whenever the container is recreated (e.g. `abi stack snapshot restore`
# or a host migration). Applying this resets the node once.
hostname: rabbitmq
ports:
- 5672:5672 # AMQP
- 15672:15672 # Management UI - http://localhost:15672
environment:
RABBITMQ_DEFAULT_USER: ${RABBITMQ_USER}
RABBITMQ_DEFAULT_PASS: ${RABBITMQ_PASSWORD}
volumes:
- rabbitmq_data:/var/lib/rabbitmq
healthcheck:
test: ["CMD", "rabbitmq-diagnostics", "ping"]
interval: 10s
timeout: 5s
start_period: 30s
retries: 10
networks:
- abi-network
# -----------------------------------------------------------------------------
# Redis - In-memory Cache / Message Broker
# -----------------------------------------------------------------------------
redis:
image: redis:7-alpine
pull_policy: always
restart: unless-stopped
command: ["redis-server", "--appendonly", "yes"]
ports:
- 6379:6379
volumes:
- redis_data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 10
networks:
- abi-network
# -----------------------------------------------------------------------------
# Redis Commander - Web UI for Redis management
# -----------------------------------------------------------------------------
redis-commander:
image: rediscommander/redis-commander:latest
pull_policy: always
restart: unless-stopped
ports:
- 8082:8081 # Redis Commander UI - http://localhost:8082
environment:
- REDIS_HOSTS=local:redis:${REDIS_PORT}
depends_on:
- redis
networks:
- abi-network
# -----------------------------------------------------------------------------
# Service Portal - Single-page navigation for all HTTP UIs
# -----------------------------------------------------------------------------
service-portal:
image: nginx:alpine
pull_policy: always
restart: unless-stopped
ports:
- 8080:80 # Service portal - http://localhost:8080
environment:
- PUBLIC_WEB_HOST=${PUBLIC_WEB_HOST:-localhost}
- PUBLIC_API_HOST=${PUBLIC_API_HOST:-api.localhost}
- NGINX_ENVSUBST_TEMPLATE_DIR=/usr/share/nginx/templates
- NGINX_ENVSUBST_TEMPLATE_SUFFIX=.template
- NGINX_ENVSUBST_OUTPUT_DIR=/usr/share/nginx/html
volumes:
- ./.deploy/docker/service-portal:/usr/share/nginx/templates:ro
networks:
- abi-network
# -----------------------------------------------------------------------------
# Neptune Graph Explorer
# -----------------------------------------------------------------------------
graph-explorer:
image: public.ecr.aws/neptune/graph-explorer
restart: unless-stopped
environment:
- HOST=graph-explorer.${PUBLIC_WEB_HOST:-localhost}
networks:
- abi-network
# -----------------------------------------------------------------------------
# Headscale - Self-hosted Tailscale control server
# -----------------------------------------------------------------------------
# Not needed by the coding workspaces (Coder uses its own tailnet coordinator),
# and the :stable image drifted to a config schema this config doesn't satisfy,
# so it crash-looped. Gated behind a profile so it no longer starts by default;
# run `docker compose --profile headscale up -d headscale` to bring it back.
headscale:
profiles: ["headscale"]
image: docker.io/headscale/headscale:stable
pull_policy: always
restart: unless-stopped
command: ["serve"]
ports:
- ${HEADSCALE_SERVER_PORT}:8080
- ${HEADSCALE_METRICS_PORT}:9090
- ${HEADSCALE_GRPC_PORT}:50443
volumes:
- ./.deploy/docker/headscale/config.yaml:/etc/headscale/config.yaml:ro
- ./.deploy/docker/headscale/extra-records.json:/var/lib/headscale/extra-records.json:ro
- headscale_data:/var/lib/headscale
- headscale_run:/var/run/headscale
healthcheck:
test: ["CMD", "headscale", "health"]
interval: 15s
timeout: 5s
retries: 10
start_period: 30s
networks:
- abi-network
# =============================================================================
# PERSISTENT VOLUMES
# =============================================================================
# Named volumes for persistent data storage across container restarts
qdrant:
image: qdrant/qdrant:latest
pull_policy: always
restart: unless-stopped
ports:
- 6333:6333
- 6334:6334
volumes:
- qdrant_storage:/qdrant/storage
environment:
- QDRANT__SERVICE__HTTP_PORT=6333
- QDRANT__SERVICE__GRPC_PORT=6334
healthcheck:
test: ["CMD-SHELL", "bash -ec 'exec 3<>/dev/tcp/127.0.0.1/6333'"]
interval: 10s
timeout: 5s
retries: 10
networks:
- abi-network
volumes:
venv_cache: # Python virtual environment cache for faster startup
huggingface_cache: # Hugging Face model cache (docling layout / table models)
fuseki_data: # RDF triple store data (knowledge graph persistence)
postgres_data: # PostgreSQL database files (agent memory persistence)
coder_data: # Coder control-plane state (home dir)
forgejo_data: # Forgejo git repositories + state
dagster_home: # Dagster metadata, run history, and configuration
qdrant_storage: # Qdrant vector store data
headscale_data: # Headscale state and sqlite database
headscale_run: # Headscale unix socket runtime data
minio_data: # MinIO object storage data
rabbitmq_data: # RabbitMQ message bus data
redis_data: # Redis persistence data
caddy_data: # Caddy certificates and ACME data
caddy_config: # Caddy runtime configuration
# =============================================================================
# NETWORK CONFIGURATION
# =============================================================================
# Custom bridge network for service-to-service communication
networks:
abi-network:
driver: bridge # Default Docker bridge network for internal communication