Back to the blog

Routing server (GraphHopper): installation, data import and deployment

This article explains how to set up a routing server (computing a route between two points) from OpenStreetMap data, using GraphHopper. The goal is an HTTP API (e.g. /route) that a web app, a mobile app or a back end can call.

We will cover:

  • the available tools (SaaS vs self-hosted),

  • installing GraphHopper,

  • downloading/merging the OSM data,

  • configuration (profiles, CH/LM speed-ups, elevation),

  • import, deployment and exposure through Nginx + systemd,

  • testing and performance notes.

 

Tools and alternatives

Before self-hosting, here are two popular solutions:

To try the GraphHopper API quickly (explorer):

 

Useful tutorials and docs

 

1) Installing GraphHopper

Prerequisite: Java (JRE + JDK)

GraphHopper is a Java application. It needs a full Java environment (Java 17 or later for GraphHopper 11).

Install the JDK (it includes what is needed to run and compile):

sudo apt install -y default-jdk

Check the Java and compiler versions:

java -version
javac -version

Downloading GraphHopper

We fetch:

  • the “web” jar (HTTP server),

  • an example configuration file.

wget https://github.com/graphhopper/graphhopper/releases/download/11.0/graphhopper-web-11.0.jar
wget https://raw.githubusercontent.com/graphhopper/graphhopper/11.x/config-example.yml

 

Plan your own route

MapPlanner helps you calculate distances, elevation, duration and plan your hikes, runs and bike rides on an interactive map.

  • Automatic distance & elevation calculation
  • Topographic, satellite maps
  • GPX/KML export for your GPS
  • Real-time GPS tracking

2) Downloading the OpenStreetMap (OSM) data

GraphHopper computes routes from OSM data in PBF format (.osm.pbf).

Download by area (e.g. Europe, France):

 

Several files: merging PBFs

GraphHopper does not take several PBF files as input. To cover several regions (e.g. Europe + North America), you have to merge the PBFs into one.

Important:

  • ideally, avoid overlapping areas, or you risk inconsistencies.

Install osmium:

sudo apt-get install osmium-tool

Merge:

osmium merge europe-latest.osm.pbf north-america-latest.osm.pbf -o map.osm.pbf

Then point the GraphHopper configuration to map.osm.pbf.

 

3) Configuring GraphHopper

GraphHopper is driven by a YAML file.

Copy the example file:

cp config-example.yml config.yml

Routing profiles (car, foot, hiking, …)

Profiles define the behavior: car, walking, hiking, cycling…

Profile docs:

 

Custom models

Custom models let you tune the rules (road types, preferences, penalties…).

 

4) Speed-ups: CH vs LM vs “default”

GraphHopper can precompute speed-up structures during the import.

Two modes:

  • profiles_ch: fastest at query time, but expensive in RAM and import time.

  • profiles_lm: more flexible, allows partial customization at query time.

Key points (practical summary):

  • CH:

    • very fast queries,

    • longer import (often ×10),

    • more RAM,

    • less flexible (no profile customization per request).

  • LM:

    • a good trade-off,

    • allows requests with parameters (dynamic custom model),

    • slower than CH but more flexible.

If CH is enabled, disabling turn costs is recommended:

  • turn_costs: false
    (greatly reduces the generation time of a profile, and speeds up queries)

Note: adding several similar profiles usually adds little RAM (profiles overlap), but each LM profile can take a lot of disk space.

 

5) Adding elevation

GraphHopper can enrich the graph with altitude data.

Docs:

Available modes (+ a hidden one):

  • srtm: 3.1 GB compressed on disk, very imprecise data

  • cgiar: 3.2 GB compressed on disk, approximate data but with fewer inconsistencies than srtm

  • gmted: not tested

  • multi: not tested

  • SRTMGL1: URL not reachable, but precise data

How it works:

  • you set a mode (e.g. srtm) in the config,

  • GraphHopper automatically downloads the elevation tiles needed for the covered area.

 

6) Importing the data into GraphHopper

The import step converts the OSM data into an optimized graph the server can use.

Import command (with dedicated memory):

java -Xms62g -Xmx62g -jar *.jar import config.yml

Why import?

  • better optimized for the conversion,

  • frees the memory once done,

  • does not start the server.

Generated data:

  • a graph-data directory holds the cache and the imported data.

Important:

  • if you change the profiles or update the PBF, you usually have to delete graph-data before importing again.

 

Observed import times

  • Europe: 45 min and 38 GB of dedicated RAM, 12 GB on disk (no speed-up profiles, Ryzen 3800X)

  • Europe and North America: 92 min and 58 GB of RAM without speed-up profiles, 27.2 GB on disk (5 profiles: car, foot, hike, bike and mtb)

  • Europe and North America: 12 h and 62 GB of RAM with one LM speed-up profile (with a single LM: 9 h 10 and 46 GB), 60 GB on disk (each LM profile takes 14 GB)

  • Europe and North America: 7 h 50 with elevation and no speed-up profile, 30.2 GB

  • Europe and North America: 7 h with cgiar elevation, no speed-up profile and 2 extra profiles, 30.2 GB (the extra profiles changed neither the final size nor the generation time)

 

7) Updating the database

Check the GraphHopper releases:

If needed, update the config and the profiles:

Run the update: download the new PBF, delete the old graph, then run the step 6 import again:

rm -rf graph-data
java -Xms62g -Xmx62g -jar *.jar import config.yml

Update, then restart on the server:

nano config.yml
nano custom_models/*

sudo nano /etc/systemd/system/routing.service
sudo systemctl daemon-reload
sudo systemctl restart routing.service

Adjust the Xmx memory if needed (e.g. -Xmx28g).

 

8) Deploying GraphHopper (HTTP server)

Deployment docs:

Starting the server (Europe)

Recommended minimum (without CH/LM):

java -Xmx18g -jar *.jar server config.yml

Europe + North America (example)

/usr/bin/java -Xmx32g -XX:+UseZGC -jar graphhopper-web-11.0.jar server config.yml

Note:

  • *.jar comes in handy when updating (replacing the jar).

 

9) Requests with custom parameters (dynamic custom model)

If LM is enabled for a profile, you can send a POST request to /route to adjust parameters.

Key point:

  • you must set ch.disable=true to force LM.

Use cases:

  • choosing “fastest” vs “shortest”,

  • excluding some road types.

Caution:

  • the weights/values you send can only be higher than the initial configuration.

Simple rule for distance_influence:

  • “fastest”: the lowest possible value (1 = smallest)

  • “shortest”: the highest value (100 or more)

 

10) Memory optimization: MMAP (don’t load everything into RAM)

By default, GraphHopper may keep a lot in memory. To lower RAM pressure, you can use memory-mapped file access.

In the config:

graph.dataaccess.default_type: MMAP

Instead of RAM_STORE.

Effects:

  • uses ~50% of the RAM set with -Xmx,

  • faster startup,

  • slower queries.

Also:

  • once imported, the PBF file is no longer needed.

 

11) Exposing the API through Nginx (reverse proxy + CORS)

Example Nginx config (proxy to port 8989), with your own domain name:

server {
    listen               80;
    listen               [::]:80;

    server_name          routing.example.com;

    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;

    server_name routing.example.com;

    include snippets/ssl.conf;

    access_log  /var/log/nginx/routing_access.log;
    error_log   /var/log/nginx/routing_error.log;

    root /home/www/graphhopper;

    location / {
        proxy_pass         http://127.0.0.1:8989;
        proxy_redirect     off;
        proxy_set_header   Host              $host;
        proxy_set_header   X-Real-IP         $remote_addr;
        proxy_set_header   X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header   X-Forwarded-Proto $scheme;
        proxy_set_header   X-Forwarded-Protocol $scheme;
        proxy_set_header   X-Forwarded-Host  $http_host;

        # CORS: an API open to every origin, without credentials
        proxy_hide_header 'Access-Control-Allow-Origin';
        add_header    'Access-Control-Allow-Origin' '*' always;
        add_header    'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
        add_header    'Access-Control-Allow-Headers' 'X-Requested-With, Content-Type, Origin, Authorization, Accept, Client-Security-Token, Accept-Encoding' always;
    }

    # Disable access to front page
    location = / {
        deny all;
    }
}

To restrict the API to your own site, replace '*' with its origin (e.g. 'https://www.example.com').

Then open the domain set in the configuration to test and validate.

 

12) Creating a systemd service (automatic startup)

Create the service:

sudo nano /etc/systemd/system/routing.service

Content:

[Unit]
Description=Graphhopper Routing
After=network.target

[Service]
Type=simple

User=deploy
Group=deploy

WorkingDirectory=/home/www/graphhopper
ExecStart=/usr/bin/java -Xmx16g -jar graphhopper-web-11.0.jar server config.yml

# if we crash, restart
TimeoutSec=30
Restart=on-failure
RestartSec=5s

TimeoutStartSec=120
TimeoutStopSec=30

# output goes to the journal
StandardOutput=journal
StandardError=journal

SyslogIdentifier=routing

[Install]
WantedBy=multi-user.target

Enable and start it:

sudo systemctl daemon-reload
sudo systemctl enable routing.service
sudo systemctl start routing.service

 

13) Quick tests

Local test:

curl "http://localhost:8989/route?profile=foot&point=52.5300591%2C13.3565022&point=52.5060440%2C13.4378107"

 

14) GraphHopper API reference

Official web API docs:

https://github.com/graphhopper/graphhopper/blob/master/docs/web/api-doc.md

Related articles

What our Explorer users say

⭐⭐⭐⭐⭐

I planned a 5-mile running loop around my neighborhood in minutes. The distance and pace estimate were spot on for my morning run before work.

Sarah K. New runner
⭐⭐⭐⭐⭐

With the isochrone mode, I plan my hikes in no time! I instantly see how far I can go with my available time. GPS tracking keeps me on trail in the Rockies.

Jessica W. Passionate hiker
⭐⭐⭐⭐⭐

We prepared a 25-mile cycling loop with friends and exported the GPX track. Everyone followed the same route on their Garmin without getting lost. Perfect organization!

Mike R. Weekend cyclist
⭐⭐⭐⭐⭐

Before a hike in Shenandoah, I checked the elevation and duration. The route followed real trails, which saved me from unpleasant surprises. Very reliable!

Emily L. Beginner hiker
⭐⭐⭐⭐⭐

I wanted an easy walk for my kids. I set a short max distance and MapPlanner helped me create the perfect loop. We had a wonderful family afternoon.

David N. Family outing
⭐⭐⭐⭐⭐

I love planning my outings in advance. I saved several routes for the weekends. It saves me so much time compared to figuring it out on the go.

Karen G. Occasional hiker
⭐⭐⭐⭐⭐

By exploring the routes around my area, I discovered great trails I didn’t know about. MapPlanner opened new places for me to explore right in my backyard.

Chris P. Trail discovery
⭐⭐⭐⭐⭐

I keep track of my progress with the history feature. Comparing my running pace and distance over the months keeps me motivated and accountable.

Ashley T. Performance tracking
⭐⭐⭐⭐⭐

For our MTB group ride, we created the route and sent the GPX to everyone. Nobody got separated — the whole group stayed together from start to finish.

Brian B. Club activity
⭐⭐⭐⭐⭐

I love taking photos while hiking. MapPlanner automatically places my pictures on the map. It’s awesome to review the route and share it with friends.

Lauren F. Discovery outing
⭐⭐⭐⭐⭐

Depending on the activity, I switch the map style: topo for the mountains, street for the city, satellite to scout terrain. It makes route planning super intuitive.

Tyler S. Experienced hiker
⭐⭐⭐⭐⭐

MapPlanner now estimates my times based on my own pace. I know exactly how long my next run will take. Perfect for squeezing in a workout on a tight schedule.

Rachel J. Aspiring runner

Popular routes

Discover the most popular routes near you

Miles at the Meadow - 5k / 10K (2 loops)
pete.augrom
Running
Very easy

Miles at the Meadow - 5k / 10K (2 loops)

3.095mi
•
33m
•
5.63mph
Virginia - The Plains
Fusion Invitational High School 5K Course
nm8zkrv8hk
Running
Easy

Fusion Invitational High School 5K Course

3.201mi
•
34m
•
5.65mph
Colorado - Commerce City
ada
rajan
Running
Strenuous

ada

11.669mi
•
4h10m
•
2.8mph
New York - New York
Route 2 - Prayer Walk Connecting Churches (Ferguson)
SR St. Louis
Walk
Very easy

Route 2 - Prayer Walk Connecting Churches (Ferguson)

1.441mi
•
31m
Missouri - Ferguson
2026 HHHH 25mi Route
buschlive
Bike
Moderate

2026 HHHH 25mi Route

24.79mi
•
2h30m
•
9.92mph
Texas - Hutchins
Central park
Fibula
Running
Moderate

Central park

6.393mi
•
1h
•
6.39mph
New York - New York
Miles at the Meadow - 1 mile
pete.augrom
Running
Very easy

Miles at the Meadow - 1 mile

1.012mi
•
11m
•
5.52mph
Virginia - The Plains
Chincoteague National Wildlife Refuge
Gaynor
Bike
Very easy

Chincoteague National Wildlife Refuge

9.532mi
•
2h
•
4.77mph
Virginia - Chincoteague Island