Install Tournevia
Local setup, source kits and a Docker server with HTTPS.
Choose an installation
| Use | Installation |
|---|---|
| Evaluate on your computer | Docker, local access on localhost |
| Team on a VPS or private server | Docker, HTTPS domain and named accounts |
| Managed hosting | Future offering; Community requires no hosting subscription |
The kit targets Linux AMD64 and requires Python 3.11+, Docker and Compose 2.24.4+.
Windows, macOS and Linux
- Windows: Docker Desktop with Linux containers, PowerShell and
python. - macOS: Docker Desktop and
python3. Apple Silicon may need AMD64 emulation. A complete macOS installation has not been validated. - Linux: Docker Engine with the Compose plugin and
python3. This is also the installation path for a Linux VPS.
Follow the official Docker Desktop or Docker Engine instructions. Their terms are separate from Tournevia.
1. Obtain and verify a kit
Obtain the candidate ZIP and its SHA-256 checksum from your installation operator. A supported public download is not yet available. Verify its origin and checksum, then extract it into a new folder, for example tournevia. Keep the kit: its scripts are also used for maintenance.
If you maintain the application source, see Build a candidate kit.
From that directory:
python scripts/release.py verify
python scripts/release.py buildOn macOS/Linux, use python3. The build prints the exact image name to use.
2. Prepare routing
The displayed basemap does not calculate journeys. Valhalla needs a graph covering your service area, reachable from the worker container. VROOM is included in the application image.
Prepare the Île-de-France graph from the kit directory. The kit pins the Valhalla image and the extract’s SHA-256 in deploy/engines.json. This sequence was exercised in PowerShell on Docker Desktop; on Linux, run the same Docker commands from a shell with python3.
$enginePins = Get-Content deploy/engines.json | ConvertFrom-Json
if (-not (Test-Path deploy/runtime/ile-de-france.osm.pbf)) {
Invoke-WebRequest $enginePins.region_source -OutFile deploy/runtime/ile-de-france.osm.pbf
}
docker pull $enginePins.valhalla_image
docker run --rm --memory 6g --cpus 2 --entrypoint python3 -v "${PWD}/deploy/runtime:/data" -v "${PWD}/deploy:/workspace:ro" $enginePins.valhalla_image /workspace/build_graph.py region
python deploy/configure_matrix.py deploy/runtime/region/valhalla.json deploy/runtime/regional-matrix.json
docker run -d --restart unless-stopped --name france-vrp-valhalla --memory 2g --cpus 2 -p 127.0.0.1:8002:8002 -v "${PWD}/deploy/runtime:/data:ro" --entrypoint valhalla_service $enginePins.valhalla_image /data/regional-matrix.json 2- The first run downloads the Île-de-France extract once; an existing file is kept.
build_graph.py regionbuilds the tiles. Allow several minutes and several GB; it refuses an extract whose hash differs from the pinned one unless you pass--accept-new-extract, and it never replaces existing tiles.configure_matrix.pywrites the matrix settings the application expects. It refuses to overwrite an existing output.- The last command starts the router on loopback port 8002. Later, use
docker start france-vrp-valhallaanddocker stop france-vrp-valhalla.
To update the graph, stop all calculations, build in a new runtime directory, switch the mount and test again; never change tiles under a running calculation. Graph preparation needs more resources than the application: see Requirements and sizing.
For an installation that runs Compose directly, the kit’s routing overlay deploy/docker-compose.routing.yml adds the router to the stack on an internal network, with no published port and a health check. ROUTING_CONFIG, ROUTING_THREADS and ROUTING_MEMORY select its configuration and resources; see the configuration reference.
Keep the actual extract date and hash. Set VALHALLA_URL and ROUTING_DATASET in the installation's private environment file. Keep routing private. Basemap coverage does not prove routing coverage.
All of France
The Île-de-France graph is the one the tests and benchmarks run on. To route elsewhere in metropolitan France, build the pinned France extract instead (extracts.france in deploy/engines.json, 5.1 GB, data of 18 October 2026):
$enginePins = Get-Content deploy/engines.json | ConvertFrom-Json
Invoke-WebRequest $enginePins.extracts.france.source -OutFile deploy/runtime/france.osm.pbf
docker volume create tournevia-france
docker run --rm -v tournevia-france:/data -v "${PWD}/deploy/runtime:/src:ro" alpine:3.20 cp /src/france.osm.pbf /data/
docker run --rm --memory 16g --cpus 8 --entrypoint python3 -v tournevia-france:/data -v "${PWD}/deploy:/workspace:ro" $enginePins.valhalla_image /workspace/build_graph.py region --extract france --jobs 8
docker run -d --restart unless-stopped --name france-vrp-valhalla-france --memory 8g --cpus 4 -p 127.0.0.1:8002:8002 -v tournevia-france:/data:ro --entrypoint valhalla_service $enginePins.valhalla_image /data/france/valhalla.json 4- Build inside a Docker volume on Windows and macOS: through a bind mount from the host, the build's sorting was too slow to finish. On a Linux host a bind mount is fine.
--jobssets the build threads.--max-matrix-km(default 200) is the longest straight-line distance between two points of one calculation, the depot included.- Set
MATRIX_MAX_KMto the same value. A calculation whose points are farther apart is then refused before it is queued, naming the two farthest places, instead of failing in the router.
The build refuses an extract whose SHA-256 differs from the pin unless you pass --accept-new-extract; after a refresh, rerun the checks and update the pin. Timing, memory and disk are in Requirements and sizing.
Address lookup (optional)
With address lookup, address fields suggest French addresses while typing, the chosen address places the pin, and CSV import proposes coordinates for rows without them; every proposal still needs entrance review. Without it, each point is placed with a pin on the map or, as an exception, by typing its coordinates.
There are two ways to turn it on:
- IGN's public service, chosen by an administrator, with no server setting or account: they tick it during first-use setup or in Settings → Address search. IGN's Géoplateforme then receives the text of every looked-up address and of every imported row without coordinates, with the depot's position. It stays off for each company until an administrator chooses it.
- The installation's own geocoder, set in
GEOCODER_URL, so that addresses never leave the server. Every company then uses it.
The kit's overlay deploy/docker-compose.geocoder.yml runs addok with the Base Adresse Nationale on an internal network that only the API joins, with no published port and no request log. The kit documents it for a Compose installation configured in .env:
New-Item -ItemType Directory -Force deploy/runtime/geocoder | Out-Null
Invoke-WebRequest https://adresse.data.gouv.fr/data/ban/adresses/latest/addok/addok-france-bundle.zip -OutFile deploy/runtime/geocoder/addok-france-bundle.zip
Expand-Archive deploy/runtime/geocoder/addok-france-bundle.zip deploy/runtime/geocoder/addok-data
docker compose --env-file .env -f deploy/docker-compose.example.yml -f deploy/docker-compose.geocoder.yml up -dThe bundle is about 2.5 GB to download and 3.2 GB unpacked, and addok needs at least 6 GB of memory; loading the index takes one to two minutes after each start. Set GEOCODER_DATASET to the source and date of the bundle: the interface shows it next to the results, as the Licence Ouverte 2.0 requires. See the configuration reference.
3. Start on your computer
Use the command printed by the kit build, with its exact image:
python scripts/start_local.py --image KIT_IMAGE --port 8007 --directory PRIVATE_PATHReplace the uppercase values. The private directory can be under your Windows user directory or ~/tournevia-data on macOS/Linux.
The launcher generates secrets, configuration and a private first-use link. Only the application is exposed on loopback. Starting the same installation again preserves its data and chosen image.
The initial Valhalla address is http://host.docker.internal:8002, for a service on the Docker Desktop host. On Linux, configure a reachable private address in workspace.env, then rerun the same launcher. Also set the actual ROUTING_DATASET description. Calculations cannot succeed until routing is configured.
Open the printed private link and set up the company as in step 5. Afterwards use http://localhost:8007.
4. Install on a VPS with HTTPS
If your server already runs Coolify, Dokploy or another HTTPS proxy on ports 80 and 443, follow Install on a VPS, step by step instead: Tournevia then runs behind that proxy, which also obtains the certificate. That path was run on a real VPS. The steps below use the bundled HTTPS proxy with your own certificate.
Prepare a domain pointing to the server, a valid certificate, a private installation directory and a privately reachable Valhalla service. The certificate directory needs fullchain.pem and privkey.pem, readable by the container proxy (UID 101), without making the key world-readable.
Build the kit on the server or transfer the built image through your trusted distribution process. From the kit, run:
python3 scripts/start_server.py --directory /srv/tournevia/private --origin https://planner.example.com --certificates /srv/tournevia/certs --valhalla-url http://routing.internal:8002 --dataset "Your extract, date and speed assumptions" --image KIT_IMAGE --prepare-onlyReplace the domain, paths, routing URL, dataset label and image. This first command validates configuration without starting services. Run it again without --prepare-only to start.
The launcher enables shared mode and exposes only HTTPS on port 443. PostgreSQL and the API stay private. Port 80 is not served, so an http:// address does not redirect: share the https:// address. The launcher does not purchase a VPS, configure DNS or issue a certificate.
Then verify:
- The browser trusts the certificate without warnings.
- The private link opens company setup, as in step 5; dispatchers never receive this link.
- A named user can sign in, save data and retrieve it.
- A calculation uses the intended graph and produces a reviewable result.
- Backup and recovery have been exercised before operational use.
A small VPS may run the application without being suitable for graph building or 250-delivery calculations. Measure your workload; routing can run on another private machine.
5. Set up the company
The private link the launcher writes to private-setup.url carries the SETUP_TOKEN code. It opens first-use setup while the installation has no company and no account. Without the link, enter the code on the second step, under Use a setup code instead. Without SETUP_TOKEN, setup stays locked. There is no default password and no public sign-up.
Setup has two steps:
- Company & depot. The company name, the depot name and its address. With the installation's geocoder, or after ticking Find addresses with the IGN address service (France), the address field suggests addresses and the chosen address places the pin. Move the pin to the vehicle entrance, then tick I reviewed this vehicle entrance on the map: the workspace is not created without a reviewed entrance. Coordinates can also be typed under Enter coordinates. The IGN choice is kept for the company.
- Administrator. The first administrator's name, username and password (15 to 128 characters).
The first administrator keeps the language chosen on the setup screen; anyone can change it later in their personal preferences.
A short guide then helps the administrator add vehicles, choose the delivery number format and import or add the first deliveries, or try with example deliveries; every step can be skipped. Examples are fictional records marked Example, planned only with other examples, whose route sheets and export say they are not for driving; an administrator removes them together in Settings → Example data. See Getting started.
After installation
Prepare backups using the operations guide. Never expose local mode publicly; shared team access requires HTTPS.
In local mode, open the address the launcher prints, with localhost or 127.0.0.1. An IPv6 loopback origin such as http://[::1]:8007 is refused at startup, because the API is published on IPv4 loopback only. All settings are listed in the configuration reference.