feat(devops): self-hosted OSRM routing engine for site-finder (#39) #1929

Merged
bot-backend merged 1 commit from feat/39-osrm-routing into main 2026-06-26 19:18:22 +00:00
Collaborator

Summary

Self-hosted OSRM routing engine for site-finder so /analyze can later use real road/walking distances to POI instead of straight-line ST_Distance(geography). Infra only — the /analyze integration is a separate follow-up (A2 isochrones / A3 per-category routing-decay). Backend does NOT talk to OSRM yet.

What changed

  • docker-compose.prod.yml — new osrm service: osrm/osrm-backend:latest, osrm-routed --algorithm mld --max-table-size 8000 /data/${OSRM_REGION:-sverdlovsk}.osrm, mem_limit: 1.5g, restart: unless-stopped, network default only. No ports: — internal-only; backend reaches it at http://osrm:5000, Caddy does not proxy it. Healthcheck is a bash /dev/tcp port-probe (the OSRM image ships no curl/wget). backend does not depends_on osrm, so a crash-loop before the graph is built can't block deploy.
  • scripts/build_osrm.sh — idempotent MLD build/refresh (extract -> partition -> customize, NOT contract). Default: download Ural-FO extract + clip to Свердл bbox via osmium (lighter RAM). Options: CLIP=0 (zero-clip whole Ural-FO), EKB_TIGHT=1 (narrow ЕКБ bbox), WALK=1 (optional foot profile). Monthly-refresh cron note included.
  • docs/osrm-routing.md — region/pbf decision, RAM table, full VPS run-commands, verify curl, optional osrm-walk block for Phase-2.
  • .gitignoredata/osrm/* (don't commit ~1.5GB build output); .gitkeep holds the dir.

Decisions / deviations from the issue draft

  • Issue snippet used osrm-contract + ports: 5000:5000. Corrected: MLD requires partition+customize (contract is the CH algorithm, incompatible with --algorithm mld); port kept internal-only per routing-being-private.
  • Geofabrik has no Свердл-only extract -> default is Ural-FO + osmium clip to oblast bbox (lon 57.2-66.2, lat 56.0-62.0). Documented tradeoffs.
  • Car-first; walking profile is opt-in (WALK=1) + a documented (not-yet-added) osrm-walk service, to avoid an idle container until /analyze integrates.

VPS run-commands (orchestrator, after merge)

cd /opt/gendesign
git fetch origin main && git reset --hard origin/main
bash scripts/build_osrm.sh          # ~600MB dl + 30-50min build, ~3GB peak RAM, ~5GB disk
docker compose -p gendesign -f docker-compose.prod.yml up -d osrm
docker run --rm --network gendesign_default curlimages/curl:8 -fsS \
  'http://osrm:5000/route/v1/driving/60.6,56.84;60.65,56.86'   # expect {"code":"Ok",...}

Test plan

  • docker compose -f docker-compose.prod.yml config renders the osrm block (command/mem/volume/network/healthcheck)
  • bash -n + shellcheck -S warning clean on scripts/build_osrm.sh
  • (orchestrator, on VPS) build graph + up -d osrm + verify curl returns {"code":"Ok"}
  • (orchestrator) confirm free RAM before up -d osrm (free -h); fall back to EKB_TIGHT=1 if tight

Risks / open

  • +~1GB RAM on Beget VPS (Свердл-clip warm). Check free -h first; EKB_TIGHT=1 (~0.4-0.6GB) or upgrade plan if tight.
  • pbf size estimate uncertain: Ural-FO ≈600MB download; "1.5GB" from the issue ≈ whole-FO + intermediates on disk.

Refs #39

## Summary Self-hosted **OSRM** routing engine for site-finder so `/analyze` can later use real road/walking distances to POI instead of straight-line `ST_Distance(geography)`. **Infra only** — the `/analyze` integration is a separate follow-up (A2 isochrones / A3 per-category routing-decay). Backend does NOT talk to OSRM yet. ### What changed - **`docker-compose.prod.yml`** — new `osrm` service: `osrm/osrm-backend:latest`, `osrm-routed --algorithm mld --max-table-size 8000 /data/${OSRM_REGION:-sverdlovsk}.osrm`, `mem_limit: 1.5g`, `restart: unless-stopped`, network `default` only. **No `ports:`** — internal-only; backend reaches it at `http://osrm:5000`, Caddy does not proxy it. Healthcheck is a `bash /dev/tcp` port-probe (the OSRM image ships no curl/wget). `backend` does **not** `depends_on` osrm, so a crash-loop before the graph is built can't block deploy. - **`scripts/build_osrm.sh`** — idempotent MLD build/refresh (`extract -> partition -> customize`, NOT `contract`). Default: download Ural-FO extract + clip to Свердл bbox via `osmium` (lighter RAM). Options: `CLIP=0` (zero-clip whole Ural-FO), `EKB_TIGHT=1` (narrow ЕКБ bbox), `WALK=1` (optional foot profile). Monthly-refresh cron note included. - **`docs/osrm-routing.md`** — region/pbf decision, RAM table, full VPS run-commands, verify curl, optional `osrm-walk` block for Phase-2. - **`.gitignore`** — `data/osrm/*` (don't commit ~1.5GB build output); `.gitkeep` holds the dir. ### Decisions / deviations from the issue draft - Issue snippet used `osrm-contract` + `ports: 5000:5000`. Corrected: MLD requires `partition`+`customize` (contract is the CH algorithm, incompatible with `--algorithm mld`); port kept **internal-only** per routing-being-private. - Geofabrik has no Свердл-only extract -> default is **Ural-FO + osmium clip to oblast bbox** (`lon 57.2-66.2, lat 56.0-62.0`). Documented tradeoffs. - **Car-first**; walking profile is opt-in (`WALK=1`) + a documented (not-yet-added) `osrm-walk` service, to avoid an idle container until `/analyze` integrates. ## VPS run-commands (orchestrator, after merge) ```bash cd /opt/gendesign git fetch origin main && git reset --hard origin/main bash scripts/build_osrm.sh # ~600MB dl + 30-50min build, ~3GB peak RAM, ~5GB disk docker compose -p gendesign -f docker-compose.prod.yml up -d osrm docker run --rm --network gendesign_default curlimages/curl:8 -fsS \ 'http://osrm:5000/route/v1/driving/60.6,56.84;60.65,56.86' # expect {"code":"Ok",...} ``` ## Test plan - [x] `docker compose -f docker-compose.prod.yml config` renders the osrm block (command/mem/volume/network/healthcheck) - [x] `bash -n` + `shellcheck -S warning` clean on `scripts/build_osrm.sh` - [ ] (orchestrator, on VPS) build graph + `up -d osrm` + verify curl returns `{"code":"Ok"}` - [ ] (orchestrator) confirm free RAM before `up -d osrm` (`free -h`); fall back to `EKB_TIGHT=1` if tight ## Risks / open - **+~1GB RAM** on Beget VPS (Свердл-clip warm). Check `free -h` first; `EKB_TIGHT=1` (~0.4-0.6GB) or upgrade plan if tight. - pbf size estimate uncertain: Ural-FO ≈600MB download; "1.5GB" from the issue ≈ whole-FO + intermediates on disk. Refs #39
bot-backend added 1 commit 2026-06-26 19:15:16 +00:00
feat(devops): self-hosted OSRM routing engine for site-finder (#39)
All checks were successful
CI / changes (pull_request) Successful in 7s
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
26f9605c06
Stand up an internal-only OSRM container so /analyze can later use real
road/walking distances to POI instead of straight-line ST_Distance. Infra
only — the /analyze integration is a separate follow-up (A2/A3).

- docker-compose.prod.yml: osrm service (osrm/osrm-backend, --algorithm mld,
  mem_limit 1.5g, default network, no public port — backend reaches it at
  http://osrm:5000). TCP /dev/tcp healthcheck (image has no curl). backend does
  NOT depend_on osrm, so a crash-loop before the graph is built won't block deploy.
- scripts/build_osrm.sh: idempotent MLD build/refresh (extract→partition→customize,
  not contract). Defaults to Свердл-clip из Ural-FO via osmium (lighter RAM);
  CLIP=0 zero-clip and EKB_TIGHT=1 / WALK=1 options documented. Monthly-refresh note.
- docs/osrm-routing.md: region/pbf decision, RAM table, VPS run-commands, verify curl.
- .gitignore data/osrm/* (don't commit ~1.5GB build output); .gitkeep holds the dir.

Refs #39
bot-backend merged commit 4fe20a9cbd into main 2026-06-26 19:18:22 +00:00
bot-backend deleted branch feat/39-osrm-routing 2026-06-26 19:18:22 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lekss361/gendesign#1929
No description provided.