This comprehensive guide walks you through the steps to serve your OpenTopoMap tiles via Apache and the renderd daemon. It assumes that your server is already set up for OpenTopoMap tile rendering.
The commands below were tested on Ubuntu 24.04 LTS with Apache 2.4, Mapnik 3.1 and the official mod_tile 0.8.1 release. The renderer account in this guide is gis. Substitute another account consistently if required.
Contents
Install the build dependencies
sudo apt update
sudo apt install + git + cmake + build-essential + apache2 + apache2-dev + libmapnik-dev + libcairo2-dev + libiniparser-dev + libcurl4-openssl-dev + libglib2.0-dev + curl
The standalone curl package is required when running the mod_tile test suite. Installing the libcurl development package alone is not sufficient.
Compile and install mod_tile
Use the official openstreetmap/mod_tile repository. The old SomeoneElseOSM/mod_tile switch2osm branch, Autotools build and manual source patches are no longer needed.
mkdir -p ~/src
git clone + --branch v0.8.1 + --depth 1 + https://github.com/openstreetmap/mod_tile.git + ~/src/mod_tile
Git reports detached-HEAD state because v0.8.1 is a release tag. That is expected.
Use a logged screen session for the build:
screen -L + -Logfile /home/gis/mod-tile-build.log + -S mod-tile-build
Run inside the screen session:
set -euo pipefail
cmake + -S /home/gis/src/mod_tile + -B /home/gis/src/mod_tile-build + -DCMAKE_BUILD_TYPE=Release + -DCMAKE_INSTALL_PREFIX=/usr + -DCMAKE_INSTALL_LOCALSTATEDIR=/var + -DCMAKE_INSTALL_RUNSTATEDIR=/run + -DCMAKE_INSTALL_SYSCONFDIR=/etc + -DUSE_MEMCACHED=OFF + -DUSE_RADOS=OFF + -DENABLE_TESTS=ON
cmake + --build /home/gis/src/mod_tile-build + --parallel "$(nproc)"
ctest + --test-dir /home/gis/src/mod_tile-build + --output-on-failure
sudo cmake + --install /home/gis/src/mod_tile-build + --strip
Detach from screen with Ctrl-A, then D. On the tested server all 145 tests passed. The installation places the programs in /usr/bin, the Apache module in /usr/lib/apache2/modules, and the configuration in /etc.
renderd --version
command -v renderd
command -v render_list
ls -l + /usr/bin/renderd + /usr/bin/render_list + /usr/lib/apache2/modules/mod_tile.so + /etc/renderd.conf + /etc/apache2/mods-available/tile.load
Create the renderd configuration
Back up the generated default and replace it:
sudo cp -a /etc/renderd.conf /etc/renderd.conf.install-default
sudo tee /etc/renderd.conf >/dev/null <<'EOF'
[renderd]
num_threads=4
pid_file=/run/renderd/renderd.pid
socketname=/run/renderd/renderd.sock
stats_file=/run/renderd/renderd.stats
tile_dir=/var/cache/renderd/tiles
[mapnik]
font_dir=/usr/share/fonts
font_dir_recurse=1
plugins_dir=/usr/lib/mapnik/3.1/input
[opentopomap]
URI=/tile/
TILEDIR=/var/cache/renderd/tiles
XML=/home/gis/OpenTopoMap/mapnik/opentopomap.xml
HOST=localhost
TILESIZE=256
MINZOOM=0
MAXZOOM=17
TYPE=png image/png png256
EOF
Important: the TYPE setting contains three fields: the filename extension, MIME type and Mapnik output format. With mod_tile 0.8.1, the incomplete line TYPE=png image/png caused Apache child processes to segfault. Use the complete line shown above, or omit TYPE and use the default.
Prepare the persistent cache and runtime directory:
sudo install + -d + -o gis + -g www-data + -m 2775 + /var/cache/renderd + /var/cache/renderd/tiles
sudo install + -d + -o gis + -g www-data + -m 0775 + /run/renderd
Create the renderd systemd service
The CMake installation does not install a systemd unit. Create one explicitly:
sudo tee /etc/systemd/system/renderd.service >/dev/null <<'EOF'
[Unit]
Description=OpenTopoMap tile rendering daemon
After=network.target postgresql.service
Wants=postgresql.service
[Service]
Type=simple
User=gis
Group=www-data
WorkingDirectory=/home/gis/OpenTopoMap/mapnik
Environment=HOME=/home/gis
ExecStart=/usr/bin/renderd --foreground --config=/etc/renderd.conf
Restart=on-failure
RestartSec=5
RuntimeDirectory=renderd
RuntimeDirectoryMode=0775
UMask=0002
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
EOF
sudo systemd-analyze verify /etc/systemd/system/renderd.service
sudo systemctl daemon-reload
sudo systemctl enable --now renderd
Verify the service, socket and cache permissions:
sudo systemctl status renderd --no-pager -l
sudo journalctl + -u renderd + --since '5 minutes ago' + --no-pager
ls -la /run/renderd
ls -ld /var/cache/renderd/tiles
A healthy installation reports renderd 0.8.1, Mapnik 3.1, four rendering threads and the opentopomap map. The socket /run/renderd/renderd.sock must be accessible to Apache.
Configure Apache to load mod_tile
The installation already provides /etc/apache2/mods-available/tile.load. Do not create a second LoadModule directive.
Create the site-wide mod_tile configuration:
sudo tee /etc/apache2/conf-available/opentopomap-tiles.conf >/dev/null <<'EOF'
<IfModule tile_module>
ModTileTileDir /var/cache/renderd/tiles
LoadTileConfigFile /etc/renderd.conf
ModTileRenderdSocketName /run/renderd/renderd.sock
ModTileEnableStats On
ModTileBulkMode Off
ModTileRequestTimeout 10
ModTileMissingRequestTimeout 120
ModTileMaxLoadOld 16
ModTileMaxLoadMissing 50
ModTileCacheDurationMax 604800
ModTileCacheDurationDirty 900
ModTileCacheDurationMinimum 10800
ModTileCacheDurationMediumZoom 13 86400
ModTileCacheDurationLowZoom 9 518400
ModTileCacheLastModifiedFactor 0.20
ModTileEnableTileThrottling On
ModTileEnableTileThrottlingXForward 0
ModTileThrottlingTiles 10000 1
ModTileThrottlingRenders 128 0.2
ModTileEnableStatusURL On
ModTileEnableDirtyURL Off
</IfModule>
EOF
sudo a2enmod tile
sudo a2enconf opentopomap-tiles
sudo apache2ctl configtest
sudo systemctl restart apache2
A single full restart is sufficient. The old workaround that reloaded Apache twice is no longer required.
sudo systemctl status apache2 renderd --no-pager -l
apache2ctl -M 2>/dev/null | grep tile
Recover from an Apache crash loop
If Apache child processes start segfaulting after enabling mod_tile, stop testing requests and restore Apache first:
sudo a2disconf opentopomap-tiles
sudo a2dismod tile
sudo apache2ctl configtest
sudo systemctl restart apache2
Then check /etc/renderd.conf. In particular, verify that TYPE contains all three fields before re-enabling the module.
Test renderd directly
Render the known Zugspitze tile through renderd. This produces one 8×8 metatile containing 64 individual tiles.
screen -L + -Logfile /home/gis/render-test-zugspitze.log + -S render-test-zugspitze
Run inside screen:
set -euo pipefail
SECONDS=0
printf '8691 5734 14\n' |
render_list + --config=/etc/renderd.conf + --map=opentopomap + --force + --num-threads=1
printf 'Elapsed: %d seconds\n' "$SECONDS"
Do not place Bash’s time keyword directly after the pipe. In that position Bash tries to run an external time command. On the tested server the metatile rendered in 6.38 seconds.
You can monitor renderd in another terminal:
sudo journalctl -u renderd -f
Mapnik messages about unsupported SVG attributes such as overflow and enable-background are nonfatal.
Test tile delivery through Apache
curl + --max-time 30 + --fail + --show-error + --silent + --dump-header /tmp/opentopomap-tile.headers + --output /tmp/opentopomap-zugspitze-z14.png + http://127.0.0.1/tile/14/8691/5734.png
cat /tmp/opentopomap-tile.headers
file /tmp/opentopomap-zugspitze-z14.png
ls -l /tmp/opentopomap-zugspitze-z14.png
The tested request returned a valid 51,732-byte PNG. Tiles are exposed using this URL scheme:
http://<server>/tile/{z}/{x}/{y}.png
For example:
http://<server>/tile/14/8691/5734.png
Inspect the cached metatile and optional status endpoint:
find /var/cache/renderd/tiles/opentopomap + -type f + -printf '%s bytes %p\n' |
sort -nr |
head -20
curl --max-time 10 --fail --show-error + http://127.0.0.1/tile/14/8691/5734.png/status
Done
Apache now serves OpenTopoMap tiles generated on demand by renderd and cached as metatiles below /var/cache/renderd/tiles/opentopomap. The render daemon starts automatically with systemd. A browsable Leaflet front end and HTTPS can be added separately.