Skip to content

fix: support Docker runtime routing config with OSRM_MODES#423

Merged
DennisOSRM merged 19 commits intogh-pagesfrom
fix/runtime-env-config
Apr 26, 2026
Merged

fix: support Docker runtime routing config with OSRM_MODES#423
DennisOSRM merged 19 commits intogh-pagesfrom
fix/runtime-env-config

Conversation

@DennisOSRM
Copy link
Copy Markdown
Contributor

@DennisOSRM DennisOSRM commented Apr 25, 2026

Fixes

Resolves #421: environment override support for Docker runtime config.

Summary

This PR moves Docker routing configuration to runtime and keeps the default Docker behavior on a single default profile pointing to http://localhost:5000.

The new preferred runtime interface is OSRM_MODES, while OSRM_BACKEND remains supported as a deprecated single-backend fallback.

Behavior

  1. With no runtime routing override, Docker uses one default profile at http://localhost:5000.
  2. If only OSRM_BACKEND is set, the frontend configures one backend and emits a deprecation warning.
  3. If only OSRM_MODES is set, the frontend parses the JSON and configures the listed modes.
  4. If both are set, OSRM_MODES wins and the frontend emits a deprecation warning for OSRM_BACKEND.
  5. Local npm start keeps the public demo profiles for driving, bike, and foot.

Changes

  • add Docker entrypoint-based config.json generation at container startup
  • load runtime config before the frontend bundle initializes
  • parse OSRM_MODES JSON in the frontend and preserve deprecated OSRM_BACKEND fallback behavior
  • keep Docker default routing on localhost instead of public demo services
  • update README and inline comments to document the new configuration model and deprecation
  • add Jest coverage for deprecation warnings and direct entrypoint config generation

Docker examples

Recommended multi-profile runtime config:

docker run -p 9966:9966 \
  -e 'OSRM_MODES=[{"name":"car","url":"https://routing.openstreetmap.de/routed-car"},{"name":"foot","url":"https://routing.openstreetmap.de/routed-foot"},{"name":"bike","url":"https://routing.openstreetmap.de/routed-bike"}]' \
  ghcr.io/project-osrm/osrm-frontend:latest

Deprecated single-backend override:

docker run -p 9966:9966 \
  -e OSRM_BACKEND='http://localhost:5001' \
  ghcr.io/project-osrm/osrm-frontend:latest

Validation

  • npm run test:lint
  • npm run build
  • npm test

Fixes issue #421: Environment override no longer supported.

Users can now override configuration at container runtime using environment variables:
  docker run -e OSRM_BACKEND='http://localhost:5001' osrm-frontend

This works as documented by:
1. Creating docker/entrypoint.sh that reads OSRM_* env vars at startup
2. Generating config.json from env vars before nginx starts
3. Loading config.json in index.html before the app bundle runs
4. Using config values in src/leaflet_options.js

Supported environment variables:
- OSRM_BACKEND: Backend routing service URL
- OSRM_CENTER: Map center coordinates (lat,lng)
- OSRM_ZOOM: Initial map zoom level
- OSRM_LANGUAGE: UI language
- OSRM_LABEL: Default routing service label
- OSRM_DEFAULT_LAYER: Default map layer

Build-time configuration still supported via --build-arg.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings April 25, 2026 20:13
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds runtime (container startup) configuration for the OSRM frontend Docker image by generating a config.json from OSRM_* environment variables and loading it before the app bundle runs.

Changes:

  • Add docker/entrypoint.sh to emit /usr/share/nginx/html/config.json from OSRM_* env vars at startup.
  • Update Docker image runtime to use the new entrypoint and provide default OSRM_* ENV values.
  • Load config.json in index.html before bundle.js, and read runtime config in src/leaflet_options.js.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 8 comments.

File Description
src/leaflet_options.js Reads runtime config from window.osrmConfig and uses it for initial state and backend URL.
index.html Initializes defaults and attempts to synchronously load config.json before loading bundle.js.
docker/entrypoint.sh Generates config.json from environment variables and starts nginx.
docker/Dockerfile Adds runtime ENV defaults and sets the new entrypoint for the nginx stage.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread docker/entrypoint.sh Outdated
Comment thread docker/Dockerfile Outdated
Comment thread src/leaflet_options.js Outdated
Comment thread src/leaflet_options.js Outdated

// Get zoom level from config
function getZoom() {
return parseInt(config.OSRM_ZOOM || 13, 10);
Copy link

Copilot AI Apr 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getZoom() uses config.OSRM_ZOOM || 13, which prevents setting a valid zoom of 0 and can also return NaN if OSRM_ZOOM is non-numeric (which then propagates into defaultState.zoom). Consider checking for undefined/null explicitly and validating the parsed integer before returning it.

Suggested change
return parseInt(config.OSRM_ZOOM || 13, 10);
var zoomValue = config.OSRM_ZOOM;
var parsedZoom;
if (zoomValue === undefined || zoomValue === null) {
return 13;
}
parsedZoom = parseInt(zoomValue, 10);
if (isNaN(parsedZoom)) {
return 13;
}
return parsedZoom;

Copilot uses AI. Check for mistakes.
Comment thread src/leaflet_options.js
Comment thread index.html Outdated
Comment thread index.html Outdated
Comment thread docker/entrypoint.sh Outdated
DennisOSRM and others added 6 commits April 25, 2026 22:25
- Fix entrypoint.sh to accept CMD and exec "$@" for standard Docker behavior
- Add JSON escaping in entrypoint.sh to handle special characters in config values
- Validate OSRM_ZOOM to ensure numeric output in JSON
- Change default OSRM_BACKEND from router.project-osrm.org to localhost:5000
  (matches README.md documentation)
- Replace synchronous XHR with async fetch in index.html (sync XHR deprecated)
- Fix 404 handling in index.html (XHR fires onload, not onerror for 404)
- Add input validation to leaflet_options.js:
  * parseCenter() validates lat/lng, falls back to defaults if invalid
  * getZoom() validates numeric value, falls back if NaN
- Add comprehensive test coverage for config overrides in leaflet_options.test.js:
  * Tests for OSRM_BACKEND, OSRM_CENTER, OSRM_ZOOM, OSRM_LANGUAGE
  * Tests for invalid/edge case values with fallback behavior
  * 13 new tests added (now 112 total tests passing)

All tests pass, linting passes, Docker builds and runs correctly.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
All three routing modes (driving, bike, foot) now use the same configurable
OSRM_BACKEND, defaulting to localhost:5000 as documented in README.md.

Previously, Bike and Foot modes were hardcoded to routing.openstreetmap.de,
making it impossible to use a local OSRM instance for all modes when deployed
in Docker.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Introduce OSRM_ENVIRONMENT to differentiate execution context:
- Docker: All modes (driving, bike, foot) use localhost:5000 backend
- Local dev: Bike/foot use public routing.openstreetmap.de services

Changes:
- Add OSRM_ENVIRONMENT env var to Dockerfile (set to 'docker')
- Include OSRM_ENVIRONMENT in generated config.json via entrypoint
- Add getAlternativeBackend() that returns backend for bike/foot based on environment
- Update services array to use public routing services in dev, localhost in Docker

Benefits:
- Docker users can run full-featured local OSRM stack without modification
- Local dev still works with public services (bike/foot) when no local OSRM installed
- No breaking changes to existing behavior

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
In local dev (no OSRM_ENVIRONMENT), all routing modes now use public services:
- Driving: router.project-osrm.org
- Bike: routing.openstreetmap.de/routed-bike
- Foot: routing.openstreetmap.de/routed-foot

In Docker (OSRM_ENVIRONMENT=docker), all modes use localhost:5000 backend
for a fully local setup.

Updates:
- Refactor getBackend() to return public service URL in dev mode
- Update tests to expect public services in dev, localhost in Docker
- Add test for Docker mode explicitly

Now 113 tests pass (1 additional test for Docker mode).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add OSRM_BACKEND_DRIVING, OSRM_BACKEND_BIKE, OSRM_BACKEND_FOOT env vars
- Each mode can now have its own backend URL override
- In Docker: all modes default to localhost:5000, but can be customized per mode
- In dev: all three modes available (driving via router.project-osrm.org, bike/foot via routing.openstreetmap.de)
- Backward compatible: OSRM_BACKEND still works as fallback for all modes
- Add 6 tests covering per-mode backend configuration and dev mode availability

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Allow users to define custom names and URLs for routing modes by providing
/etc/osrm/modes.json with a JSON array of {name, url} pairs.

Example modes.json:
[
  { "name": "Car (fastest)", "url": "http://localhost:5000" },
  { "name": "Scenic Route", "url": "http://localhost:5001" },
  { "name": "Walking", "url": "http://localhost:5000" }
]

- Entrypoint now reads /etc/osrm/modes.json at startup (Docker only)
- Falls back to default modes if file not present:
  - Docker: Car, Bike, Foot (all using localhost:5000)
  - Dev: Car (router.project-osrm.org), Bike (routing.openstreetmap.de), Foot (routing.openstreetmap.de)
- leaflet_options.js now parses OSRM_MODES from config
- Each mode internally mapped to a routing profile (driving, bike, foot)
- Respects OSRM_BACKEND for first mode's URL in default config
- Add 7 tests covering custom modes, default fallbacks, and JSON parsing

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
DennisOSRM and others added 11 commits April 25, 2026 22:55
Users can now provide modes directly via docker run without mounting a file:

  docker run -e OSRM_MODES='[{"name":"Car","url":"http://localhost:5000"}]' osrm-frontend

Priority order for modes configuration:
1. OSRM_MODES environment variable (highest priority)
2. /etc/osrm/modes.json file (if mounted via -v volume)
3. Default modes (car/bike/foot using localhost:5000)

This makes it convenient for both quick testing and production deployments.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Dev mode (not in Docker):
- Uses three public routing profiles: driving, bike, foot
- Unless OSRM_BACKEND is explicitly provided (then uses just that one)

Docker mode:
- Uses single 'default' profile pointing to localhost:5000
- Can be overridden via OSRM_BACKEND env var or OSRM_MODES JSON

This ensures:
- Dev users see all three public OSRM services
- Docker users get a simple single-mode setup by default
- Either can be customized via environment variables

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Dev mode (npm start) should use public OSRM services, not localhost.
Removed OSRM_BACKEND and OSRM_LABEL from index.html defaults so that
in dev mode, parseModes() correctly defaults to three public profiles
(driving via router.project-osrm.org, bike/foot via routing.openstreetmap.de).

Only Docker mode should default to localhost:5000.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…rning

Supports both old (OSRM_BACKEND) and new (OSRM_MODES) environment variables:

Priority order:
1. OSRM_MODES (new JSON-based modes, highest priority)
2. OSRM_BACKEND (legacy, triggers deprecation warning)
3. Defaults (public services in dev, localhost in Docker)

Deprecation behavior:
- If only OSRM_BACKEND is set: creates single 'default' mode, warns to migrate
- If only OSRM_MODES is set: uses new behavior, no warning
- If both are set: uses OSRM_MODES, warns about both being configured

Example migrations:
Old: docker run -e OSRM_BACKEND='http://localhost:5000'
New: docker run -e OSRM_MODES='[{"name":"default","url":"http://localhost:5000"}]'

Add 3 tests verifying backward compat and deprecation warnings.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add detailed logging to parseModes() and buildServices() to see:
- What config is being read
- Which code path is taken (dev vs docker)
- What services array is built

This will help diagnose why bike and foot profiles are routing to
localhost instead of routing.openstreetmap.de in dev mode.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Dev mode (npm start):
- driving: router.project-osrm.org
- bike: routing.openstreetmap.de/routed-bike with correct path
- foot: routing.openstreetmap.de/routed-foot with correct path

Docker mode (default):
- Single 'default' mode using localhost:5000
- Entrypoint prioritizes: OSRM_MODES > /etc/osrm/modes.json > OSRM_BACKEND > localhost:5000

Fixed entrypoint logic to properly handle OSRM_BACKEND default value
without overriding it prematurely.

Removed debug logging - all 120 tests passing.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Docker now defaults to the same three public profiles as dev mode:
- driving: router.project-osrm.org
- bike: routing.openstreetmap.de/routed-bike
- foot: routing.openstreetmap.de/routed-foot

Users can override with:
- OSRM_MODES=... (JSON)
- OSRM_BACKEND=... (legacy, single mode)
- /etc/osrm/modes.json (file mount)

All 120 tests passing.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Docker container should default to localhost:5000 with single 'default'
profile unless explicitly configured with env vars.

Dev mode (npm start) continues to use public demo instances.

The distinction is controlled by OSRM_ENVIRONMENT:
- If 'docker' (set by entrypoint): use localhost:5000
- Otherwise (dev/browser): use public instances

All 120 tests passing.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Changed services from being computed at module load time to being
computed lazily via a getter. This ensures that when buildServices()
is called, window.osrmConfig has already been updated by config.json
loading (in Docker) or remains with dev defaults (in npm start).

This fixes the issue where Docker was reading config before
OSRM_ENVIRONMENT was set from the loaded config.json.

All 120 tests passing.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
parseModes() now reads from window.osrmConfig directly instead of
using the captured config variable. Combined with lazy-loading via
getter, this ensures that OSRM_ENVIRONMENT and other config values
are always read from the current window.osrmConfig, which gets updated
when config.json is loaded.

This fixes Docker container defaulting to public instances instead of
localhost:5000 - it now correctly detects OSRM_ENVIRONMENT='docker'
from the loaded config.json.

All 120 tests passing.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- keep Docker default routing on http://localhost:5000 with a single default profile
- support JSON-based OSRM_MODES runtime configuration for multiple profiles
- keep OSRM_BACKEND as a deprecated single-backend fallback with warnings
- document precedence and Docker usage examples
- add tests for deprecation warnings and entrypoint config generation

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@DennisOSRM DennisOSRM changed the title fix: support environment overrides at runtime via docker run -e fix: support Docker runtime routing config with OSRM_MODES Apr 26, 2026
@DennisOSRM DennisOSRM merged commit 99a9823 into gh-pages Apr 26, 2026
5 checks passed
@DennisOSRM DennisOSRM deleted the fix/runtime-env-config branch April 26, 2026 10:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Environment override no longer supported

2 participants