Skip to main content

Self-hosting transit engines

Complete Getting started first. Transit needs valid schedule feeds for the area and date being queried, enough preparation capacity, and a working provider integration. A running container alone does not prove a loaded timetable.

MOTIS or OTP?​

MOTISOpenTripPlanner (OTP)
Managed workflowTransactional schedule acquisition, candidate import, probes, promotion, rollbackOffline graph build and restart
Intended scopeRegional by default; planet operation is experimentalRegional; planet input and PBFs over 50 GB refused
OSMRecommended for assisted regional operation; required by sovereign/planet policyRequired by the CLI build
Schedule inputsPinned Transitous catalog and operator sourcesGTFS ZIPs staged locally before building
OpenMapX adaptertransit-motis, with local provider priority 1transit-otp, planning provider priority 6
API used by adapterMOTIS v2 RESTOTP 2.10 GTFS GraphQL at /otp/gtfs/v1

Choose MOTIS for the managed schedule-refresh workflow. OTP can serve a regional planning use case, but has no automatic graph refresh here. Its feasible feed count depends on data size and the build heap; there is no fixed ten-feed limit. See Requirements before importing either engine.

Self-hosting MOTIS​

1. Enable the service​

Enable the query engine and the candidate engine:

pnpm openmapx services enable motis motis-staging

Dependencies select motis-feed-proxy. Do not use --preset transit unless you also intend to prepare and run OTP: that preset includes both engines.

Set the regional workflow in infra/docker/.env before preparing data:

MOTIS_OPERATIONS_PROFILE=regional-assisted
TRANSITOUS_COUNTRIES=de
MOTIS_OSM_REGION=europe/germany
MOTIS_TWO_SLOT=true
MOTIS_SLOT_MEMORY=16g
MOTIS_SLOT_MEMORY_GB=16
MOTIS_SLOT_CPU=4
TRANSITOUS_SYNC_CRON=disabled

Keep the scheduler disabled during initial preparation. The two memory settings serve different purposes: MOTIS_SLOT_MEMORY sets each runtime container limit; MOTIS_SLOT_MEMORY_GB reports capacity to preflight. Active and staging slots can run together, so budget for both and for retained data generations.

regional-sovereign requires origin acquisition, local OSM, source/license metadata, and an external egress policy. Planet operation requires explicit confirmation and capacity; it is not obtained by leaving countries blank. Read MOTIS operations profiles before using either profile or migrating an existing live directory into A/B slots.

2. Get the source data​

pnpm openmapx data download osm europe/germany

This OSM extract supplies street access/egress data. Assisted regional operation can run without a matching extract, with reduced street routing; sovereign and planet profiles reject that omission. The low-level CLI build below requires an OSM input.

For restricted catalog feeds, generate and fill the key template first:

pnpm openmapx data generate-api-keys
# Edit services/motis/tools/transitous/api-keys.json

Keep this file private. Data-manager and runner mount it at /config/transitous/api-keys.json. The release overlay must be selected before a host-side MOTIS build so its helper image matches the release.

3. Build the prepared data​

For an initial deployment, with neither MOTIS consumer running, seed its config, feeds, license, and local feed-proxy configuration:

pnpm openmapx services build motis --region europe/germany

The positional-region alias is pnpm openmapx data build motis europe/germany. MOTIS_REGION and OPENMAPX_REGION provide build defaults, independently of the daemon's MOTIS_OSM_REGION. Keep them consistent with the selected dataset.

This low-level build writes prepared inputs in place. It is intended for initial or off-host preparation, not routine updates to a serving dataset. Add --import to services build motis when preparing an export on a larger machine; see Building on another host.

pnpm openmapx data link
pnpm openmapx services start motis motis-staging
pnpm openmapx services start app-api data-manager
pnpm openmapx services logs motis --follow

The API and data-manager recreation applies their new service selection and workflow environment. MOTIS starts with motis import followed by motis server. The staging container can wait for its first generated config. A failed import can leave a container running in a holding loop; inspect logs and query endpoints.

Now run the managed candidate workflow and monitor the returned job:

pnpm openmapx data sync --countries de
pnpm openmapx data source list

The primary endpoint is http://motis:8080, published on host loopback 8081. Staging uses loopback 8083 and does not serve application traffic. Both run as UID:GID, default 1000:1000.

Adding GTFS feeds​

Inspect source IDs and desired/active state:

pnpm openmapx data source list
pnpm openmapx data source remove catalog:de:vbb
pnpm openmapx data source enable catalog:de:vbb

Use an ID actually present in your source list. For an operator source:

pnpm openmapx data source add https://example.org/agency-gtfs.zip \
--name "Example Transit" \
--region de-be \
--attribution "Example Transit" \
--license-spdx CC-BY-4.0

Replace the example URL and provenance with a real permitted feed. Each mutation queues an asynchronous complete candidate build. Desired state changes before active state; only successful promotion changes the live source set.

Operator URLs cannot embed credentials or point to private network addresses. Acquisition uses a bounded, single-use relay; the private runner receives that relay instead of the remote operator URL. Authenticated catalog feeds use the key file. See Transit schedule sources for acquisition limits and protected inputs.

Keeping feeds fresh: the staging pipeline​

After the first sync and query checks pass, set TRANSITOUS_SYNC_CRON=0 3 * * * in .env and recreate data-manager. The default schedule is daily 03:00 UTC; disabled, off, or false disables it. Staleness checks default to daily 04:00 UTC and have the same disable values.

A sync acquires the requested feed set, prepares and probes a candidate, stops the required processes, promotes the candidate, recreates the primary so Docker resolves its data mount, and probes the stable endpoint. With A/B slots enabled, a failed post-activation probe restores the prior healthy slot. There is a bounded interruption during promotion; this is not a zero-downtime guarantee.

Keep both ops-agent and data-manager current before resuming promotion after an upgrade. An unknown stop/recovery outcome requires container inspection; do not retry promotion blindly. The operations runbook explains the retained state and recovery boundary.

Where the data comes from: mirror vs build​

TRANSIT_SOURCE=mirror downloads preprocessed Transitous GTFS/NeTEx artifacts; TRANSITOUS_ARTIFACT_BASE_URL can select another artifact source. TRANSIT_SOURCE=build runs the pinned catalog's acquisition scripts through the private Transitous runner and fetches feeds from their origins. The regional assisted profile defaults to mirror; sovereign operation requires build.

Both modes generate configuration and attribution locally and use the local feed proxy for mapped realtime and GBFS resources. They still have external source dependencies. Inspect the generated configuration and admin status when checking a sovereignty requirement; application checks do not replace firewall or DNS policy.

MOTIS_ROUTE_SHAPES=missing synthesizes absent route geometry from OSM. all is more expensive. Both add preparation work and storage; measure candidate imports before enabling them.

To inspect or advance the catalog pin:

pnpm openmapx transitous show
pnpm openmapx transitous bump

Bump asks permission to write the Transitous and GBFS proposed lockfiles. It does not change the active pins or running graph. Follow Pin upgrades to review the proposal, validate an inactive slot, and separately approve the exact proposed ref. Opt-in TRANSITOUS_AUTO_BUMP_CRON tests a proposed upstream revision through the candidate workflow before activating it. A rejection retains the previous pin. Set TRANSITOUS_ALERT_GH_TOKEN and TRANSITOUS_ALERT_GH_REPO if failures should open deduplicated issues; otherwise monitor logs and dataset age. A retained healthy dataset can still become stale.

Self-hosting OpenTripPlanner​

1. Enable and get data​

pnpm openmapx services enable otp
pnpm openmapx data download osm europe/germany

Place permitted regional GTFS ZIP archives in infra/docker/data/gtfs/ before building. For example, copy an archive you have already obtained and validated:

mkdir -p infra/docker/data/gtfs
cp /path/to/agency.gtfs.zip infra/docker/data/gtfs/agency.gtfs.zip

The OTP build stages every top-level .zip in that directory; it does not filter by the OSM region. Keep only compatible GTFS feeds for this graph there. data sync is a MOTIS lifecycle operation, not an OTP-only download or updater. It can supply catalog archives when MOTIS is configured, but does not rebuild OTP.

2. Build the graph​

pnpm openmapx services build otp --region europe/germany

Preparation runs OTP 2.10.0 with --build --save, stages a replacement, then publishes data/otp-graph/graph.obj. The build refuses planet.osm.pbf and inputs over 50 billion bytes. Its default Java heap is -Xmx24g, independent of the 16 GiB runtime container ceiling and -Xmx12g runtime heap. OTP_BUILD_JAVA_TOOL_OPTIONS=-Xmx8g can reduce the build heap for a small graph; reserve host overhead beyond the heap and measure a representative build.

Stop otp before rebuilding. Missing or invalid feeds can produce failed or incomplete transit preparation even when street data is available.

pnpm openmapx services start otp
pnpm openmapx services start app-api
pnpm openmapx services logs otp --follow

Start re-renders and links automatically. The container uses --load --serve and publishes http://otp:8080 as host loopback 8090. Build and routing config files live under services/otp/config/. Rebuild for build-config or input changes; recreate the runtime for container or router configuration changes.

After refreshing the PBF or GTFS files:

pnpm openmapx services stop otp
pnpm openmapx services build otp --region europe/germany
pnpm openmapx services start otp

Pointing the transit integration at your engine​

Enable transit-motis for local MOTIS planning and related capabilities; geocoding-motis supplies stop geocoding. A selected local service resolves before integration config or MOTIS_URL. Without local MOTIS or an explicit local endpoint, assisted deployments can use the separate public Transitous provider. Features requiring the local static dataset are then unavailable.

transit-otp is a planning-only adapter. It resolves selected otp first, then its configured endpoint, with development fallback http://localhost:8090. INTEGRATION_TRANSIT_OTP_ENDPOINT overrides the admin value within config but does not replace selected-service resolution. Disable the matching local service and recreate API when switching these adapters to external instances.

Configure participation and credentials at /admin/integrations. See Public transit for the chain.

Verifying it works​

On the Docker host, check MOTIS liveness and queries within loaded coverage:

curl --fail --silent --show-error http://localhost:8081/api/v1/health
curl --fail --silent --show-error \
'http://localhost:8081/api/v1/map/stops?min=52.51,13.36&max=52.54,13.38' | jq 'length'
curl --fail --silent --show-error \
'http://localhost:8081/api/v1/geocode?text=Berlin+Hbf' | jq '.[0].name'
curl --fail --silent --show-error \
'http://localhost:8081/api/v1/plan?fromPlace=52.525,13.369&toPlace=48.140,11.558' \
| jq '.itineraries | length'

Health can answer before the timetable is queryable. Empty stops or plans can also reflect geography or schedule dates, so use a known stop and a currently operating trip. Static timetables use the stop's local day; route-pattern IDs belong to the active dataset epoch and must not survive promotion.

For OTP, check the same GraphQL surface OpenMapX uses:

curl --fail --silent --show-error http://localhost:8090/otp/gtfs/v1 \
-H 'Content-Type: application/json' \
-d '{"query":"query OtpReadiness { __typename }"}'

A response with data.__typename confirms GraphQL reachability, not a usable transit plan. Plan a known journey in OpenMapX, check itinerary attribution and OTP logs, and verify that the chosen route is transit rather than a walking-only fallback. OpenMapX does not use the retired REST-v1 plan example or the old /otp/routers/default/index/graphql endpoint.

See also​