# Production-style override: put the gateway (frontend + API) behind Traefik # with automatic Let's Encrypt TLS. Use together with the base file: # # docker compose -f docker-compose.yml -f docker-compose.traefik.yml up -d --build # # Only Traefik publishes host ports (80/443). Everything else is reachable # only on the compose network. # # Metrics: every Ledgerline service serves OTel metrics on :9464 (see # common/telemetry.py). Traefik exposes each one as # https://ledgerline.vione.cloud/metrics/, behind basic auth, so the # platform's Prometheus on falken can scrape it (k8s/prometheus/prometheus.yml, # job `ledgerline`). The htpasswd file is written by k8s/create-secrets.sh and # copied here once, like .env.observability. services: traefik: image: traefik:v3.6 restart: unless-stopped command: - --providers.docker=true - --providers.docker.exposedByDefault=false - --entrypoints.web.address=:80 - --entrypoints.web.http.redirections.entryPoint.to=websecure - --entrypoints.web.http.redirections.entryPoint.scheme=https - --entrypoints.websecure.address=:443 - --certificatesresolvers.le.acme.httpchallenge=true - --certificatesresolvers.le.acme.httpchallenge.entrypoint=web - --certificatesresolvers.le.acme.storage=/letsencrypt/acme.json - --log.level=INFO - --accesslog=true ports: - "80:80" - "443:443" volumes: - /var/run/docker.sock:/var/run/docker.sock:ro - letsencrypt:/letsencrypt - ./observability/traefik/metrics-users:/etc/traefik/metrics-users:ro gateway: ports: !reset [] labels: - traefik.enable=true - traefik.http.routers.ledgerline.rule=Host(`ledgerline.vione.cloud`) - traefik.http.routers.ledgerline.entrypoints=websecure - traefik.http.routers.ledgerline.tls.certresolver=le - traefik.http.services.ledgerline.loadbalancer.server.port=8000 # gateway now has two Traefik services, so each router names its own. - traefik.http.routers.ledgerline.service=ledgerline - traefik.http.routers.gateway-metrics.rule=Host(`ledgerline.vione.cloud`) && Path(`/metrics/gateway`) - traefik.http.routers.gateway-metrics.entrypoints=websecure - traefik.http.routers.gateway-metrics.tls.certresolver=le - traefik.http.routers.gateway-metrics.middlewares=metrics-auth,metrics-path - traefik.http.routers.gateway-metrics.service=gateway-metrics - traefik.http.services.gateway-metrics.loadbalancer.server.port=9464 # Shared by every -metrics router. Declared here because a # middleware needs an enabled container to carry it. - traefik.http.middlewares.metrics-auth.basicauth.usersfile=/etc/traefik/metrics-users - traefik.http.middlewares.metrics-path.replacepath.path=/metrics postgres: ports: !reset [] redis: ports: !reset [] ledger: ports: !reset [] labels: - traefik.enable=true - traefik.http.routers.ledger-metrics.rule=Host(`ledgerline.vione.cloud`) && Path(`/metrics/ledger`) - traefik.http.routers.ledger-metrics.entrypoints=websecure - traefik.http.routers.ledger-metrics.tls.certresolver=le - traefik.http.routers.ledger-metrics.middlewares=metrics-auth,metrics-path - traefik.http.services.ledger-metrics.loadbalancer.server.port=9464 fraud: ports: !reset [] labels: - traefik.enable=true - traefik.http.routers.fraud-metrics.rule=Host(`ledgerline.vione.cloud`) && Path(`/metrics/fraud`) - traefik.http.routers.fraud-metrics.entrypoints=websecure - traefik.http.routers.fraud-metrics.tls.certresolver=le - traefik.http.routers.fraud-metrics.middlewares=metrics-auth,metrics-path - traefik.http.services.fraud-metrics.loadbalancer.server.port=9464 sanctions: ports: !reset [] labels: - traefik.enable=true - traefik.http.routers.sanctions-metrics.rule=Host(`ledgerline.vione.cloud`) && Path(`/metrics/sanctions`) - traefik.http.routers.sanctions-metrics.entrypoints=websecure - traefik.http.routers.sanctions-metrics.tls.certresolver=le - traefik.http.routers.sanctions-metrics.middlewares=metrics-auth,metrics-path - traefik.http.services.sanctions-metrics.loadbalancer.server.port=9464 insights: ports: !reset [] labels: - traefik.enable=true - traefik.http.routers.insights-metrics.rule=Host(`ledgerline.vione.cloud`) && Path(`/metrics/insights`) - traefik.http.routers.insights-metrics.entrypoints=websecure - traefik.http.routers.insights-metrics.tls.certresolver=le - traefik.http.routers.insights-metrics.middlewares=metrics-auth,metrics-path - traefik.http.services.insights-metrics.loadbalancer.server.port=9464 notifier: ports: !reset [] labels: - traefik.enable=true - traefik.http.routers.notifier-metrics.rule=Host(`ledgerline.vione.cloud`) && Path(`/metrics/notifier`) - traefik.http.routers.notifier-metrics.entrypoints=websecure - traefik.http.routers.notifier-metrics.tls.certresolver=le - traefik.http.routers.notifier-metrics.middlewares=metrics-auth,metrics-path - traefik.http.services.notifier-metrics.loadbalancer.server.port=9464 audit: labels: - traefik.enable=true - traefik.http.routers.audit-metrics.rule=Host(`ledgerline.vione.cloud`) && Path(`/metrics/audit`) - traefik.http.routers.audit-metrics.entrypoints=websecure - traefik.http.routers.audit-metrics.tls.certresolver=le - traefik.http.routers.audit-metrics.middlewares=metrics-auth,metrics-path - traefik.http.services.audit-metrics.loadbalancer.server.port=9464 # Ships every container's stdout/stderr to the platform's Loki on falken # (k8s/), labelled `container`. Credentials come from .env.observability, # generated by k8s/create-secrets.sh and copied to the server once. # Publishes no ports; memory-capped because czy has 1 GB of RAM. Steady # state is ~70 MiB, but the initial backfill of every container's log # history peaked at ~150 MiB in testing, hence the headroom. alloy-agent: image: grafana/alloy:v1.10.2 restart: unless-stopped command: - run - --storage.path=/var/lib/alloy/data - /etc/alloy/config.alloy env_file: .env.observability mem_limit: 256m volumes: - ./observability/alloy-agent/config.alloy:/etc/alloy/config.alloy:ro - /var/run/docker.sock:/var/run/docker.sock:ro - alloy-agent-data:/var/lib/alloy/data volumes: letsencrypt: alloy-agent-data: