services: # ─── PostgreSQL + PostGIS ────────────────────────────────────────────────── postgres: image: postgis/postgis:16-3.4 restart: unless-stopped environment: POSTGRES_DB: fifteenmin POSTGRES_USER: app POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} volumes: - postgres_data:/var/lib/postgresql/data - ./infra/schema.sql:/docker-entrypoint-initdb.d/01-schema.sql:ro healthcheck: test: ["CMD-SHELL", "pg_isready -U app -d fifteenmin"] interval: 10s timeout: 5s retries: 10 start_period: 30s # ─── Valkey ─────────────────────────────────────────────────────────────── valkey: image: valkey/valkey:8-alpine restart: unless-stopped command: valkey-server --appendonly yes --requirepass ${VALKEY_PASSWORD} volumes: - valkey_data:/data healthcheck: test: ["CMD", "valkey-cli", "-a", "${VALKEY_PASSWORD}", "ping"] interval: 10s timeout: 5s retries: 5 # ─── Valhalla road worker (port 8002) ───────────────────────────────────── # Builds road-only tiles (no transit data) → cycling/walking/driving routing. # Without GTFS in its volume, valhalla_build_tiles produces clean road tiles # with no ghost transit edges, so bicycle routing works correctly. valhalla: build: context: . target: valhalla-worker restart: unless-stopped volumes: - osm_data:/data/osm:ro # PBF files downloaded by the main worker - valhalla_tiles:/data/valhalla # Road-only config and tiles environment: REDIS_HOST: valkey REDIS_PORT: "6379" REDIS_PASSWORD: ${VALKEY_PASSWORD} VALHALLA_QUEUE_NAME: valhalla OSM_DATA_DIR: /data/osm VALHALLA_CONFIG: /data/valhalla/valhalla.json VALHALLA_TILES_DIR: /data/valhalla/valhalla_tiles NODE_ENV: production ports: - "127.0.0.1:8002:8002" # Valhalla HTTP API (road) depends_on: valkey: condition: service_healthy # ─── Valhalla transit worker (port 8002 internal) ───────────────────────── # Builds tiles with GTFS transit data → multimodal routing. # Separate volume from the road worker so transit ghost edges never affect # the road instance. valhalla-transit: build: context: . target: valhalla-worker restart: unless-stopped volumes: - osm_data:/data/osm:ro # PBF files downloaded by the main worker - valhalla_tiles_transit:/data/valhalla # Transit config, tiles and GTFS data environment: REDIS_HOST: valkey REDIS_PORT: "6379" REDIS_PASSWORD: ${VALKEY_PASSWORD} VALHALLA_QUEUE_NAME: valhalla-transit OSM_DATA_DIR: /data/osm VALHALLA_CONFIG: /data/valhalla/valhalla.json VALHALLA_TILES_DIR: /data/valhalla/valhalla_tiles NODE_ENV: production # Optional: connect-info.net token for NDS-specific GTFS feed CONNECT_INFO_TOKEN: ${CONNECT_INFO_TOKEN:-} depends_on: valkey: condition: service_healthy # ─── Protomaps tile server ───────────────────────────────────────────────── tiles: image: ghcr.io/protomaps/go-pmtiles:latest restart: unless-stopped volumes: - pmtiles_data:/data command: serve /data --cors "*" ports: - "127.0.0.1:8080:8080" # ─── Next.js web application ─────────────────────────────────────────────── web: build: context: . target: web restart: unless-stopped ports: - "3000:3000" environment: DATABASE_URL: postgres://app:${POSTGRES_PASSWORD}@postgres:5432/fifteenmin REDIS_HOST: valkey REDIS_PORT: "6379" REDIS_PASSWORD: ${VALKEY_PASSWORD} VALHALLA_URL: http://valhalla:8002 VALHALLA_TRANSIT_URL: http://valhalla-transit:8002 ADMIN_PASSWORD_HASH: ${ADMIN_PASSWORD_HASH} ADMIN_JWT_SECRET: ${ADMIN_JWT_SECRET} NODE_ENV: production depends_on: postgres: condition: service_healthy valkey: condition: service_healthy # ─── BullMQ pipeline worker ─────────────────────────────────────────────── worker: build: context: . target: worker restart: unless-stopped environment: DATABASE_URL: postgres://app:${POSTGRES_PASSWORD}@postgres:5432/fifteenmin REDIS_HOST: valkey REDIS_PORT: "6379" REDIS_PASSWORD: ${VALKEY_PASSWORD} OSM_DATA_DIR: /data/osm LUA_SCRIPT: /app/infra/osm2pgsql.lua VALHALLA_URL: http://valhalla:8002 VALHALLA_TRANSIT_URL: http://valhalla-transit:8002 NODE_ENV: production # Optional: enables NDS-specific GTFS source for cities in Niedersachsen CONNECT_INFO_TOKEN: ${CONNECT_INFO_TOKEN:-} volumes: - osm_data:/data/osm # Worker downloads PBF here depends_on: postgres: condition: service_healthy valkey: condition: service_healthy volumes: postgres_data: valkey_data: osm_data: # Shared: worker writes, valhalla containers read valhalla_tiles: # Road-only tiles (no transit) — cycling works correctly here valhalla_tiles_transit: # Transit tiles (with GTFS) — multimodal routing pmtiles_data: