-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdocker-compose.prod.yml
More file actions
177 lines (171 loc) · 6.62 KB
/
Copy pathdocker-compose.prod.yml
File metadata and controls
177 lines (171 loc) · 6.62 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
# AF Stack production docker-compose.
#
# What this gets you in 3 bullets:
# - Runtime + dashboard + AgentField control plane behind Caddy with
# automatic Let's Encrypt TLS on {$AF_STACK_DOMAIN}.
# - NO Postgres or MinIO services — point at managed/external ones via
# AF_STACK_DATABASE_URL + AF_STACK_S3_*.
# - NO docker.sock mount on the runtime. Sandbox runs via e2b (default)
# or gvisor adapter. Stronger isolation, no privileged container.
#
# Required env (set in .env next to this file):
# AF_STACK_DOMAIN public hostname for Caddy
# ACME_EMAIL Let's Encrypt registration email
# AF_STACK_DATABASE_URL postgres://user:pass@host:5432/afstack?sslmode=require
# AGENTFIELD_STORAGE_POSTGRES_URL postgres://user:pass@host:5432/agentfield?sslmode=require
# AF_STACK_KMS_KEY openssl rand -hex 32
# AF_STACK_AUTH_SECRET openssl rand -hex 32
# AF_STACK_S3_ENDPOINT https://fly.storage.tigris.dev (or R2/AWS)
# AF_STACK_S3_BUCKET af-stack-prod
# AF_STACK_S3_ACCESS_KEY <s3 access key>
# AF_STACK_S3_SECRET_KEY <s3 secret key>
# OPENROUTER_API_KEY (or ANTHROPIC_API_KEY / OPENAI_API_KEY)
# E2B_API_KEY required when AF_STACK_SANDBOX_ADAPTER=e2b
#
# Verify the file BEFORE deploying:
# docker compose -f docker-compose.prod.yml config --quiet
#
# Bring up:
# docker compose -f docker-compose.prod.yml up -d
#
# Notes on resource limits:
# `deploy.resources.limits` are enforced ONLY in swarm mode. In plain
# `docker compose up` they are advisory. To enforce on a single node,
# either run `docker stack deploy -c docker-compose.prod.yml af-stack`
# or migrate the limits to `mem_limit` / `cpus` (compose v2.x supports
# both natively; we keep `deploy.resources.limits` so a swarm upgrade is
# a one-liner).
services:
caddy:
image: caddy:2-alpine
restart: unless-stopped
ports:
- "80:80"
- "443:443"
- "443:443/udp" # HTTP/3
environment:
AF_STACK_DOMAIN: ${AF_STACK_DOMAIN}
ACME_EMAIL: ${ACME_EMAIL}
METRICS_ALLOW: ${METRICS_ALLOW:-10.0.0.0/8}
volumes:
- ./deploy/caddy/Caddyfile:/etc/caddy/Caddyfile:ro
- caddy-data:/data
- caddy-config:/config
networks:
- frontend
depends_on:
- runtime
- dashboard
deploy:
resources:
limits:
cpus: "0.50"
memory: "256M"
agentfield:
# Pinned off :latest for reproducible prod deploys. Override AGENTFIELD_VERSION
# to move the AgentField control plane deliberately (published on Docker Hub).
image: agentfield/control-plane:${AGENTFIELD_VERSION:-v0.1.96}
restart: unless-stopped
environment:
AGENTFIELD_STORAGE_MODE: postgres
AGENTFIELD_STORAGE_POSTGRES_URL: ${AGENTFIELD_STORAGE_POSTGRES_URL}
AGENTFIELD_STORAGE_POSTGRES_ENABLE_MEMORY_FALLBACK: "false"
AGENTFIELD_STORAGE_POSTGRES_ENABLE_DID_FALLBACK: "false"
AGENTFIELD_STORAGE_POSTGRES_ENABLE_VC_FALLBACK: "false"
AGENTFIELD_STORAGE_POSTGRES_ENABLE_AUTO_MIGRATION: "true"
OPENROUTER_API_KEY: ${OPENROUTER_API_KEY:-}
ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY:-}
OPENAI_API_KEY: ${OPENAI_API_KEY:-}
networks:
- backend
deploy:
resources:
limits:
cpus: "1.0"
memory: "512M"
runtime:
# Pinned off :latest. AF_STACK_VERSION selects the runtime + dashboard image
# tag together; set it to the release CI publishes (e.g. a git SHA or vX.Y.Z)
# so prod pulls a reproducible build instead of a moving :latest.
image: ghcr.io/agent-field/af-stack-runtime:${AF_STACK_VERSION:-0.1.0}
restart: unless-stopped
environment:
AF_STACK_HTTP_ADDR: ":8080"
AF_STACK_METRICS_ADDR: ":9090"
AF_STACK_DATABASE_URL: ${AF_STACK_DATABASE_URL}
AF_STACK_AGENTFIELD_URL: http://agentfield:8080
AF_STACK_KMS_KEY: ${AF_STACK_KMS_KEY}
AF_STACK_MODULE_MULTI_TENANCY: ${AF_STACK_MODULE_MULTI_TENANCY:-true}
OPENROUTER_API_KEY: ${OPENROUTER_API_KEY:-}
ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY:-}
OPENAI_API_KEY: ${OPENAI_API_KEY:-}
# External S3 (Tigris/R2/AWS) — NOT MinIO.
AF_STACK_S3_ADAPTER: s3
AF_STACK_S3_ENDPOINT: ${AF_STACK_S3_ENDPOINT}
AF_STACK_S3_BUCKET: ${AF_STACK_S3_BUCKET}
AF_STACK_S3_ACCESS_KEY: ${AF_STACK_S3_ACCESS_KEY}
AF_STACK_S3_SECRET_KEY: ${AF_STACK_S3_SECRET_KEY}
AF_STACK_S3_REGION: ${AF_STACK_S3_REGION:-us-east-1}
# Sandbox: e2b by default (hosted, strong isolation, no docker.sock).
# Trade-off vs docker adapter:
# - e2b: no privileged mount, no host kernel exposure, costs per
# sandbox-second, requires E2B_API_KEY.
# - gvisor: stays on-box, user-space kernel, free, requires gvisor
# installed on host (Phase 9.2).
# - docker: easiest in dev, NEVER in prod — full host kernel exposed
# via /var/run/docker.sock.
AF_STACK_SANDBOX_ADAPTER: ${AF_STACK_SANDBOX_ADAPTER:-e2b}
E2B_API_KEY: ${E2B_API_KEY:-}
AF_STACK_E2B_BASE_URL: ${AF_STACK_E2B_BASE_URL:-https://api.e2b.dev}
LOG_FORMAT: json
LOG_LEVEL: ${LOG_LEVEL:-info}
OTEL_EXPORTER_OTLP_ENDPOINT: ${OTEL_EXPORTER_OTLP_ENDPOINT:-}
OTEL_SERVICE_NAME: af-stack-runtime
networks:
- frontend
- backend
depends_on:
- agentfield
deploy:
replicas: 2
resources:
limits:
cpus: "2.0"
memory: "1G"
# NOTE: NO `user: "0:0"` override here. The distroless image's nonroot
# user (uid 65532) is used. NO docker.sock mount.
dashboard:
image: ghcr.io/agent-field/af-stack-dashboard:${AF_STACK_VERSION:-0.1.0}
restart: unless-stopped
environment:
DATABASE_URL: ${AF_STACK_DATABASE_URL}
AF_STACK_AUTH_SECRET: ${AF_STACK_AUTH_SECRET}
BETTER_AUTH_URL: https://${AF_STACK_DOMAIN}
BETTER_AUTH_TRUSTED_ORIGINS: ${BETTER_AUTH_TRUSTED_ORIGINS:-}
RUNTIME_URL: http://runtime:8080
NEXT_PUBLIC_RUNTIME_URL: https://${AF_STACK_DOMAIN}
GOOGLE_CLIENT_ID: ${GOOGLE_CLIENT_ID:-}
GOOGLE_CLIENT_SECRET: ${GOOGLE_CLIENT_SECRET:-}
NODE_ENV: production
NEXT_TELEMETRY_DISABLED: "1"
networks:
- frontend
depends_on:
- runtime
deploy:
replicas: 1
resources:
limits:
cpus: "1.0"
memory: "512M"
networks:
# Caddy <-> runtime/dashboard. Public-facing edge.
frontend:
driver: bridge
# runtime <-> agentfield (+ external Postgres / S3 over the host network).
# Dashboard is NOT on this network.
backend:
driver: bridge
volumes:
caddy-data:
caddy-config: