Skip to content

Commit 123a8a6

Browse files
committed
chore: improve s2-lite local-dev onboarding
Three small papercuts hitting new contributors: 1. .env.example was silent on realtime-streams S2 config, so fresh clones defaulted to v1 (Redis-only) streams and Sessions / chat.agent failed with "S2 configuration is missing". The `s2` service in compose pre-seeds a `trigger-local` basin (see docker/config/s2-spec.json) — wire the env vars to it by default. 2. The s2 healthcheck shelled `wget` on a distroless image, so docker always reported it unhealthy even when the API was live. Drop it; no service depends on s2. 3. Two clones could not run docker side by side: ports, project name, and container names were all hardcoded. Parameterize host ports with `${VAR:-default}`, drive the project name via `COMPOSE_PROJECT_NAME` (with a `name:` field for backward compat), prefix container names with `${CONTAINER_PREFIX:-}`, and pass `--env-file .env` so compose reads the same root `.env` the webapp does. Also split the optional services (electric-shard-1, ch-ui, toxiproxy, nginx-h2, otel-collector, prometheus, grafana) into docker-compose.extras.yml + a `docker:full` pnpm script. The core stack keeps everything the webapp actually needs (postgres, redis, electric, minio, clickhouse + migrator, s2-lite).
1 parent 05d3ab1 commit 123a8a6

7 files changed

Lines changed: 213 additions & 128 deletions

File tree

.env.example

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,52 @@ REDIS_TLS_DISABLED="true"
2929
DEV_OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:3030/otel"
3030
DEV_OTEL_BATCH_PROCESSING_ENABLED="0"
3131

32+
# Realtime streams v2 (Sessions, chat.agent, large stream backfills) backed
33+
# by S2 (https://s2.dev). The `s2` service in docker/docker-compose.yml runs
34+
# the open-source s2-lite binary and pre-creates a basin named `trigger-local`
35+
# (see docker/config/s2-spec.json). Comment these out to fall back to v1
36+
# (Redis-only) streams; Sessions and chat.agent then become unavailable.
37+
REALTIME_STREAMS_S2_BASIN=trigger-local
38+
REALTIME_STREAMS_S2_ACCESS_TOKEN=ignored
39+
REALTIME_STREAMS_S2_ENDPOINT=http://localhost:4566/v1
40+
REALTIME_STREAMS_S2_SKIP_ACCESS_TOKENS=true
41+
REALTIME_STREAMS_DEFAULT_VERSION=v2
42+
43+
# Running multiple instances side by side (worktrees, branch experiments)
44+
#
45+
# Every host port in docker/docker-compose.yml is `${VAR:-default}` and the
46+
# project name comes from `COMPOSE_PROJECT_NAME`. To stand up a second stack
47+
# alongside the default one, uncomment the block below in this clone's `.env`
48+
# (pick any offset that doesn't clash with anything else running), then update
49+
# the URL/PORT vars further up to match. Default values are commented for
50+
# reference.
51+
#
52+
# --- core (pnpm run docker) ---
53+
# COMPOSE_PROJECT_NAME=triggerdotdev-docker-alt
54+
# CONTAINER_PREFIX=alt-
55+
# POSTGRES_HOST_PORT=15432 # default 5432
56+
# REDIS_HOST_PORT=16379 # default 6379
57+
# ELECTRIC_HOST_PORT=13060 # default 3060
58+
# MINIO_API_HOST_PORT=19005 # default 9005
59+
# MINIO_CONSOLE_HOST_PORT=19006 # default 9006
60+
# CLICKHOUSE_HTTP_HOST_PORT=18123 # default 8123
61+
# CLICKHOUSE_TCP_HOST_PORT=19000 # default 9000
62+
# S2_HOST_PORT=14566 # default 4566
63+
# REMIX_APP_PORT=13030 # default 3030
64+
# --- extras (only needed if you also run `pnpm run docker:full`) ---
65+
# ELECTRIC_SHARD_1_HOST_PORT=13061 # default 3061
66+
# CH_UI_HOST_PORT=15521 # default 5521
67+
# TOXIPROXY_PROXY_HOST_PORT=40303 # default 30303
68+
# TOXIPROXY_API_HOST_PORT=18474 # default 8474
69+
# NGINX_H2_HOST_PORT=18443 # default 8443
70+
# OTEL_GRPC_HOST_PORT=14317 # default 4317
71+
# OTEL_HTTP_HOST_PORT=14318 # default 4318
72+
# OTEL_PROMETHEUS_HOST_PORT=18889 # default 8889
73+
# PROMETHEUS_HOST_PORT=19090 # default 9090
74+
# GRAFANA_HOST_PORT=13001 # default 3001
75+
# (and update DATABASE_URL / CLICKHOUSE_URL / REDIS_PORT / APP_ORIGIN /
76+
# LOGIN_ORIGIN / ELECTRIC_ORIGIN / REALTIME_STREAMS_S2_ENDPOINT to match)
77+
3278
# When the domain is set to `localhost` the CLI deploy command will only --load the image by default and not --push it
3379
DEPLOY_REGISTRY_HOST=localhost:5000
3480

@@ -106,7 +152,7 @@ POSTHOG_PROJECT_KEY=
106152
# INTERNAL_OTEL_TRACE_LOGGING_ENABLED=1
107153
# INTERNAL_OTEL_TRACE_INSTRUMENT_PRISMA_ENABLED=0
108154

109-
# Enable local observability stack (requires `pnpm run docker` to start otel-collector)
155+
# Enable local observability stack (requires `pnpm run docker:full` to bring up otel-collector + prometheus + grafana)
110156
# Uncomment these to send metrics to the local Prometheus via OTEL Collector:
111157
# INTERNAL_OTEL_METRIC_EXPORTER_ENABLED=1
112158
# INTERNAL_OTEL_METRIC_EXPORTER_URL=http://localhost:4318/v1/metrics

AGENTS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ See `ai/references/repo.md` for a more complete explanation of the workspaces.
1919
```bash
2020
pnpm run docker
2121
```
22+
Add `:full` (`pnpm run docker:full`) for the optional observability + chaos tooling. See `docker/docker-compose.extras.yml`.
2223
4. Run database migrations:
2324
```bash
2425
pnpm run db:migrate

CLAUDE.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ This is a pnpm 10.33.2 monorepo using Turborepo. Run commands from root with `pn
99
**Adding dependencies:** Edit `package.json` directly instead of using `pnpm add`, then run `pnpm i` from the repo root. See `.claude/rules/package-installation.md` for the full process.
1010

1111
```bash
12-
pnpm run docker # Start Docker services (PostgreSQL, Redis, Electric)
12+
pnpm run docker # Core dev services (Postgres, Redis, Electric, MinIO, ClickHouse, s2-lite)
13+
# pnpm run docker:full # Same + observability stack (Prometheus, Grafana, OTEL) and chaos tooling
1314
pnpm run db:migrate # Run database migrations
1415
pnpm run db:seed # Seed the database (required for reference projects)
1516

CONTRIBUTING.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,12 +71,14 @@ branch are tagged into a release periodically.
7171

7272
Feel free to update `SESSION_SECRET` and `MAGIC_LINK_SECRET` as well using the same method.
7373

74-
8. Start Docker. This starts the required services: Postgres, Redis, Electric, and ClickHouse (the ClickHouse migrator runs once on first start). If this is your first time using Docker, consider going through this [guide](DOCKER_INSTALLATION.md).
74+
8. Start Docker. This starts the core dev services (Postgres, Redis, Electric, MinIO, ClickHouse, s2-lite) and runs the ClickHouse migrator once on first start. If this is your first time using Docker, consider going through this [guide](DOCKER_INSTALLATION.md).
7575

7676
```
7777
pnpm run docker
7878
```
7979

80+
For the observability stack (Prometheus, Grafana, OTEL collector) and other optional tooling (Toxiproxy, nginx-h2, ch-ui, extra electric shard), use `pnpm run docker:full` instead. See `docker/docker-compose.extras.yml` for the full list.
81+
8082
9. Migrate the database
8183
```
8284
pnpm run db:migrate
@@ -300,3 +302,7 @@ The process running on port `3030` should be destroyed.
300302
```sh
301303
sudo kill -9 <PID>
302304
```
305+
306+
### Running two clones side by side (worktree, branch experiment)
307+
308+
The default `pnpm run docker` uses the project name `triggerdotdev-docker` and the standard host ports (5432, 6379, 3060, 4566, 8123, 9000, 9005, 9006). To stand up a second instance in another clone without clashing, set a different `COMPOSE_PROJECT_NAME` and the offset host ports in that clone's `.env`. The "Running multiple instances side by side" block in `.env.example` lists every overridable env var with its default for reference; uncomment the lines you need and update `DATABASE_URL` / `CLICKHOUSE_URL` / `REDIS_PORT` / `APP_ORIGIN` / `LOGIN_ORIGIN` / `ELECTRIC_ORIGIN` / `REALTIME_STREAMS_S2_ENDPOINT` to match.

docker/docker-compose.extras.yml

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
# Optional services for advanced local-dev workflows. Pair with
2+
# `docker-compose.yml` via `pnpm run docker:full`.
3+
#
4+
# Same `name:` so `docker compose` treats both files as one project — bring
5+
# them up together and they share the `app_network` and `triggerdotdev-docker`
6+
# volume namespace. Tear down with `pnpm run docker:full:stop`.
7+
#
8+
# Includes:
9+
# - electric-shard-1: second Electric instance for multi-shard testing
10+
# - ch-ui: ClickHouse browser UI
11+
# - toxiproxy: chaos / flake testing
12+
# - nginx-h2: HTTP/2 reverse proxy
13+
# - otel-collector + prometheus + grafana: observability stack
14+
name: triggerdotdev-docker
15+
16+
volumes:
17+
prometheus-data:
18+
grafana-data:
19+
20+
networks:
21+
app_network:
22+
external: false
23+
24+
services:
25+
electric-shard-1:
26+
container_name: ${CONTAINER_PREFIX:-}electric-shard-1
27+
image: electricsql/electric:1.2.4@sha256:20da3d0b0e74926c5623392db67fd56698b9e374c4aeb6cb5cadeb8fea171c36
28+
restart: always
29+
environment:
30+
DATABASE_URL: postgresql://postgres:postgres@database:5432/postgres?sslmode=disable
31+
ELECTRIC_INSECURE: true
32+
ELECTRIC_REPLICATION_STREAM_ID: "triggershard1"
33+
networks:
34+
- app_network
35+
ports:
36+
- "${ELECTRIC_SHARD_1_HOST_PORT:-3061}:3000"
37+
depends_on:
38+
- database
39+
40+
ch-ui:
41+
image: ghcr.io/caioricciuti/ch-ui:latest
42+
restart: always
43+
ports:
44+
- "${CH_UI_HOST_PORT:-5521}:5521"
45+
environment:
46+
VITE_CLICKHOUSE_URL: "http://localhost:${CLICKHOUSE_HTTP_HOST_PORT:-8123}"
47+
VITE_CLICKHOUSE_USER: "default"
48+
VITE_CLICKHOUSE_PASS: "password"
49+
networks:
50+
- app_network
51+
52+
toxiproxy:
53+
container_name: ${CONTAINER_PREFIX:-}toxiproxy
54+
image: ghcr.io/shopify/toxiproxy:latest
55+
restart: always
56+
volumes:
57+
- ./config/toxiproxy.json:/config/toxiproxy.json
58+
ports:
59+
- "${TOXIPROXY_PROXY_HOST_PORT:-30303}:30303" # Proxied webapp port
60+
- "${TOXIPROXY_API_HOST_PORT:-8474}:8474" # Toxiproxy API port
61+
networks:
62+
- app_network
63+
command: ["-host", "0.0.0.0", "-config", "/config/toxiproxy.json"]
64+
65+
nginx-h2:
66+
image: nginx:1.27
67+
container_name: ${CONTAINER_PREFIX:-}nginx-h2
68+
restart: unless-stopped
69+
ports:
70+
- "${NGINX_H2_HOST_PORT:-8443}:8443"
71+
volumes:
72+
- ./config/nginx.conf:/etc/nginx/nginx.conf:ro
73+
- ./config/certs:/etc/nginx/certs:ro
74+
75+
# Observability stack for local development
76+
otel-collector:
77+
container_name: ${CONTAINER_PREFIX:-}otel-collector
78+
image: otel/opentelemetry-collector-contrib:0.96.0
79+
restart: always
80+
command: ["--config", "/etc/otel-collector-config.yaml"]
81+
volumes:
82+
- ./config/otel-collector-config.yaml:/etc/otel-collector-config.yaml:ro
83+
ports:
84+
- "${OTEL_GRPC_HOST_PORT:-4317}:4317" # OTLP gRPC receiver
85+
- "${OTEL_HTTP_HOST_PORT:-4318}:4318" # OTLP HTTP receiver
86+
- "${OTEL_PROMETHEUS_HOST_PORT:-8889}:8889" # Prometheus exporter
87+
networks:
88+
- app_network
89+
90+
prometheus:
91+
container_name: ${CONTAINER_PREFIX:-}prometheus
92+
image: prom/prometheus:v2.54.1
93+
restart: always
94+
volumes:
95+
- ./config/prometheus.yml:/etc/prometheus/prometheus.yml:ro
96+
- prometheus-data:/prometheus
97+
ports:
98+
- "${PROMETHEUS_HOST_PORT:-9090}:9090"
99+
networks:
100+
- app_network
101+
command:
102+
- "--config.file=/etc/prometheus/prometheus.yml"
103+
- "--storage.tsdb.path=/prometheus"
104+
- "--web.enable-lifecycle"
105+
106+
grafana:
107+
container_name: ${CONTAINER_PREFIX:-}grafana
108+
image: grafana/grafana:11.3.0
109+
restart: always
110+
volumes:
111+
- grafana-data:/var/lib/grafana
112+
- ./config/grafana/provisioning:/etc/grafana/provisioning:ro
113+
ports:
114+
- "${GRAFANA_HOST_PORT:-3001}:3000"
115+
environment:
116+
GF_SECURITY_ADMIN_USER: admin
117+
GF_SECURITY_ADMIN_PASSWORD: admin
118+
GF_USERS_ALLOW_SIGN_UP: false
119+
networks:
120+
- app_network
121+
depends_on:
122+
- prometheus

0 commit comments

Comments
 (0)