OpenHunt Docs
Self-hosting

Self-hosting

Run the whole platform on your own hardware — API, Postgres, web app, and your own tiles.

Self-hosting is a supported product, not a README promise: the compose stack boots and passes smoke tests in CI, and any design that would break single-box self-hosting gets rejected. Self-hosters get every feature ungated — sync, offline, sharing — because paid plans sell hosted convenience, not capability.

A full deployment is three pieces:

  1. The API + Postgres — accounts, sync, sharing.
  2. The web app — the map workspace.
  3. Tiles — static PMTiles you generate with the pipeline for the states you hunt.

Quickstart: API + Postgres

cd deploy/compose
docker compose up -d

That brings up Postgres (PostGIS image) and the API on http://localhost:8080, with migrations applied automatically. Verify with:

curl http://localhost:8080/healthz

The full single-box stack

deploy/homelab runs everything — Postgres, the API, the background worker, and the web app — localhost-bound so it can't collide with anything else on the box:

cd deploy/homelab
./deploy.sh        # build, migrate, start, verify — idempotent

Endpoints: web on http://localhost:3200, API on http://localhost:8090. Postgres data persists in a named volume; tiles bind-mount read-only from the repo's tiles directory, so regenerating them is a pipeline run plus a web-container restart (the web tier snapshots its public directory at startup).

Updating is git pull && ./deploy.sh — it rebuilds changed layers, restarts, and re-verifies.

Bringing your own tiles

The API never serves tiles — map data is static files. Run the pipeline for your states and drop the resulting .pmtiles in the tiles directory the web tier serves. Published OpenHunt artifacts are also freely downloadable, so you can start from those instead of processing raw sources yourself.

Exposing it with a hostname

Put a reverse proxy (traefik, Caddy, nginx) in front of the web and API containers, then:

  • set COOKIE_SECURE=true on the API,
  • set ALLOWED_ORIGINS to your web origin (e.g. https://hunt.example.com),
  • rebuild the web app with NEXT_PUBLIC_API_URL pointing at the API's public URL.

The configuration reference covers every variable.

Sizing

Boring tech on purpose: one Rust binary, one Postgres. A small VM or a homelab box runs the whole thing; map serving costs nothing because tiles are static range-requested files, and there is no tile server to scale.

On this page