Deployment guide

MOTIS

Overview

MOTIS is an open-source multimodal journey planning engine that powers large public planners such as Transitous. It imports OpenStreetMap street data and 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.

MOTIS computes door-to-door itineraries that combine walking and public transport. Each result shows departures, arrivals, transfers and a drawn route on the map.

This SecureAnyCloud image runs the MOTIS server as a single self-contained native binary that serves both the REST API and the bundled web app. It sits 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 nginx is the only public surface.

MOTIS is a public, read-only service by design. 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 VM, a one-shot service resolves the VM's public address, writes an instance info file to /root/motis-info.txt, records the bootstrap ready marker, and starts the engine and nginx. The engine unit is gated on that marker, so it never serves before first boot has completed.

What you get:

  • 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 :80 in 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.service and nginx.service as enabled and active systemd units, plus a one-shot motis-firstboot.service that runs once per VM
  • The engine running as a dedicated non-root motis service 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 SecureAnyCloud 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 on loopback port 8080.

Prerequisites

You need an active Azure subscription, an SSH key pair, and a VNet with a subnet in the target region.

Standard_B2s (2 vCPU / 4 GiB RAM) is a good starting size 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.

Configure the network security group (NSG) to allow inbound 22/tcp from your management network and 80/tcp from the networks your users will reach the planner on. Also allow 443/tcp once you place your own TLS terminator in front.

Step 1 - Deploy from the Azure Marketplace

Sign in to the Azure Portal, choose Create a resource, search the Marketplace for MOTIS by SecureAnyCloud, and select Create.

On the Basics tab, 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).

Select Review + create, then Create. First-boot initialisation takes a few seconds after the VM starts.

Step 2 - Deploy from the Azure CLI

# Accept the Marketplace terms once per subscription (required before the first CLI deploy)
az vm image terms accept --urn sac1647283583153:motis-ubuntu-24-04:default:latest
az vm create \
  --resource-group <your-rg> \
  --name motis \
  --image sac1647283583153:motis-ubuntu-24-04:default:latest \
  --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

Connect to the VM over SSH:

ssh azureuser@<vm-public-ip>

To look up the public IP at any time, run 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 that both are active, that the engine binds to loopback only, and that nginx serves the public port:

systemctl is-active motis nginx ss -tln | grep -E ':80 |:8080 '

Both services report active. The socket list shows nginx on 0.0.0.0:80 and 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 a destination, or click two points on the map. MOTIS plans the connections and shows the 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 a 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 itineraries with 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 - Check 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. It contains the VM's own reachable URL and a note that the service is read-only and public by design:

sudo cat /root/motis-info.txt

Step 8 - Review the sample dataset and its licenses

The image ships a small demonstration dataset so the planner works out of the box. The VM records the attribution and licensing:

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 SecureAnyCloud 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 contains no trips to plan. Size the VM to the region, because larger areas need more memory and disk to import and serve.

Server components

The installation consists of the following components:

  • MOTIS engine (binary, web UI and tile profiles), version 2.11.1: /var/lib/motis/motis
  • nginx reverse proxy (distribution package): /etc/nginx/sites-available/sac-motis
  • MOTIS config (generated): /var/lib/motis/config.yml
  • Imported dataset (generated): /var/lib/motis/data/

Filesystem layout

These are the key paths on the VM:

  • /: root filesystem
  • /var/lib/motis: MOTIS home, holding the binary, bundled UI, config, OSM extract, GTFS feed and the 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/sac/data/motis-firstboot.done: first-boot bootstrap ready marker

Manage the service

Check the service status, restart MOTIS and read its recent log with these commands:

systemctl status motis --no-pager sudo systemctl restart motis journalctl -u motis -n 50 --no-pager

Security

MOTIS runs here as a public, read-only routing service by design. The posture on your VM is:

  • The engine runs as the non-root motis service account with no shell and no sudo.
  • It binds 127.0.0.1:8080 only. 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 does 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 starts only once the first-boot marker exists at /var/lib/sac/data/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. Use the geocode endpoint to verify 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 SecureAnyCloud support:

  • Email: sales@secureanycloud.com
  • Response time: 24/7, with a 24 hour response SLA
  • Website: https://secureanycloud.com

Deploy on Azure

Launch MOTIS on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.

View this image

Need Help?

Our support team is available 24/7.

sales@secureanycloud.com

Deploy on Azure