# BRouter host compose project — runs on the dedicated Hetzner Robot # server `ullrich.is` under the `trails` user. See README.md for first-time # provisioning notes. # # Exposed surface: Caddy listens on 10.0.1.10:17777 (vSwitch IP only). # BRouter itself is not published to the host — only reachable via the # internal Docker network from the Caddy sidecar. # # Logging: the host's default logging driver is `loki` (user's personal # Loki). Every service here explicitly overrides to `json-file` so logs # stay local and are picked up by the promtail sidecar (section 6.3) for # shipping to trails.cool's Loki. services: brouter: image: ghcr.io/trails-cool/brouter:latest container_name: trails-brouter restart: unless-stopped # Planet-scale coverage: segments live on the host and are mounted in. # 8 GB heap for segment cache; -Xms generous because routing is # memory-heavy and we don't benefit from a slow JVM warmup. # 16 routing threads on the 32 GB / multi-core dedicated host absorbs # burst traffic from "click around wildly" editing without triggering # BRouter's contention watchdog on the 4-thread default. environment: JAVA_OPTS: "-Xmx8g -Xms512M" BROUTER_THREADS: "16" volumes: - ./segments:/data/segments:ro networks: - trails-brouter-internal # Scope logs to json-file so we don't leak to the host's default Loki logging: driver: json-file options: max-size: "10m" max-file: "3" labels: trails.cool.service: "brouter" # No custom healthcheck: the BRouter image strips wget/curl post-build, # and /bin/sh in the base image doesn't support /dev/tcp. Real health # is observed via Caddy upstream (502 on outage) and the Planner-side # brouter_request_duration_seconds metric. caddy: image: caddy:2-alpine container_name: trails-brouter-caddy restart: unless-stopped depends_on: brouter: condition: service_started # Bind ONLY to the vSwitch IP on the host — the dedicated host's # public IP remains unaffected. UFW further restricts this to traffic # sourced from the flagship's private IP (10.0.0.2). ports: - "10.0.1.10:17777:17777" environment: BROUTER_AUTH_TOKEN: ${BROUTER_AUTH_TOKEN:?BROUTER_AUTH_TOKEN must be set} volumes: - ./Caddyfile:/etc/caddy/Caddyfile:ro # POI index artifact published by poi-extract.sh, served read-only under # /poi/* on the same vSwitch-only listener as BRouter. - ./poi-extract/publish:/srv/poi:ro - caddy-data:/data - caddy-config:/config networks: - trails-brouter-internal logging: driver: json-file options: max-size: "10m" max-file: "3" labels: trails.cool.service: "brouter-caddy" # cAdvisor — container metrics scraped by flagship Prometheus over the # vSwitch. We set container label filters so the exposed metrics only # cover trails-labeled containers (brouter + caddy), not the operator's # other workloads on this shared host. cadvisor: image: gcr.io/cadvisor/cadvisor:latest container_name: trails-brouter-cadvisor restart: unless-stopped privileged: true ports: - "10.0.1.10:8080:8080" volumes: - /:/rootfs:ro - /var/run:/var/run:ro - /sys:/sys:ro - /var/lib/docker:/var/lib/docker:ro - /dev/disk/:/dev/disk:ro command: # Only consider containers (not the host) and only those that carry # our label; filters out everything else on the shared host. - --docker_only=true - --store_container_labels=false - --whitelisted_container_labels=trails.cool.service logging: driver: json-file options: max-size: "10m" max-file: "3" labels: trails.cool.service: "brouter-cadvisor" # Promtail — tails Docker logs for trails-labeled containers only and # pushes to flagship Loki over the vSwitch. Does NOT scrape other # containers' logs on this host. promtail: image: grafana/promtail:latest container_name: trails-brouter-promtail restart: unless-stopped volumes: - /var/run/docker.sock:/var/run/docker.sock:ro - ./promtail-config.yml:/etc/promtail/config.yml:ro - promtail-positions:/tmp command: ["-config.file=/etc/promtail/config.yml"] logging: driver: json-file options: max-size: "10m" max-file: "3" labels: trails.cool.service: "brouter-promtail" volumes: caddy-data: caddy-config: promtail-positions: networks: trails-brouter-internal: driver: bridge # Container-to-container only; no host-level exposure via this net