On this page
Self-hosting
Wherebox is one Node 22 API process plus a PostGIS database. Rendering uses MapLibre Native, so the machine needs OpenGL (software GL is fine) and the process is CPU-bound.
Install#
You need Node 22 (MapLibre Native ships prebuilt binaries only for specific Node versions; .nvmrc pins it), pnpm, PostgreSQL with PostGIS, and osmium-tool for the import.
git clone <your checkout of wherebox> && cd wherebox
pnpm installCreate a PostGIS database and point DATABASE_URL at it (the default is postgres:///wherebox).
Import data#
Areas and places come from an OpenStreetMap extract; the neighbor labels come from Natural Earth.
# Czechia: boundaries and places (about a minute)
mkdir -p data/raw
curl -LO --output-dir data/raw https://download.geofabrik.de/europe/czech-republic-latest.osm.pbf
pnpm import:cz
# World countries for neighbor labels (about 30 seconds)
pnpm import:countriesThe importer rebuilds the tables and swaps them in atomically, so re-importing a fresh extract does not interrupt a running server. Country-level areas (area=Japan, iso:IT, wikidata:Q17) fall back to Natural Earth when the OSM import does not cover them, so import:countries makes every country available as an area. Regions, cities and other finer areas outside the extract return 404 not-found, which is why the examples in these docs stick to Czech places. Without import:countries, maps work but neighbors has no countries to label.
Start the API#
pnpm --filter @wherebox/api start # http://localhost:8080/playgroundCheck it with a first map:
/v1/map.pngGET /v1/map.png?area=Czechia&w=468&h=280
/healthz says the process is up; /readyz also checks the database and reports the data version and the render queue. /openapi.json and /playground are served by the API itself, and / redirects to the playground. The API does not serve these docs: they are published at https://wherebox.dev/docs, and the Markdown sources are in the docs/ folder of the repository. Problem type URIs link to the published error reference; set WHEREBOX_DOCS_URL to point them at your own copy.
Configuration#
Everything is configured with environment variables: the database, the port, the image cache (disk, S3-compatible or none), render concurrency and queue, routing, and the allow-lists for remote icons and fonts. The authoritative list, with defaults and which values are secrets, is the configuration reference; this guide does not repeat it.
Three decisions you will actually make:
- Cache. The disk cache is capped and evicts the least-recently-used images. Use an S3-compatible store if you run more than one API process, so they share it.
- Remote icons and fonts. Both are off until you list trusted hosts. Leave them off unless you need them.
- Public URL. Set
WHEREBOX_PUBLIC_URLwhen the API sits behind a proxy, so?response=jsonreturns correct image URLs.
Docker and Compose#
Unverified until the first deployment. The Dockerfile and compose file were written without a Docker host available. Treat the first
docker compose up --buildas the real test and expect to fix something.
The repository's docker-compose.yml runs db (PostGIS), api and caddy (TLS in front of the API), plus an importer service under the tools profile. The image is based on Ubuntu 24.04 because the MapLibre Native binaries target it, and it starts Xvfb for headless GL.
# .env needs at least WHEREBOX_DOMAIN and POSTGRES_PASSWORD (see deploy/README.md)
docker compose up -d --build
deploy/import.sh https://download.geofabrik.de/europe/czech-republic-latest.osm.pbfThe step-by-step runbook (sizing, firewall, DNS, backups, troubleshooting) is deploy/README.md.
Routing engine#
car, bike and foot routes need a routing engine; straight, geodesic and given lines do not. Options:
- Valhalla (the default provider): self-host it and set
WHEREBOX_VALHALLA_URL. This is the recommended production setup. - openrouteservice or GraphHopper: set
WHEREBOX_ROUTING_PROVIDERand a server-side key, or let callers send their ownX-Routing-Key.
The public Valhalla demo instance run for OpenStreetMap is for development only (about one request per second, not for third-party production use). Do not point production at it. Without a configured engine, routed profiles return 400 bad-request; see Routes for the request side.
Geocoder fallback#
place: points and route ends are looked up in the imported OSM places. For names outside your import, set WHEREBOX_GEOCODER_URL to a geocoder. The database is always asked first, and the geocoder only for names it does not know. A server without a database can use the geocoder alone.
- Nominatim (the default
WHEREBOX_GEOCODER_PROVIDER), self-hosted or any Nominatim-compatible service. Put a key in the URL if the service needs one (https://…/?key=…); it is never shown in errors. - Photon: set
WHEREBOX_GEOCODER_PROVIDER=photon.
Results are cached in the process (found places for a week, misses for an hour), identical lookups share one request, and requests are spaced to WHEREBOX_GEOCODER_RPS per second. The public nominatim.openstreetmap.org and photon.komoot.io instances are for development only: their usage policies allow about one request per second, and Nominatim requires a WHEREBOX_GEOCODER_USER_AGENT that identifies your application. Watch wherebox_geocoder_total in metrics. All variables are in the configuration reference.