Repository navigation
Expand file tree
/
Copy pathdocker-compose.yml
More file actions
130 lines (125 loc) · 5.14 KB
/
Copy pathdocker-compose.yml
File metadata and controls
130 lines (125 loc) · 5.14 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
name: langgraph-app
services:
postgres:
# ponytail: pgvector/pgvector is the official pgvector image
# (FROM postgres:16-bookworm + pgvector built from source). The
# upstream postgres:16-alpine does NOT bundle the vector extension
# and the KB migration (0005_*.sql) requires
# `CREATE EXTENSION IF NOT EXISTS vector` — without it, the first
# `pnpm db:migrate` dies with `extension "vector" is not available`.
# pg16 only ships bookworm/trixie Debian variants (no alpine tag),
# but `pgvector/pgvector:pg16` tracks whichever Debian base is
# current — wire-format (POSTGRES_*, PGDATA,
# /docker-entrypoint-initdb.d/) is inherited unchanged from the
# upstream image so no env / init scripts need to change.
# See docs/DB.md § KB retrieval.
image: pgvector/pgvector:pg16
restart: unless-stopped
environment:
POSTGRES_USER: ${POSTGRES_USER:-postgres}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-postgres}
POSTGRES_DB: ${POSTGRES_DB:-langgraph_app}
volumes:
- postgres-data:/var/lib/postgresql/data
ports:
- "5432:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-postgres}"]
interval: 5s
timeout: 3s
retries: 10
# LangGraph's Python runtime requires Redis at startup (pub/sub + queue
# init). FF_USE_REDIS_QUEUE=false isn't enough — the connection check
# still runs. Keep Redis in the stack; disable the queue in env if you
# don't need it.
redis:
image: redis:7-alpine
restart: unless-stopped
command: ["redis-server", "--save", "", "--appendonly", "no"]
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 10
app:
# ponytail: env_file replaces the per-var `environment:` block.
# Every value below (BETTER_AUTH_SECRET, OPENAI_API_KEY, all OAuth /
# Resend / Alchemy / R2 / Jina / Deno Deploy / LangSmith / observability
# tunings, etc) lives in `.env` per CLAUDE.md rule #12. Adding a new
# server-only env is now a one-line change in `.env.example`.
# Hardcoded service-to-service URLs (DATABASE_URL etc) stay in
# `environment:` — they're compose-internal, not part of `.env`.
#
# Dual-source: `image: ${IMAGE}` pulls the GHCR-published build
# (used by CD + VPS `docker compose up`); `build:` below is the
# local-dev fallback (`docker compose up` rebuilds when there's no
# image cached). `pull` reads `image`; `up` prefers an existing
# image over a rebuild, so they coexist cleanly.
image: ${IMAGE}
build:
context: .
# DATABASE_URL is the only remaining build-arg: `next build` opens
# a Postgres connection at module load (Better Auth runs
# migrations). The default in Dockerfile points at
# localhost:5432/langgraph_app; CD overrides via this arg to the
# build-pg container.
args:
DATABASE_URL: ${DATABASE_URL:-postgresql://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD:-postgres}@postgres:5432/${POSTGRES_DB:-langgraph_app}}
restart: unless-stopped
env_file: .env
environment:
ROLE: all
NODE_ENV: production
# App + LangGraph share the same DB; pass both env vars so each
# process picks up its preferred key (DATABASE_URL for the Next.js
# auth/checkpointer modules, POSTGRES_URI for langgraph-api).
DATABASE_URL: postgresql://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD:-postgres}@postgres:5432/${POSTGRES_DB:-langgraph_app}
POSTGRES_URI: postgresql://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD:-postgres}@postgres:5432/${POSTGRES_DB:-langgraph_app}
REDIS_URI: redis://redis:6379
LANGGRAPH_RUNTIME_EDITION: postgres
PORT: "2024"
LANGGRAPH_SERVER_HOST: 0.0.0.0
ports:
- "3000:3000"
- "2024:2024"
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
# ponytail: VPS-only sidecar cron. `profiles: [backup]` keeps it out of
# dev `docker compose up` — VPS runs `docker compose --profile backup up -d`
# once per deploy. Mounts the host docker socket so the script can
# `docker compose exec postgres pg_dump` against the live app container;
# the .ssh mount is read-only and never leaves the host's key tree.
# See scripts/backup-vps.sh.
backup:
profiles: ["backup"]
build:
context: ./docker/backup
dockerfile: Dockerfile
restart: unless-stopped
environment:
APP_DIR: /opt/langgraph-app
COMPOSE_FILE: /opt/langgraph-app/docker-compose.yml
BACKUP_DEST: /var/backups/langgraph
BACKUP_RETAIN_DAYS: "30"
RCLONE_REMOTE: ${RCLONE_REMOTE:-}
RCLONE_BWLIMIT: "${RCLONE_BWLIMIT:-0}"
volumes:
- /opt/langgraph-app:/opt/langgraph-app:ro
- /var/run/docker.sock:/var/run/docker.sock
- /var/backups/langgraph:/var/backups/langgraph
- ${HOME}/.ssh:/home/backup/.ssh:ro
- ./backup-crontab:/etc/crontabs/root:ro
- rclone-config:/home/backup/.config/rclone
user: "1000:1001"
read_only: true
tmpfs:
- /tmp
cap_drop: [ALL]
security_opt:
- no-new-privileges:true
volumes:
postgres-data:
rclone-config: