Getting started
This page takes you from a fresh clone of the monorepo to a running OpenMapX
stack. It's a happy-path walkthrough: you'll bring up the lightweight core, hand
the data-manager some source data, and start the app — all driven by the
openmapx CLI.
Everything runs as Docker containers, and there is no hand-maintained compose
file. You enable the services you want, render a
docker-compose.generated.yml from their manifests, and bring the stack up. The
deeper reference pages — Managing services and
Preparing data — go further than this walkthrough does;
come here first, then follow those when you need the detail.
Before you start, make sure your host meets the requirements:
a 64-bit Linux host with Docker Engine, the Compose v2 plugin, Node.js 24+, and
pnpm. The CLI runs the TypeScript sources directly — there's no build step.
docker compose, not docker-composeOpenMapX uses Compose v2, which ships as the docker compose subcommand of the
Docker CLI. The standalone v1 docker-compose binary is not supported.
1. Clone and install
Clone the repository and install the workspace once. pnpm install sets up the
monorepo and the openmapx CLI; you never install anything globally.
git clone https://github.com/OpenMapX/openmapx.git
cd openmapx
pnpm install
From here on, every command is run as pnpm openmapx <command> from the repo
root.
2. Configure your environment
A Docker deployment reads exactly one environment file: infra/docker/.env.
Docker Compose loads it automatically, and the openmapx CLI loads it too, so
the same values reach both the rendered stack and the CLI. Copy the example to
get started:
cp infra/docker/.env.example infra/docker/.env
Open infra/docker/.env and set, at minimum, the values the stack refuses to
start without. The generated compose file references several of these with the
${VAR:?} form, so a missing one fails the boot loudly rather than shipping an
insecure default:
# Public domain + TLS (Traefik / Let's Encrypt)
DOMAIN=openmapx.example.com
ACME_EMAIL=admin@example.com
# Database — generate with: openssl rand -hex 32
POSTGRES_PASSWORD=
# Auth secrets
BETTER_AUTH_SECRET= # npx @better-auth/cli@latest secret
OPENMAPX_SECRETS_KEY= # openssl rand -hex 32
# Internal service auth — generate each with: openssl rand -hex 32
DATA_MANAGER_AUTH_TOKEN= # app-api ↔ data-manager
OPENMAPX_LOCAL_ADMIN_TOKEN= # CLI ↔ loopback admin
# Host wiring (Linux)
OPENMAPX_HOST_DIR= # absolute path of this checkout — run `pwd` here
DOCKER_GID= # docker-socket group id — `stat -c %g /var/run/docker.sock`
OPENMAPX_HOST_DIR and DOCKER_GID let the app-api and data-manager containers
drive Docker on the host, so the CLI's lifecycle and data commands work from
inside the stack. The .env.example file documents how to generate each secret
inline.
Everything else — map-tile keys, traffic and street-imagery tokens, OAuth login, per-integration credentials — is optional at boot and mostly managed from the admin panel after the stack is up. For the full reference, see Configuration.
The default NEXT_PUBLIC_STYLE_PROVIDER=maptiler renders maps from MapTiler
Cloud and needs a MAPTILER_KEY. You can leave the key unset for now and add it
from the admin UI later, or self-host tiles with the tileserver service.
3. Enable a minimal set of services
OpenMapX separates an always-on core from the optional, heavier backend engines. If you do nothing, the default selection is just the core:
traefikandwell-known(reverse proxy + passkey/well-known files)app-apiandapp-web(the Fastify API and the Next.js frontend)postgisandredis(database + cache)data-manager(owns the/datatree and downloads source data)
That core renders maps and serves the app, but self-hosted search, routing, and transit come from the optional engines you opt into. For a first boot, a small self-hosted stack is a good target — a lightweight geocoder and a regional router:
pnpm openmapx services enable photon osrm
This persists your choice to infra/docker/service-selection.json; the renderer
expands each root into its dependencies automatically, so you only list the
engines you actually want. You can confirm what's effectively selected with:
pnpm openmapx services selected
To skip the heavy engines entirely and run the core against hosted endpoints instead, just don't enable anything here — the default core selection is enough to bring the app up. See Managing services for the full list of engines, presets, and how capability bindings work.
4. Render the compose stack
Generate the compose file from your enabled service manifests:
pnpm openmapx compose render
This reads infra/docker/.env (so it picks up DOMAIN automatically) and
writes two files, both gitignored:
infra/docker/docker-compose.generated.yml— the actual compose fileinfra/docker/docker-compose.generated.hardlinks.json— the plan that maps downloaded source files to per-service consumer paths
Re-render any time .env, the service selection, or a manifest changes — the
output is deterministic. Many CLI commands (services start, data link,
compose up) re-render for you, so you rarely run this by hand after the first
time.
5. Bring up the infrastructure first
Start the database, cache, proxy, and data-manager before anything else. The
data-manager needs its /data volume mounted before you can ask it to download
anything:
pnpm openmapx services start postgis redis traefik data-manager
Confirm the data-manager is healthy:
curl -s http://localhost:4000/status
You should see a small JSON object reporting "ok": true and "dataDir": "/data". If the call fails, give the container a few seconds to finish starting
and try again.
6. Download source data
The data-manager does the actual fetching; the CLI is a thin client over its
HTTP API. Download an OSM extract for the region you care about — regions follow
the Geofabrik layout (europe/germany,
north-america/us, planet, …):
pnpm openmapx data download osm europe/germany
Then grab the bundled map styles, fonts, and sprites used by the frontend and self-hosted tiles:
pnpm openmapx data download style
If you're running a transit engine, also pull GTFS feeds (via the community-curated Transitous catalog), filtered to the countries you want:
pnpm openmapx data download gtfs --countries de,at,ch
Inspect what's been downloaded at any time:
pnpm openmapx data status
Set OPENMAPX_REGION=europe/germany in infra/docker/.env and you can drop the
positional region from data download osm (and the build commands below). A
region passed on the command line still wins for that one run.
Preparing data covers custom feed lists, per-feed API keys, and how the hardlink sharing works under the hood.
7. Build prepared artifacts
Some engines need an explicit build step that turns the downloaded OSM/GTFS data
into the graph or index they serve from. For the regional router we enabled
above (osrm), run its build — Photon downloads a prebuilt index on first start,
so it needs no build here:
pnpm openmapx services build osrm --region europe/germany
--region is optional; without it the build falls back to a service-specific
env default, then OPENMAPX_REGION, and finally the single OSM extract on disk
if there's exactly one. Other buildable engines (motis, otp, tileserver,
pelias) follow the same pattern. To build every selected buildable service in
one pass:
pnpm openmapx services build-all --region europe/germany
Heavier engines that aren't in this list (Valhalla, Nominatim, Overpass) build their indexes from the OSM extract on first container start instead — that happens automatically when the stack comes up, and it can take hours at large scale.
8. Apply the hardlink plan
Link the downloaded source files into each consuming service's data directory. The data-manager creates filesystem hardlinks, so every consumer sees its own copy at zero extra disk cost:
pnpm openmapx data link
This re-renders the compose plan first (so the links match your current service
selection) and then applies it. build, build-all, and compose up run this
step for you, but it's worth knowing as its own command for when you refresh data
without rebuilding.
9. Bring up the full stack
Finally, start everything:
pnpm openmapx compose up
This re-renders, applies the hardlink plan, and runs docker compose up -d for
the whole enabled selection. Prepared services start from the artifacts you built
in step 7; any heavy engine builds its internal index from the OSM extract on
first start, which is slow at continent or planet scale.
Watch progress with:
pnpm openmapx services status # container state for every service
pnpm openmapx services logs <id> --follow # tail one service's logs
On modest hardware, starting several heavy indexers at once can saturate CPU,
disk, and RAM. The indexers are independent, so it's usually better to start one,
wait for it to finish its initial build (CPU and disk I/O quiet down, and
services status reports it healthy), and only then start the next. See
Managing services for a staggered-startup order.
10. Create your first admin user
Open https://your-domain in a browser and sign up through the web UI. Traefik
obtains a Let's Encrypt certificate automatically on the first request, once your
domain's DNS points at the host. Then promote your account to admin from the repo
root:
pnpm openmapx users promote you@example.com
That account now has access to /admin, where you manage integrations, service
config, capability bindings, data workflows, and the rest of the operations
surface.
Where to go next
- Configuration — the full
infra/docker/.envreference, including the optional keys this walkthrough skipped. - Managing services — the engine catalog, presets, capability bindings, and staggered startup for heavy backends.
- Preparing data — custom GTFS feeds, per-feed API keys, region selection, and how hardlink sharing works.
- Upgrading — pulling new code and images, and refreshing source data.