-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdocker-compose.yml
More file actions
203 lines (196 loc) · 8.31 KB
/
Copy pathdocker-compose.yml
File metadata and controls
203 lines (196 loc) · 8.31 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
services:
postgres:
image: postgres:16-alpine
restart: unless-stopped
environment:
POSTGRES_USER: ramp
POSTGRES_PASSWORD: ramp
POSTGRES_DB: ramp
ports:
# Override by setting RAMP_PG_PORT in the environment (or via --env-file
# from scripts/devstack.sh). Default 55432 avoids clashing with local
# Postgres or testcontainers running on 5432.
- "${RAMP_PG_PORT:-55432}:5432"
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ramp -d ramp"]
interval: 5s
timeout: 3s
retries: 5
redis:
image: redis:7-alpine
restart: unless-stopped
ports:
- "${RAMP_REDIS_PORT:-56379}:6379"
command: ["redis-server", "--save", "", "--loglevel", "warning"]
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 5
# Local-dev TigerBeetle for running the Exchange binary against a real ledger.
# (Integration tests do NOT use this service — they spin up their own container
# via internal/testutil.StartSharedTigerBeetle.) A single-replica cluster: the
# data file is formatted on first boot only (the volume persists it across
# restarts), then started. Point the Exchange at it with
# EXCHANGE_BILLING_TB_ADDRESS=127.0.0.1:${RAMP_TB_PORT:-53000}.
#
# --development relaxes Direct IO (Docker/overlay/macOS volumes) and shrinks
# caches. security_opt/cap_add are mandatory: Docker >= 25 blocks io_uring
# without seccomp=unconfined (error: PermissionDenied), and TigerBeetle locks
# memory (macOS error: SystemResources without IPC_LOCK).
tigerbeetle:
image: ghcr.io/tigerbeetle/tigerbeetle:0.17.8
restart: unless-stopped
entrypoint: ["sh", "-c"]
command:
- |
set -e
[ -f /data/0_0.tigerbeetle ] || /tigerbeetle format --cluster=0 --replica=0 --replica-count=1 --development /data/0_0.tigerbeetle
exec /tigerbeetle start --addresses=0.0.0.0:3000 --cache-grid=256MiB --development /data/0_0.tigerbeetle
security_opt:
- "seccomp=unconfined"
cap_add:
- "IPC_LOCK"
# The image is busybox-based (it already runs `sh -c` above), so `nc` is
# available for a TCP probe. This proves the replica is accepting connections,
# which is what callers wait for — not that the cluster answers correctly; only
# a real request does that. Formatting runs before `start`, so the port opening
# is the honest readiness edge.
healthcheck:
test: ["CMD", "nc", "-z", "127.0.0.1", "3000"]
interval: 5s
timeout: 3s
retries: 5
ports:
- "${RAMP_TB_PORT:-53000}:3000"
volumes:
- tbdata:/data
# Local-dev Vault for running the Identity Service against a real key store.
# (Integration tests do NOT use this service — they spin up their own container
# via internal/testutil.StartSharedVault.) Dev mode is in-memory and starts
# UNSEALED with a fixed root token: perfect for a laptop, unacceptable anywhere
# else, which is why nothing outside this file knows that token. The keys the
# service mints here vanish when the container stops, exactly as dev keys should.
vault:
image: hashicorp/vault:1.20
restart: unless-stopped
environment:
VAULT_DEV_ROOT_TOKEN_ID: ${RAMP_VAULT_DEV_TOKEN:-dev-root-token}
VAULT_DEV_LISTEN_ADDRESS: 0.0.0.0:8200
# IPC_LOCK lets Vault lock its memory so secrets are never swapped to disk.
cap_add:
- "IPC_LOCK"
# Bound to the loopback interface, not 0.0.0.0: this Vault boots unsealed with a
# root token that is a published default in this file, and it is the one service
# here holding agents' private keys. On a shared network an unbound port hands
# every one of them to anyone who can reach the host.
ports:
- "127.0.0.1:${RAMP_VAULT_PORT:-58200}:8200"
healthcheck:
test: ["CMD", "vault", "status", "-address=http://127.0.0.1:8200"]
interval: 5s
timeout: 3s
retries: 5
# ── Zitadel: the upstream OIDC provider for developer sign-up. Integration tests
# do not use these; the `zitadel` test tier starts its own pair via
# internal/testutil.StartSharedZitadel. Bring them up with `make zitadel-up`,
# which runs scripts/zitadel-bootstrap.sh to provision the org, the OIDC app and
# — when GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET are exported — Google
# federation. Sign-in then runs through the Identity service's own flow.
#
# The `zitadel` profile keeps all three out of the default set, so a bare
# `docker compose up` skips their ~30-45s boot. Corollary: a bare
# `docker compose down` does not stop them either — use `make zitadel-down`.
zitadel-db:
image: postgres:17-alpine
restart: unless-stopped
profiles: ["zitadel"]
environment:
POSTGRES_USER: zitadel
POSTGRES_PASSWORD: zitadel
POSTGRES_DB: zitadel
# Not published: only Zitadel talks to it, over the compose network.
volumes:
- zitadeldata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U zitadel -d zitadel"]
interval: 5s
timeout: 3s
retries: 5
# v3.4.9 is PINNED: v4.x has an open migration-03 boot crash, and v3's Login V1
# fixes the external-IdP callback path Google must be told about (see runbook).
zitadel:
image: ghcr.io/zitadel/zitadel:v3.4.9
restart: unless-stopped
profiles: ["zitadel"]
command:
- start-from-init
- --masterkey
- ${RAMP_ZITADEL_MASTERKEY:-MasterkeyNeedsToHave32Characters}
- --tlsMode
- disabled
- --steps
- /etc/zitadel/init-steps.yaml
environment:
# ExternalDomain + ExternalPort are baked into the issuer, JWKS, and endpoint
# URLs AT INIT, not derived from the request Host. So the published port is
# fixed, never ephemeral, and changing it after first boot needs a
# `make zitadel-down` (which drops the volumes) first.
ZITADEL_EXTERNALDOMAIN: ${RAMP_ZITADEL_DOMAIN:-localhost}
ZITADEL_EXTERNALPORT: ${RAMP_ZITADEL_PORT:-58080}
ZITADEL_EXTERNALSECURE: "false"
ZITADEL_PORT: "8080"
ZITADEL_TLS_ENABLED: "false"
ZITADEL_DATABASE_POSTGRES_HOST: zitadel-db
ZITADEL_DATABASE_POSTGRES_PORT: "5432"
ZITADEL_DATABASE_POSTGRES_DATABASE: zitadel
ZITADEL_DATABASE_POSTGRES_USER_USERNAME: zitadel
ZITADEL_DATABASE_POSTGRES_USER_PASSWORD: zitadel
ZITADEL_DATABASE_POSTGRES_USER_SSL_MODE: disable
ZITADEL_DATABASE_POSTGRES_ADMIN_USERNAME: zitadel
ZITADEL_DATABASE_POSTGRES_ADMIN_PASSWORD: zitadel
ZITADEL_DATABASE_POSTGRES_ADMIN_SSL_MODE: disable
# root, so FirstInstance can write the admin PAT into the shared volume.
user: "0"
ports:
- "127.0.0.1:${RAMP_ZITADEL_PORT:-58080}:8080"
volumes:
- ./deploy/zitadel/init-steps.yaml:/etc/zitadel/init-steps.yaml:ro
- zitadel-bootstrap:/bootstrap
depends_on:
zitadel-db:
condition: service_healthy
# One-shot provisioning; see scripts/zitadel-bootstrap.sh. It dials zitadel:8080
# over the compose network while ExternalDomain is `localhost`, so it overrides
# the Host header — Zitadel routes instances by it.
zitadel-init:
image: curlimages/curl:8.11.1
profiles: ["zitadel"]
entrypoint: ["sh", "/bootstrap.sh"]
# root, to match the zitadel service: FirstInstance creates /bootstrap owned by
# root, and this image's default uid 100 cannot write the client id/secret there.
user: "0"
environment:
ZITADEL_BASE_URL: http://zitadel:8080
ZITADEL_INSTANCE_HOST: ${RAMP_ZITADEL_DOMAIN:-localhost}
ZITADEL_ISSUER: http://${RAMP_ZITADEL_DOMAIN:-localhost}:${RAMP_ZITADEL_PORT:-58080}
IDENTITY_AUTH_ISSUER: ${IDENTITY_AUTH_ISSUER:-http://localhost:8083}
EXTRA_REDIRECT_URIS: ${EXTRA_REDIRECT_URIS:-}
GOOGLE_CLIENT_ID: ${GOOGLE_CLIENT_ID:-}
GOOGLE_CLIENT_SECRET: ${GOOGLE_CLIENT_SECRET:-}
volumes:
- ./scripts/zitadel-bootstrap.sh:/bootstrap.sh:ro
- zitadel-bootstrap:/bootstrap
depends_on:
- zitadel
# Named volumes are scoped by docker compose project name (`-p <project>`),
# so two concurrent projects get independent data dirs automatically.
volumes:
pgdata:
tbdata:
zitadeldata:
# Carries the admin PAT from FirstInstance to the bootstrap, and the resulting
# client id/secret back out to `make zitadel-creds`.
zitadel-bootstrap: