Routing docs

Self-hosting routing

The geolith router library and its Cloudflare Worker app, which reads a .glx routing part from R2 block by block. Not deployed for public use. Latency and CPU measured on a test Worker, and the work in progress to make it fast.

By · Updated

Status: in development, not deployed for public use. There is no released router to download yet. The router library (geolith-router, crates/router in geolith/routing) and a Cloudflare Worker app around it (apps/worker) exist. The Worker runs only as a test deployment on workers.dev. This page says how it runs and what was measured.

The router library

The router library is pure Rust and does no I/O of its own: it asks for the bytes a request needs and the program around it fetches them. It builds for wasm32-unknown-unknown, so the same code runs natively and as WebAssembly. It keeps the tile index in memory and a byte-bounded cache of decompressed blocks (16 MiB by default).

The Worker app

The Worker serves the six endpoints over the routing part of a .glx file in R2. It reads the blocks a request needs with R2 range reads and merges adjacent blocks into one read. Every answer carries its cost in x-route-* headers: R2 reads, bytes, fetch rounds, time spent waiting for R2, and wall time.

Measured on the edge

A test Worker on workers.dev, with geolith's own Berlin build in R2, answered the 100 test routes from Mumbai. Two runs on 8 Nov 2026:

MeasureResult
Routes answered100/100, no out-of-memory errors
Latency, median7.6 s and 7.7 s
Latency, p9527 s to 28 s
Fetch rounds per route, median26 to 27, each a 270 ms to 300 ms R2 read
Engine CPU per route, median445 ms and 480 ms
/isochrone, 5-minute contourHits the Worker CPU limit

The router is fast; the storage round trips are not. Each fetch round waits for the blocks the search has just found it needs, so a route pays one R2 read after another.

In progress

  • A block store in KV and the Cache API, as geolith search uses, so warm blocks do not need an R2 read.
  • Spatial read-ahead, so one fetch round brings the blocks the search will need next.

The target is a warm median of 400 ms or less. Routing is not offered until it is fast enough.

The file

A router needs a .glx file with a routing part. The builder writes one:

geolith --output-format routing --osm-pbf berlin.osm.pbf \
  --routing-output berlin-routing.glx

A file with both a search part and a routing part serves both: glx-parts merge joins the parts of a search file and a routing file into one file, and glx-parts info lists the parts a file holds and checks their checksums. Each reader reads only its own part.