Routing server on Debian 11

OSRM (Open Source Routing Machine) is a super fast routing engine for OpenStreetMap (OSM) road networks. In previous tutorials, we explained the process of setting up a self-hosted OpenStreetMap tile server and routing server on Debian 12. This tutorial shows you how to add navigation functionality to your OpenStreetMap on Debian 11.

1. Build OSRM from source

Install dependency packages.

sudo apt-get update
sudo apt-get -y --no-install-recommends install ca-certificates cmake make git gcc g++ libbz2-dev libxml2-dev wget libzip-dev libboost1.74-all-dev lua5.4 liblua5.4-dev -o APT::Install-Suggests=0 -o APT::Install-Recommends=0

NPROC=${BUILD_CONCURRENCY:-$(nproc)}
sudo ldconfig /usr/local/lib
git clone --branch v2021.3.0 --single-branch https://github.com/oneapi-src/oneTBB.git
cd oneTBB
mkdir build
cd build
cmake -DTBB_TEST=OFF -DCMAKE_BUILD_TYPE=Release ..
cmake --build .
sudo cmake --install .

Create the osrm user. (No need to create a password for this user.)

sudo useradd -d /srv/osrm -s /bin/bash -m osrm

Grant permissions to your own user account. Replace username with your real Linux username.

sudo apt install acl
sudo setfacl -R -m u:username:rwx /srv/osrm/

Change to the /srv/osrm/ directory.

cd /srv/osrm/

Download the OSRM source code from its Github repository.

git clone --branch v5.27.1 https://github.com/Project-OSRM/osrm-backend.git

Create the build directory.

mkdir build

Change to this directory and configure the build environment.

cd build
cmake /srv/osrm/osrm-backend/

You may also need to create the swap file:
        sudo fallocate  -l  4G /debianswapfile ; sudo chmod 600 /debianswapfile ; 
        sudo mkswap /debianswapfile && sudo swapon /debianswapfile ; 
        sudo sed -i '$a\/debianswapfile  swap  swap  defaults 0 0' /etc/fstab
        
Compile the source code. (If you see some TBB warning: deprecated message, you can ignore them.)
It takes long time to compile, so you can use screen to keep the process running.

make

[  0%] Building CXX object CMakeFiles/UTIL.dir/src/util/assert.cpp.o
[  0%] Building CXX object CMakeFiles/UTIL.dir/src/util/conditional_restrictions.cpp.o
[  0%] Building CXX object CMakeFiles/UTIL.dir/src/util/coordinate.cpp.o
[  0%] Building CXX object CMakeFiles/UTIL.dir/src/util/coordinate_calculation.cpp.o
[  0%] Building CXX object CMakeFiles/UTIL.dir/src/util/exception.cpp.o
[  4%] Building CXX object CMakeFiles/UTIL.dir/src/util/fingerprint.cpp.o
[  4%] Building CXX object CMakeFiles/UTIL.dir/src/util/geojson_debug_policies.cpp.o
[  4%] Building CXX object CMakeFiles/UTIL.dir/src/util/guidance/bearing_class.cpp.o
[  4%] Building CXX object CMakeFiles/UTIL.dir/src/util/guidance/entry_class.cpp.o
[  4%] Building CXX object CMakeFiles/UTIL.dir/src/util/guidance/turn_lanes.cpp.o
[  4%] Building CXX object CMakeFiles/UTIL.dir/src/util/log.cpp.o
[  8%] Building CXX object CMakeFiles/UTIL.dir/src/util/opening_hours.cpp.o
……
/usr/include/boost/function_output_iterator.hpp:14:1: note: ‘#pragma message: This header is deprecated. 
Use <boost/iterator/function_output_iterator.hpp> instead.’
   14 | BOOST_HEADER_DEPRECATED(“<boost/iterator/function_output_iterator.hpp>”
      | ^~~~~~~~~~~~~~~~~~~~~~~
13% Building CXX object CMakeFiles/EXTRACTOR.dir/src/extractor/extractor.cpp.o
In file included from /usr/include/boost/smart_ptr/detail/sp_thread_sleep.hpp:22,
                                                          yield_k.hpp:23,
                                                          spinlock_gcc_atomic.hpp:14,
                                                          spinlock.hpp:42
……

21% Building CXX object CMakeFiles/EXTRACTOR.dir/src/extractor/raster_source.cpp.o
21% Building CXX object CMakeFiles/EXTRACTOR.dir/src/extractor/restriction_graph.cpp.o
21% Building CXX object CMakeFiles/EXTRACTOR.dir/src/extractor/restriction_parser.cpp.o
21% Building CXX object CMakeFiles/EXTRACTOR.dir/src/extractor/scripting_environment_lua.cpp.o
In file included from /usr/include/boost/smart_ptr/detail/sp_thread_sleep.hpp:22,
                 from /usr/include/boost/smart_ptr/detail/yield_k.hpp:23,
                 from /usr/include/boost/smart_ptr/detail/spinlock_gcc_atomic.hpp:14,
                 from /usr/include/boost/smart_ptr/detail/spinlock.hpp:42,
                 from /usr/include/boost/smart_ptr/detail/spinlock_pool.hpp:25,
                 from /usr/include/boost/smart_ptr/shared_ptr.hpp:29,
                 from /usr/include/boost/shared_ptr.hpp:17,
                 from /usr/include/boost/format/alt_sstream.hpp:22,
                 from /usr/include/boost/format.hpp:38,
                 from /srv/osrm/osrm-backend/include/util/exception.hpp:38,
                 from /srv/osrm/osrm-backend/include/extractor/extraction_relation.hpp:4,
                from /srv/osrm/osrm-backend/include/extractor/scripting_environment_lua.hpp:4,
                from /srv/osrm/osrm-backend/src/extractor/scripting_environment_lua.cpp:1:
/usr/include/boost/detail/no_exceptions_support.hpp:17:1: note: ‘#pragma message: This header is deprecated. 
Use <boost/core/no_exceptions_support.hpp> instead.’
   17 | BOOST_HEADER_DEPRECATED(“<boost/core/no_exceptions_support.hpp>”)
      | ^~~~~~~~~~~~~~~~~~~~~~~
……
        
Install the binaries.

sudo make install

The following binaries will be installed.

2. Install GNU screen

In the next step, we will need to extract road networks from OpenStreetMap, which can take a long time. Your computer might be disconnected from the Internet, so it’s recommended to use the GNU Screen utility to keep your session alive. Install screen on the Debian server:

sudo apt install screen

Then start screen:

screen

Upon the first launch, you will see an introduction text, simply press Enter to end. Then you will be able to run commands as usual.

3. Generate OSRM routing data

Now we need to download the OpenStreetMap data and make it usable for routing. If you want a map of an individual country/state/province/city, go to http://download.geofabrik.de. For example, download the map data of New Jersey (148MB) with the following command.

wget -c https://download.geofabrik.de/north-america/us/new-jersey-latest.osm.pbf -P /srv/osrm/osrm-backend/

Make sure you are in the /srv/osrm/osrm-backend/ directory.

cd /srv/osrm/osrm-backend/

Extract a graph out of the OpenStreetMap data.

osrm-extract new-jersey-latest.osm.pbf --threads=2

By default, it will use the car.lua profile.

Now you probably don’t need to do other things on your server. Since you are using Screen, you can press Ctrl+A, release those keys, and then press D key to detach from the current Screen session. You will see a message like below.

[detached from 32113.pts-1.focal]

This tells me that the previous Screen session ID is 32113. You can log out from the SSH session and even shut down your computer. Don’t worry, the osrm-extract process is still running. When you need to come back and check the progress, SSH into your server and run the following command to get the previous Screen Session ID.

screen -ls

Then you can re-attach to the previous Screen session.

screen -r 32113

Once it’s finished, there will be a file with the same filename but with the .osrm extension. Run the following command to partition this graph recursively into cells.

osrm-partition new-jersey-latest.osrm

Customize the cells by calculating routing weights for all cells.

sudo apt-get install libtbb2
osrm-customize new-jersey-latest.osrm

Now you can start the routing engine.

osrm-routed --algorithm=MLD new-jersey-latest.osrm

As you can see, it listens on TCP port 5000.

4. Creating a systemd service

We can manually run the OSRM routing engine with osrm-routed --algorithm=MLD new-jersey-latest.osrm, but it’s more convenient to run osrm-routed as a systemd service in the background.

Press Ctrl+C to stop the current osrm-routed process and create a systemd service unit file for osrm-routed with the following command.

sudo vi /etc/systemd/system/osrm-routed.service

Put the following lines into the file.

[Unit]
Description=Open Source Routing Machine
Wants=network-online.target
After=network.target network-online.target

[Service]
ExecStart=/usr/local/bin/osrm-routed --algorithm=MLD /srv/osrm/osrm-backend/new-jersey-latest.osrm
User=osrm
Group=osrm
Restart=always
RestartSec=5s

[Install]
WantedBy=multi-user.target

Save and close the file. Change the ownership of the /srv/osrm/osrm-backend/ directory.

sudo chown osrm:osrm /srv/osrm/osrm-backend/ -R

Now we can start and enable the osrm-routed systemd service.

sudo systemctl start osrm-routed
sudo systemctl enable osrm-routed

Check status.

systemctl status osrm-routed

5. Set up reverse proxy

We can configure Nginx web server as a reverse proxy for the osrm-routed service, so we will be able to use a domain name to access the routing service and also enable HTTPS encryption.

sudo apt install nginx

Then create a server block file for OSRM.

sudo vi /etc/nginx/conf.d/osr.conf

Add the following content to this file. Replace osr.your-domain.com with your actual domain name and don’t forget to create DNS A record for it.
        server {
              listen 80;
              listen [::]:80;
              server_name osr.your-domain.com tile.eastus.cloudapp.azure.com;

              access_log /var/log/nginx/osrm.access;
              error_log /var/log/nginx/osrm.error;

              location / {
                  proxy_pass http://127.0.0.1:5000;
                  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;
              }
        }
Save and close this file. Then test Nginx configuration.

sudo nginx -t

If the test is successful, reload Nginx for the change to take effect.

sudo systemctl reload nginx

6. Enable HTTPS

To encrypt HTTP traffic, we can obtain and install a free TLS certificate from Let’s Encrypt. First, install the Let’s Encrypt client (certbot) on Debian 11.

sudo apt install certbot

Since we are using Nginx web server, we also need to install the Nginx plugin.

sudo apt install python3-certbot-nginx

Then run the following command to obtain and install TLS certificate.

sudo certbot --nginx --agree-tos --email your-account@example.com -d osr.your-domain.com -d tile.eastus.cloudapp.azure.com

Where: The certificate should now be obtained and automatically installed. And you will be able to access Webmin web interface over a secure HTTPS connection.

7. Integrate OSRM with a slippy map

I assume your slippy map is displayed using the Leaflet JavaScript library. To integrate OSRM with a slippy map, we can use a plugin called Leaflet Routing Machine. First, include the Leaflet routing machine JavaScript and CSS file to your slippy map. Note that they should be placed after the main Leaflet JavaScript.

Download leaflet-routing-machine.css, leaflet-routing-machine.min.js, and leaflet.routing.icons.png and routing-icon.png into dist folder:

sudo mkdir /var/www/html/dist
cd /var/www/html/dist
sudo wget https://unpkg.com/leaflet-routing-machine@latest/dist/leaflet-routing-machine.css
sudo wget https://unpkg.com/leaflet-routing-machine@latest/dist/leaflet-routing-machine.js
sudo wget https://unpkg.com/leaflet-routing-machine@latest/dist/leaflet.routing.icons.png
sudo wget https://unpkg.com/leaflet-routing-machine@latest/dist/routing-icon.png

        <html>
          <head>
             ....
             ....
             <link rel="stylesheet" href="dist/leaflet-routing-machine.css" />
             <script src="dist/leaflet-routing-machine.js"></script>
         </head>
         <body>
         ....
         ....
         </body>
        </html>
        

Next, add the following lines to the <script>...</script> snippet in the HTML body.

            start = [39.891215, -74.968797]; // Springdale Farms
            end = [39.89478, -74.93103]; // Bank of America
            L.Routing.control({
                serviceUrl: 'https://osr.your-domain.com/route/v1',
                waypoints: [
                L.latLng(start[0], start[1]),
                L.latLng(end[0], end[1])
            ],
            routeWhileDragging: true
            }).addTo(map);
        
Like this:
        <html>
          <head>
             ....
             ....
             <link rel="stylesheet" href="dist/leaflet-routing-machine.css" />
             <script src="dist/leaflet-routing-machine.js"></script>
         </head>
         <body>
           <div id="map"></div>
             <script>
             ....
             ....

             start = [39.891215, -74.968797]; // Springdale Farms
             end = [39.89478, -74.93103]; // Bank of America
             L.Routing.control({
                 serviceUrl: 'https://osr.your-domain.com/route/v1',
                   waypoints: [
                L.latLng(start[0], start[1]),
                L.latLng(end[0], end[1])
              ],
                 routeWhileDragging: true
               }).addTo(map);

            </script>

          </body>
        </html>
        

Save and close the file. Then reload the map in your web browser, you should see a routing panel on the upper-right corner, where you can see the steps.

You can drag the waypoints on the map and OSRM will automatically recalculate the route.

Troubleshooting

If your nginx status has the following error:

nginx.service: Failed to parse PID from file /run/nginx.pid: Invalid argument

Possible causes and solutions:

sudo mkdir -p /etc/systemd/system/nginx.service.d
printf "[Service]\nExecStartPost=/bin/sleep 0.1\n" | sudo tee /etc/systemd/system/nginx.service.d/override.conf
sudo systemctl daemon-reload
sudo systemctl restart nginx