Tournevia

Install on a VPS, step by step

Deploy Tournevia on a Linux VPS behind Coolify or another HTTPS proxy, with each step as it was run on a real server.

This guide installs Tournevia on a Linux VPS whose ports 80 and 443 already belong to an HTTPS reverse proxy that obtains certificates, such as the Traefik that Coolify installs. Tournevia runs as its own Docker project next to the other services; the proxy routes your hostname to it and handles the certificate.

Every step below was run on 20 October 2026 on a 2 vCPU / 8 GB Ubuntu 24.04 VPS that also runs Coolify 4 and about a dozen other containers. The times given are from that server. Replace planner.example.com with your hostname.

If nothing on your server holds ports 80 and 443, use the bundled HTTPS proxy with your own certificate instead: see Install Tournevia.

What you need

  • A Linux AMD64 server with Docker Engine and Compose 2.24.4 or newer, and a root shell. Check with docker compose version.
  • Enough resources for your day. 2 vCPU / 8 GB suits up to about 100 deliveries a day in one city; see Requirements and sizing. Allow about 2 GB of disk for the images, the road data and the graph.
  • An HTTPS proxy that discovers Docker containers. With Coolify, its proxy is Traefik on the Docker network coolify, with a certificate resolver named letsencrypt. docker network ls shows the network.
  • A hostname whose DNS A record points to the server. getent hosts planner.example.com must print the server's address.

1. Get the source

mkdir -p /srv/tournevia
git clone https://github.com/HBlackfoxx/tournevia.git /srv/tournevia/kit
cd /srv/tournevia/kit

This clones the Tournevia repository. A verified kit, extracted into /srv/tournevia/kit, works the same way: see obtain and verify a kit. On the test server, the source was copied as an archive of the commit, because the repository was not yet published. The commands below run from /srv/tournevia/kit.

2. Build the images

docker build -f deploy/Dockerfile.vroom -t france-vrp-vroom:1.14.0 .
docker build --target runtime -t tournevia-app:1 .

The first command builds the route solver, the second the application. Choose a tag you will recognise, here tournevia-app:1. On the test server they took 1 min 55 s and 33 s, with both CPUs busy; the other services kept running.

3. Build the road graph

mkdir -p deploy/runtime
curl -fL -o deploy/runtime/ile-de-france.osm.pbf https://download.geofabrik.de/europe/france/ile-de-france-latest.osm.pbf
sha256sum deploy/runtime/ile-de-france.osm.pbf
VALHALLA=$(python3 -c "import json; print(json.load(open('deploy/engines.json'))['valhalla_image'])")
docker run --rm --memory 3g --memory-swap 3g --cpus 2 --entrypoint python3 \
  -v "$PWD/deploy/runtime:/data" -v "$PWD/deploy:/workspace:ro" "$VALHALLA" \
  /workspace/build_graph.py region --accept-new-extract
  1. The Île-de-France extract is about 340 MB; at Geofabrik's download rate it took 5 min 49 s.
  2. Note the date and the SHA-256 that sha256sum prints: they go into the dataset label in step 4.
  3. The build took 2 min 16 s. The --memory 3g and --cpus 2 caps keep it from taking memory the other services need; an Île-de-France build stays under 3 GB.
  4. --accept-new-extract is needed because the kit's tests and benchmarks ran on the extract of 22 September 2026, which Geofabrik no longer publishes. With a newer extract, check the results in step 7 before relying on them.

The graph is written to deploy/runtime/region (330 MB). For all of France, see All of France: it needs a larger server.

4. Prepare the installation

python3 scripts/start_server.py --directory /srv/tournevia/private \
  --origin https://planner.example.com --behind-proxy coolify \
  --routing-config region/valhalla.json \
  --dataset "Geofabrik Ile-de-France downloaded 2026-10-20 (sha256 314d51020bf1); static OSM speeds" \
  --image tournevia-app:1 --prepare-only

Use your hostname, your extract's date and hash, and your image tag. --behind-proxy coolify names the proxy's Docker network; --routing-config runs the router inside the installation, on a private network. This command starts nothing. It creates /srv/tournevia/private, readable by root only, with the generated secrets in workspace.env, the private first-use link in private-setup.url, and checks the merged configuration.

On a server shared with other services, lower the router's memory cap from 4 GB to 3 GB:

sed -i 's/^ROUTING_MEMORY=.*/ROUTING_MEMORY=3g/' /srv/tournevia/private/workspace.env

workspace.env also holds the Traefik names: PROXY_HTTPS_ENTRYPOINT, PROXY_HTTP_ENTRYPOINT and PROXY_CERT_RESOLVER are Coolify's https, http and letsencrypt. For Dokploy, the network is dokploy-network and the entrypoints are websecure and web. Another proxy forwards your hostname to http://edge:8080 on the shared network; it must pass the visitor's address in X-Forwarded-For, as Traefik and Caddy do. All settings are in the configuration reference.

5. Start

Run the same command without --prepare-only:

python3 scripts/start_server.py --directory /srv/tournevia/private \
  --origin https://planner.example.com --behind-proxy coolify \
  --routing-config region/valhalla.json \
  --dataset "Geofabrik Ile-de-France downloaded 2026-10-20 (sha256 314d51020bf1); static OSM speeds" \
  --image tournevia-app:1
docker ps --format '{{.Names}} {{.Status}}' | grep tournevia-server

It started in 25 s on the test server. Five containers run: the database, the router, the API, the worker and the edge web server. None publishes a port on the host: only edge joins the proxy's network. On the test server, the proxy had obtained the certificate for the hostname within a minute of the start.

6. Create the company

cat /srv/tournevia/private/private-setup.url

Open the address on the URL= line in your browser. It opens first-use setup in two steps: the company and its depot, found by its address and then reviewed on the map, then the administrator account; see Install Tournevia. The link works only while the installation is empty; never send it to dispatchers.

A short guide then offers to add vehicles, the delivery number format and the first deliveries, or to try with fictional example deliveries, which an administrator later removes in Settings → Example data; see Getting started. The test server holds fictional data only and serves as a public demonstration.

7. Check before use

  1. The browser shows the site without a certificate warning, and http://planner.example.com redirects to https://.
  2. You can sign in, save a change and find it again.
  3. A calculation produces a reviewable result. On the test server, the ten fictional example deliveries were calculated in 3.3 to 4.6 s, with validation passed and road geometry on every leg.
  4. The other sites on the server still answer.
  5. free -m leaves room for the other services. On the test server, at least 5.6 GB stayed available during the first calculation; after three calculations the router held 0.9 GB, Tournevia about 1.1 GB in all, and 4.9 GB stayed available.

8. Back up

The backup tool runs on the server with the kit's Python packages. Install them once:

apt install python3-venv
python3 -m venv /srv/tournevia/venv
/srv/tournevia/venv/bin/pip install -r /srv/tournevia/kit/backend/requirements.txt

If apt refuses because an earlier package installation was interrupted, finish that first with dpkg --configure -a, at a quiet moment: it can restart system services. The test server was in that state; rather than change system packages during the test, python3 -m venv --without-pip /srv/tournevia/venv followed by pip's official get-pip.py script gave the same environment there.

Then, from /srv/tournevia/kit:

/srv/tournevia/venv/bin/python scripts/database_backup.py --workspace /srv/tournevia/private backup --output /srv/tournevia/private/backups/$(date -u +%Y%m%dT%H%M%SZ).dump
/srv/tournevia/venv/bin/python scripts/database_backup.py --workspace /srv/tournevia/private restore /srv/tournevia/private/backups/FILE.dump

The backup took 1.6 s with the fictional data. The restore check loads it into a separate database and prints what it contains, without touching the live data. Copy the backups off the server: a backup kept only on the VPS is lost with the VPS. Encryption, scheduling and pruning are in Backup and restore.

9. Coolify's Docker cleanup

Coolify's Docker cleanup (forced nightly on the test server, otherwise when disk use passes its threshold) deletes unused Docker images it did not create and clears the build cache. Running Tournevia containers keep their images. The solver image from step 2 and any previous application image are deleted: before an update, save the current image as in step 10. If Coolify's server settings also delete unused volumes, never stop Tournevia with docker compose down, which would leave its database volume unused; the launcher and docker compose stop keep it.

10. Update to a new version

Build the new version from a separate copy, save the current image, upgrade with the installed kit, then update the kit:

git clone --branch NEW_VERSION https://github.com/HBlackfoxx/tournevia.git /srv/tournevia/build
cd /srv/tournevia/build
docker image inspect france-vrp-vroom:1.14.0 >/dev/null 2>&1 || docker build -f deploy/Dockerfile.vroom -t france-vrp-vroom:1.14.0 .
docker build --target runtime -t tournevia-app:2 .
docker save -o /srv/tournevia/private/backups/tournevia-app-1.tar tournevia-app:1
cd /srv/tournevia/kit
/srv/tournevia/venv/bin/python scripts/start_server.py --directory /srv/tournevia/private --upgrade-image tournevia-app:2
git -C /srv/tournevia/kit fetch && git -C /srv/tournevia/kit checkout NEW_VERSION
rm -rf /srv/tournevia/build

The upgrade backs up the database first; if the backup fails, nothing changes. It took 19 s on the test server, including the backup, and the saved plans were all still there afterwards. Later starts use the new image tag with --image. To go back, see Upgrade: docker load -i restores the saved image.

Address lookup on a small server

The self-hosted address index needs about 6 GB of memory, which does not fit next to other services on an 8 GB server. Tournevia can instead query IGN's public Base Adresse Nationale service, with no account. Without changing anything on the server, an administrator turns it on for their company during first-use setup or in Settings → Address search. To force it for every company on the installation, add two lines to workspace.env instead, then rerun the start command of step 5:

printf 'GEOCODER_URL=https://data.geopf.fr/geocodage\nGEOCODER_DATASET=Base Adresse Nationale via IGN Géoplateforme (Licence Ouverte 2.0)\n' >> /srv/tournevia/private/workspace.env

Every looked-up address then goes to IGN, including those of CSV rows without coordinates: decide this with whoever owns the delivery data. The test server, a demonstration with fictional data, uses it; lookups through the live site returned the expected house numbers. To turn it off, untick the option in Settings → Address search or, if you forced it, delete the two lines and rerun the start command.

What was not tested on this server

A 250-delivery day, the France graph and the self-hosted address index were not run on the 2 vCPU / 8 GB VPS; their measured requirements are in Requirements and sizing. The rollback to a previous version and the bundled-certificate path were not run on a VPS either.

On this page