docker pull atcr.io/rtw.run/waldo:latest
waldo
A low-resource forward/reverse geocoder that uses only WhosOnFirst administrative data. That means: no addresses, no postal codes, no points of interest — it geocodes administrative boundaries only, so think cities, regions, neighborhoods, countries, and the like.
Typical planet-scale geocoders like Nominatim or Pelias need on the order of 64GB of RAM and a complicated operational architecture (Elasticsearch in the mix) because they index postal addresses from all over the world. Waldo trades that address-level precision for simplicity: if city- or neighborhood-level precision is enough, you can run a planet-scale geocoder on less than 2GB of RAM with response times under 250ms.
Waldo was built for rtw.run, an adventure motorcycle trip planning app, where searching for a city is sufficient for planning a route. For places that aren’t administrative areas (a restaurant, hotel, or venue), the forward geocoder also decodes Google’s Open Location Codes (plus codes): paste something like 35X7+FH Shimla, Himachal Pradesh, India and it first geocodes the reference city, then resolves the plus code to an exact location.
Under the hood, a curated subset of the WhosOnFirst parquet data is loaded into an in-memory DuckDB database and queried with SQL, with an index on a 3-letter name prefix to keep lookups fast.
Source: https://kilimanjaro.io/waldo · License: MIT
Running the container
Multiarch image (amd64 + arm64): atcr.io/rtw.run/waldo:latest
The image contains the binary + entrypoint only. The parquet data (~5.9GB planet) lives on a volume and is downloaded from data.geocode.earth at container start — keep >= 12GB free on the volume (the parquet is replaced via .tmp + rename).
mkdir -p /srv/rtw/waldo
podman run -d --name waldo --userns=keep-id \
-p 127.0.0.1:8999:8999 \
-v /srv/rtw/waldo:/data \
atcr.io/rtw.run/waldo:latest
| Var | Default | Meaning |
|---|---|---|
PARQUET_URL |
planet WhosOnFirst admin parquet | Source parquet. Country extracts work for small deployments/tests. |
UPDATE_CHECK |
1 |
On start, conditional-GET with the saved ETag and re-download when upstream changed. 0 = never touch the network. |
DATA_DIR |
/data |
Where world.parquet and its .etag live. |
/health only comes up after the parquet finishes loading into the in-memory DuckDB — allow ~1min at planet scale. Then:
curl 'http://localhost:8999/forward?q=kathmandu' # forward geocode
curl 'http://localhost:8999/reverse?lat=27.7&lon=85.3' # reverse geocode
Replace world.parquet and podman kill -s HUP waldo to reload the database on the fly (~2x RAM transiently during reload).