# Phase 0+1 stack: Redpanda -> ingest -> ClickHouse -> api -> web, plus # search (Tantivy full-text indexing, reads the same Redpanda topic # ingest's consumer does). # # Does NOT include the Rust agent — see /agent/README.md: journald # sourcing needs the host's journal, which isn't something a container # gets for free. Run the agent natively on the host per # /docs/phase-0-runbook.md, pointed at ingest's mapped port (localhost:4317). # Windows Event Log/ETW sourcing needs a real Windows host regardless — # see /docs/phase-1-runbook.md. # # Before first run: generate dev mTLS certs (hack/dev-certs/generate.sh). # See /docs/phase-0-runbook.md (Linux pipeline) and # /docs/phase-1-runbook.md (Windows + full-text search) for the full # sequences. services: redpanda: image: docker.redpanda.com/redpandadata/redpanda:v24.2.7 container_name: sentry-redpanda command: - redpanda - start - --smp=1 - --memory=1G - --reserve-memory=0M - --overprovisioned - --node-id=0 - --check=false - --kafka-addr=PLAINTEXT://0.0.0.0:9092 - --advertise-kafka-addr=PLAINTEXT://redpanda:9092 ports: - "9092:9092" volumes: - redpanda-data:/var/lib/redpanda/data healthcheck: test: ["CMD", "rpk", "cluster", "health", "--exit-when-healthy"] interval: 5s timeout: 5s retries: 30 # One-shot: creates the sentry.logs.raw topic, then exits 0. ingest # waits on this completing successfully before it starts. redpanda-provision: build: context: ./transport container_name: sentry-redpanda-provision depends_on: redpanda: condition: service_healthy environment: REDPANDA_BROKERS: "redpanda:9092" REDPANDA_ADMIN_HOSTS: "redpanda:9644" # Explicit rather than relying on both this script's and /search's # defaults happening to agree — search consumes this same topic and # needs to know the partition count up front (see /search/README.md). REDPANDA_TOPIC_PARTITIONS: "6" clickhouse: image: clickhouse/clickhouse-server:24.8 container_name: sentry-clickhouse ports: - "8123:8123" # HTTP interface, used by the migrate step - "9000:9000" # native protocol, used by ingest and api environment: # The official image disables *network* access entirely for the # default user (even from sibling containers) unless # CLICKHOUSE_USER or CLICKHOUSE_PASSWORD is set to a genuinely # non-empty value — confirmed by testing, not just reading docs: an # explicitly-empty CLICKHOUSE_PASSWORD="" still triggers the # lockdown, silently returning 403 to every other container. This # password isn't a real secret (mTLS between agent and ingest is # the actual security boundary here) — it exists purely to satisfy # this image's login gate for local/homelab use. CLICKHOUSE_PASSWORD: "sentry-dev-only" volumes: - clickhouse-data:/var/lib/clickhouse ulimits: nofile: soft: 262144 hard: 262144 healthcheck: test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8123/ping"] interval: 5s timeout: 5s retries: 30 # One-shot: applies /storage/migrations/*.sql, then exits 0. ingest and # api both wait on this completing successfully. clickhouse-migrate: build: context: ./storage container_name: sentry-clickhouse-migrate depends_on: clickhouse: condition: service_healthy environment: CLICKHOUSE_HTTP: "http://clickhouse:8123" CLICKHOUSE_PASSWORD: "sentry-dev-only" # Control-plane metadata store (dashboards, alert rules -- see # /docs/phase-3-dashboard-design.md for why this is Postgres rather # than new ClickHouse tables). Log data stays on ClickHouse/Tantivy # only, unaffected. metadata-postgres: image: postgres:16-alpine container_name: sentry-metadata-postgres environment: POSTGRES_DB: sentry_metadata POSTGRES_USER: sentry POSTGRES_PASSWORD: "sentry-dev-only" # not a real secret, same framing as CLICKHOUSE_PASSWORD above volumes: - metadata-postgres-data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U sentry -d sentry_metadata"] interval: 5s timeout: 5s retries: 30 # One-shot: applies /metadata/migrations/*.sql, then exits 0. api waits # on this completing successfully, same shape as clickhouse-migrate. metadata-migrate: build: context: ./metadata container_name: sentry-metadata-migrate depends_on: metadata-postgres: condition: service_healthy environment: POSTGRES_HOST: "metadata-postgres" POSTGRES_PORT: "5432" POSTGRES_USER: "sentry" POSTGRES_PASSWORD: "sentry-dev-only" POSTGRES_DATABASE: "sentry_metadata" ingest: build: context: . # needs both ingest/ and proto/ dockerfile: ingest/Dockerfile container_name: sentry-ingest depends_on: redpanda-provision: condition: service_completed_successfully clickhouse-migrate: condition: service_completed_successfully ports: - "4317:4317" # gRPC, mTLS — this is what the host-run agent connects to environment: REDPANDA_BROKERS: "redpanda:9092" CLICKHOUSE_ADDR: "clickhouse:9000" CLICKHOUSE_PASSWORD: "sentry-dev-only" # TLS_*_FILE env vars are left at their defaults # (/etc/sentry-ingest/{server,server-key,ca}.pem) — matches where # the volume below mounts the generated dev certs. volumes: - ./hack/dev-certs/out:/etc/sentry-ingest:ro # Reads the same sentry.logs.raw topic ingest's consumer does (own # offset tracking, own failure domain — see /search/README.md) and # builds a Tantivy full-text index over the message field. search: build: context: . # needs both search/ and proto/ dockerfile: search/Dockerfile container_name: sentry-search depends_on: redpanda-provision: condition: service_completed_successfully environment: REDPANDA_BROKERS: "redpanda:9092" REDPANDA_TOPIC_PARTITIONS: "6" # must match redpanda-provision's above # tracing-subscriber's default filter suppresses INFO without this # -- found by actually checking `docker compose logs search` and # seeing nothing, same silent-logging gap the agent had in Phase 0. RUST_LOG: "info" volumes: - search-index-data:/var/lib/sentry-search api: build: context: . # needs both api/ and proto/ (gRPC client to search) dockerfile: api/Dockerfile container_name: sentry-api depends_on: clickhouse-migrate: condition: service_completed_successfully metadata-migrate: condition: service_completed_successfully ports: - "8080:8080" environment: CLICKHOUSE_ADDR: "clickhouse:9000" CLICKHOUSE_PASSWORD: "sentry-dev-only" SEARCH_GRPC_ADDR: "search:50052" POSTGRES_ADDR: "metadata-postgres:5432" POSTGRES_DATABASE: "sentry_metadata" POSTGRES_USERNAME: "sentry" POSTGRES_PASSWORD: "sentry-dev-only" healthcheck: # alerting (Phase 3 task 5) depends_on api -- without this, that # dependency can only mean "container started," not "actually # listening," and would hammer a not-yet-ready api with errors on # every evaluator tick during stack startup. api's image is # distroless (no shell, no wget) so this execs the api binary's own # -healthcheck self-check mode instead of an external tool. test: ["CMD", "/api", "-healthcheck"] interval: 5s timeout: 5s retries: 30 alerting: build: context: alerting # self-contained, no /proto needed -- see alerting/Dockerfile dockerfile: Dockerfile container_name: sentry-alerting depends_on: metadata-migrate: condition: service_completed_successfully api: condition: service_healthy ports: - "8081:8081" environment: POSTGRES_ADDR: "metadata-postgres:5432" POSTGRES_DATABASE: "sentry_metadata" POSTGRES_USERNAME: "sentry" POSTGRES_PASSWORD: "sentry-dev-only" API_QUERY_URL: "http://api:8080" healthcheck: test: ["CMD", "/alerting", "-healthcheck"] interval: 5s timeout: 5s retries: 30 web: build: context: web args: # Baked in at build time (static site, not a server) as # localhost:8080/8081 -- fetched from the *browser*, which # resolves against the host's mapped ports, not the compose # network's service DNS names. VITE_API_BASE_URL: "http://localhost:8080" VITE_ALERTING_API_BASE_URL: "http://localhost:8081" container_name: sentry-web depends_on: - api - alerting ports: - "3000:3000" volumes: redpanda-data: clickhouse-data: search-index-data: metadata-postgres-data: