MOTIS on Ubuntu 24.04 on Azure User Guide
Overview
MOTIS is the open source multimodal journey planning engine that powers large scale public planners such as Transitous. It imports OpenStreetMap street data together with GTFS public transport timetables into an on disk dataset, then serves a JSON routing API and a map based web interface on a single port. It computes optimal door to door itineraries that combine walking and public transport, with departures, arrivals, transfers and a drawn route on the map.
The MOTIS server is a single self contained native binary that serves both the REST API and the bundled web app. It runs behind nginx as a reverse proxy on port 80. The engine listens on 127.0.0.1:8080 and binds to the loopback interface only, so the only public surface is nginx.
MOTIS is a public by design, read only service: it exposes only routing, geocoding, map tile and health GET endpoints. There is no login, no administrator account and no mutating endpoint, so no credentials ship in the image or are generated at first boot. On the first boot of every deployed VM a one shot service resolves the VM public address, writes an instance info file to /root/motis-info.txt, records the bootstrap ready marker, and starts the engine and the front door. The engine unit is gated on that marker, so it never serves before first boot has completed.

What is included:
- MOTIS 2.11.1 server (single native binary, bundles the web UI and map tile profiles) at
/var/lib/motis/motis - nginx reverse proxy on
:80in front of the MOTIS engine on loopback:8080, with version disclosure suppressed - A small ready to explore sample dataset baked into the image: a central Freiburg OpenStreetMap extract (© OpenStreetMap contributors, Open Database License ODbL) paired with a compact synthetic GTFS demonstration timetable (CC0), so the planner answers real journeys on first boot
motis.serviceandnginx.serviceas systemd units, enabled and active, plus amotis-firstboot.serviceone shot that runs once per VM- The engine runs as a dedicated non root
motisservice account with no shell and no sudo - A fully patched Ubuntu 24.04 LTS security baseline at capture time, with unattended security updates enabled
- 24/7 cloudimg support
Key facts: platform Ubuntu 24.04 LTS on Azure, default SSH user azureuser, application home /var/lib/motis, web UI and API on port 80, engine loopback on 8080.
Prerequisites
An active Azure subscription, an SSH key pair, and a VNet plus subnet in the target region. Standard_B2s (2 vCPU / 4 GiB RAM) is a good starting point for the bundled sample region. A larger region or a real country feed needs more memory and disk, so scale the VM size and OS disk to the dataset you import. NSG inbound: allow 22/tcp from your management network and 80/tcp (plus 443/tcp once you place your own TLS terminator in front) from the networks your users will reach the planner on.
Step 1 — Deploy from the Azure Marketplace
Sign in to the Azure Portal, choose Create a resource, search the Marketplace for MOTIS by cloudimg, and select Create. On Basics pick your subscription, resource group, region and size; under Administrator account choose SSH public key and paste your key; under Inbound port rules allow SSH (22) and HTTP (80). Then Review + create and Create. First boot initialisation takes a few seconds after the VM starts.
Step 2 — Deploy from the Azure CLI
az vm create \
--resource-group <your-rg> \
--name motis \
--image <marketplace-image-urn> \
--size Standard_B2s \
--admin-username azureuser \
--ssh-key-values ~/.ssh/id_ed25519.pub \
--vnet-name <your-vnet> --subnet <your-subnet> \
--public-ip-sku Standard
az vm open-port --resource-group <your-rg> --name motis --port 80 --priority 1010
Step 3 — Connect to your VM
ssh azureuser@<vm-public-ip>
Look up the public IP at any time with az vm show -d -g <your-rg> -n motis --query publicIps -o tsv.
Step 4 — Verify the MOTIS stack
Two systemd services make up the stack. Confirm both are active and that the engine binds loopback only while nginx serves the public port:
systemctl is-active motis nginx
ss -tln | grep -E ':80 |:8080 '
Both lines read active, and the socket list shows nginx on 0.0.0.0:80 with the MOTIS engine bound to 127.0.0.1:8080 only:
active
active
LISTEN 0 511 0.0.0.0:80 0.0.0.0:*
LISTEN 0 128 127.0.0.1:8080 0.0.0.0:*
LISTEN 0 511 [::]:80 [::]:*

Step 5 — Open the journey planner
Browse to http://<vm-public-ip>/. The MOTIS web UI opens with the journey planner on the left and the map on the right. Enter a start and destination (or click two points on the map) and MOTIS plans the connections, showing departure, arrival, transfers, duration and the drawn route.

A journey with a change and longer walking legs is planned the same way; MOTIS chooses the fastest multimodal option and lists earlier and later alternatives:

Step 6 — Plan journeys over the routing API
The same routing is available as a JSON REST API. The plan endpoint takes a fromPlace and toPlace (a lat,lng pair or a stop id) and returns ranked itineraries. The radius parameter uses transit stops within that many metres of each coordinate:
curl -s 'http://127.0.0.1/api/v6/plan?fromPlace=47.9990,7.8410&toPlace=47.9900,7.8640&radius=1000&time=2026-08-10T06:00:00Z' \
| jq '{itineraries: (.itineraries | length), first: (.itineraries[0] | {startTime, endTime, duration, transfers})}'
The response contains real itineraries with real departure and arrival times computed from the timetable:
{
"itineraries": 5,
"first": {
"startTime": "2026-08-10T06:00:00Z",
"endTime": "2026-08-10T06:08:00Z",
"duration": 480,
"transfers": 0
}
}

Geocoding turns a place name into coordinates and stop ids:
curl -s 'http://127.0.0.1/api/v1/geocode?text=Bertoldsbrunnen' | jq -c '.[0] | {name, type, lat, lon}'
Step 7 — Health and per VM instance info
The health endpoint reports whether MOTIS has completed a full cycle over its configured feeds:
curl -s -o /dev/null -w 'health: HTTP %{http_code}\n' http://127.0.0.1/api/v1/health
Every VM writes an instance info file on first boot with its own reachable URL and the read only, public by design note:
sudo cat /root/motis-info.txt

Step 8 — The sample dataset and its licenses
The image ships a small demonstration dataset so the planner works out of the box. The attribution and licensing are recorded on the VM:
cat /var/lib/motis/DATA-LICENSES.txt
The street and map data is an extract of OpenStreetMap, © OpenStreetMap contributors, licensed under the Open Database License (ODbL). The public transport timetable is a synthetic demonstration GTFS feed authored by cloudimg and released under CC0; it does not represent any real transit service.

Step 9 — Import your own region
To plan real journeys for your own area, replace the sample data with a real OpenStreetMap extract (for example from Geofabrik) and one or more real GTFS feeds, then rebuild the on disk dataset. This is a one time build step, run as the motis service account:
# Download an OpenStreetMap extract for your region and one or more GTFS feeds into /var/lib/motis, then:
cd /var/lib/motis
sudo -u motis ./motis config your-region.osm.pbf your-feed.gtfs.zip
sudo -u motis ./motis import
sudo systemctl restart motis
Use a valid, current timetable: a feed whose service calendar does not cover upcoming dates will contain no trips to plan. Size the VM to the region, as larger areas need more memory and disk to import and serve.
Server Components
| Component | Version | Path |
|---|---|---|
| MOTIS engine (binary + web UI + tile profiles) | 2.11.1 | /var/lib/motis/motis |
| nginx reverse proxy | distribution | /etc/nginx/sites-available/cloudimg-motis |
| MOTIS config | generated | /var/lib/motis/config.yml |
| Imported dataset | generated | /var/lib/motis/data/ |
Filesystem Layout
| Path | Description |
|---|---|
| / | Root filesystem |
| /var/lib/motis | MOTIS home: binary, bundled UI, config, OSM extract, GTFS feed and imported data/ folder |
| /var/lib/motis/DATA-LICENSES.txt | OpenStreetMap (ODbL) and timetable (CC0) attribution |
| /root/motis-info.txt | Per VM instance info written at first boot |
| /var/lib/cloudimg/motis-firstboot.done | First boot bootstrap ready marker |
Managing the service
systemctl status motis --no-pager
sudo systemctl restart motis
journalctl -u motis -n 50 --no-pager
Security
MOTIS is deployed here as a public by design, read only routing service. Confirm the posture on your VM:
- The engine runs as the non root
motisservice account with no shell and no sudo. - It binds
127.0.0.1:8080only; the sole public surface is nginx on port 80, with version disclosure suppressed. - There is no login, administrator account, token or mutating endpoint; only routing, geocoding, map and health GET endpoints are served.
- The engine unit is gated on the first boot bootstrap ready marker, so it will not serve before first boot completes.
Restrict inbound 80/tcp to the networks that should reach the planner, and place your own TLS terminator in front for HTTPS. Keep 22/tcp limited to your management network.
Troubleshooting
The web UI does not load. Check both services with systemctl is-active motis nginx. If motis is not active, inspect journalctl -u motis -n 100 --no-pager. The engine only starts once the first boot marker exists at /var/lib/cloudimg/motis-firstboot.done.
A routing query returns no itineraries. The bundled feed covers a wide calendar, but a feed you import yourself must have service dates that include the time you query. Verify with the geocode endpoint that your start and destination resolve to stops or coordinates inside the imported region.
Health returns HTTP 400. MOTIS reports 400 until it has completed a full first cycle over every configured feed. With the static sample feed and no realtime sources it settles at 200 shortly after start.
Support
For assistance, contact cloudimg support:
- Email: support@cloudimg.co.uk
- Response Time: 24/7 with a guaranteed 24 hour response SLA
- Website: https://cloudimg.co.uk