# 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.

```bash
git clone <your checkout of wherebox> && cd wherebox
pnpm install
```

Create 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.

```bash
# 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:countries
```

The 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

```bash
pnpm --filter @wherebox/api start       # http://localhost:8080/playground
```

Check it with a first map:

```http
GET /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](/docs/reference/configuration.md); 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_URL` when the API sits behind a proxy, so `?response=json` returns 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 --build` as 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.

```bash
# .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.pbf
```

The 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_PROVIDER` and a server-side key, or let callers send their own `X-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](/docs/guides/routes.md) 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](/docs/reference/metrics.md). All variables are in the [configuration reference](/docs/reference/configuration.md#geocoding).
