# Single-VM getting-started stack: MySQL + the EDR server + Caddy. # # Caddy terminates public HTTPS with automatic Let's Encrypt certificates and # reverse-proxies to the server over the private Docker network, so the operator # manages zero certificates and there is NO content-inspecting WAF in the path # (unlike a managed PaaS edge). Bring it up with ./bootstrap.sh, which generates # the secret files and .env this stack expects. Full walkthrough: # docs/quickstart-vm.md. For high availability, use # packaging/docker-compose-multi-replica.yml instead. services: mysql: image: mysql:8.4.9@sha256:c36050afdca850f23cef85703f84c7531a5ae155a11b5ee1c60acb09937c4084 restart: unless-stopped # Single MySQL instance: no replica and no point-in-time recovery, so the # binary log is pure overhead. Disabling it drops a per-commit fsync and # roughly halves write volume, which directly relieves the disk-bound event # store. The buffer pool is MySQL's page cache: at MySQL's own 128 MB default # the session views read most pages from disk, so it defaults to 1 GB here # and EDR_MYSQL_BUFFER_POOL_SIZE in .env resizes it. The other ingest knobs # (commit durability, flush pacing) are disk-specific, so they are not # defaulted; see "Tuning MySQL" in docs/quickstart-vm.md. command: - --skip-log-bin - --innodb-buffer-pool-size=${EDR_MYSQL_BUFFER_POOL_SIZE:-1G} environment: MYSQL_ROOT_PASSWORD_FILE: /run/secrets/mysql_root MYSQL_DATABASE: edr volumes: - edr-mysql-data:/var/lib/mysql secrets: - mysql_root healthcheck: test: ["CMD-SHELL", "mysqladmin ping --silent 2>/dev/null || exit 1"] interval: 5s timeout: 5s retries: 20 start_period: 10s # ClickHouse event archive (ADR-0015): the durable event store the server writes ingested events to and reads them back # from. Reachable only on the private compose network (no host port), same posture as mysql. The default user is # passwordless; the DSN rides a docker secret so a password can be added later without editing this file. clickhouse: image: clickhouse/clickhouse-server:24.8@sha256:1ffa82edee000a42c09313bd9f1293d94c570aee74babc1b3ca9983a35fa597b restart: unless-stopped environment: CLICKHOUSE_DB: edr # Required for the default user to authenticate over the network with an empty password; without it the server's # ClickHouse ping fails with code 516 (AUTHENTICATION_FAILED). Mirrors the dev/demo clickhouse service. CLICKHOUSE_DEFAULT_ACCESS_MANAGEMENT: "1" ulimits: # ClickHouse opens many files; the stock 1024 soft limit trips "too many open files" under load. nofile: soft: 262144 hard: 262144 volumes: - edr-clickhouse-data:/var/lib/clickhouse healthcheck: test: ["CMD", "clickhouse-client", "--query", "SELECT 1"] interval: 5s timeout: 5s retries: 20 start_period: 10s server: # Pin EDR_VERSION in .env to a release tag for production; bootstrap.sh warns # when it defaults to `latest`. image: ghcr.io/rawtrail/rawtrail-server:${EDR_VERSION:-latest} restart: unless-stopped # Longer than every stage of a graceful stop put together: the drain (EDR_SHUTDOWN_DRAIN, # 30s), the in-flight deadline (15s), the two loop joins (10s + 5s) and the telemetry # flush (5s): 65s in all. Docker's default is 10s, which cuts the drain the load # balancer is watching and severs in-flight requests (#1127). test/arch keeps this # in step with those five constants. Raise it too if you raise EDR_SHUTDOWN_DRAIN. stop_grace_period: 90s depends_on: mysql: condition: service_healthy clickhouse: condition: service_healthy # Pass .env through to the server so any server variable (OIDC/SSO, session # timeouts, JIT provisioning, etc.) can be set there without editing this # file. The explicit `environment:` keys below take precedence over .env, so # the security-critical wiring (proxy-terminated TLS, trusted proxies, the # secret *_FILE paths) cannot be overridden by accident. See # docs/quickstart-vm.md ("Set server configuration"). env_file: - .env environment: # DSN + secrets are read from files (docker secrets) so no password lands # in `docker inspect`. EDR_DSN is blank so the *_FILE path wins. EDR_DSN: "" EDR_DSN_FILE: /run/secrets/edr_dsn EDR_ENROLL_SECRET_FILE: /run/secrets/enroll_secret EDR_SECRET_KEY_FILE: /run/secrets/secret_key # ClickHouse event archive DSN (ADR-0015). Passwordless on the private network; blank env so the *_FILE path wins. EDR_CLICKHOUSE_DSN: "" EDR_CLICKHOUSE_DSN_FILE: /run/secrets/clickhouse_dsn # Caddy terminates TLS in front; the server listens plaintext on the # private Docker network only. It is never published to the host, so the # only way in is through Caddy. EDR_LISTEN_ADDR: "0.0.0.0:8088" EDR_TLS_TERMINATED_BY_PROXY: "1" # Trust X-Forwarded-For only from the Docker bridge network (Caddy). The # default Compose address pool lives inside 172.16.0.0/12. EDR_TRUSTED_PROXIES: "172.16.0.0/12" # Break-glass WebAuthn binds credentials to the public host, so both must # equal your domain. EDR_BREAKGLASS_RP_ID: ${EDR_DOMAIN} EDR_BREAKGLASS_RP_ORIGINS: https://${EDR_DOMAIN} # Sign in with the break-glass admin, then configure SSO under Admin # settings -> Single sign-on; it applies at runtime and survives restarts. # See docs/quickstart-vm.md ("Single sign-on (OIDC)"). The server always # boots without OIDC config (issue #512). EDR_LOG_LEVEL: ${EDR_LOG_LEVEL:-info} EDR_LOG_FORMAT: "json" # Process-graph retention: completed process records older than this are # deleted from MySQL, except those an alert references. Events live in # ClickHouse and expire on their own 30-day window, so this matches it: a # shorter value leaves every older alert's graph showing its process with # none of its neighbours while the event timeline is complete. Lower it in # .env if MySQL disk is tight. EDR_RETENTION_DAYS: ${EDR_RETENTION_DAYS:-30} # OTel export (optional). Set OTEL_EXPORTER_OTLP_ENDPOINT in .env to an # OTLP/gRPC collector to turn on traces, metrics, and logs; leave it unset # to keep telemetry off. The URL scheme picks the transport: http:// is # plaintext, https:// uses TLS. OTEL_EXPORTER_OTLP_HEADERS carries an # auth/ingestion token (e.g. SigNoz Cloud); OTEL_RESOURCE_ATTRIBUTES sets # deployment.environment and any other resource tags. # OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE=delta suits SigNoz and # Datadog; leave it unset (cumulative) for Prometheus-style backends. See # docs/quickstart-vm.md ("Send telemetry to a collector"). OTEL_EXPORTER_OTLP_ENDPOINT: ${OTEL_EXPORTER_OTLP_ENDPOINT:-} OTEL_EXPORTER_OTLP_HEADERS: ${OTEL_EXPORTER_OTLP_HEADERS:-} OTEL_EXPORTER_OTLP_COMPRESSION: ${OTEL_EXPORTER_OTLP_COMPRESSION:-} OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE: ${OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE:-} OTEL_RESOURCE_ATTRIBUTES: ${OTEL_RESOURCE_ATTRIBUTES:-} OTEL_SERVICE_NAME: rawtrail-server secrets: - enroll_secret - edr_dsn - secret_key - clickhouse_dsn caddy: image: caddy:2 restart: unless-stopped depends_on: - server environment: EDR_DOMAIN: ${EDR_DOMAIN} ports: - "80:80" - "443:443" volumes: - ./packaging/caddy/Caddyfile:/etc/caddy/Caddyfile:ro - caddy-data:/data - caddy-config:/config volumes: edr-mysql-data: edr-clickhouse-data: caddy-data: caddy-config: secrets: mysql_root: file: ./secrets/mysql_root enroll_secret: file: ./secrets/enroll_secret edr_dsn: file: ./secrets/edr_dsn secret_key: file: ./secrets/secret_key clickhouse_dsn: file: ./secrets/clickhouse_dsn