I am greatly dependent on Google Maps. Whenever I go someplace new or want to navigate, I turn to Google Maps. I don’t like being dependent on FAANG companies, so here we are building our own tile server.

Running your own tile server is quite intimidating, as it comes with quite a few moving parts (apache, mod_tile, renderd, osm2psql, postgres, mapnik). Also the hardware requirements are quite big for a small side-project to run indefinitely(24+ Gigs RAM, quad-core processor).

Hardware requirements are higher than what I can afford to continuously spend on a small side-project (24+ Gigs RAM, quad-core processor - even at Hetzner that’s around 35€/month). To be honest, I don’t travel outside Europe too much. In fact, 99% of the time I am in the city I live in. I really just want the following:

  • high resolution of the city I live in
  • street-level resolution of the country I live in
  • “highway” / city-level resolution of the continent I live in

Geofabrik has the OSM data in geographic chunks so you can download just the subset you want. Europe is just 25GB of download size (around 620GB of postgres database space).

Setting up the tileserver

As a server I decided to use my old desktop that is ten years old and thereby essentially free. My ten year old desktop has 16GB RAM and an Intel i5-3450.

There is a great docker image that makes setting up your own tileserver trivial.

Rendering tiles on this machine tuns out to require a lot of swapspace (just take something really high such as 48-64 GB to be on the safe side), but it works. The reason for this seems to be that performance optimization was focussed on the higher zoomlevels, because there are a lot more tiles to generate at the higher zoomlevels.

Prerendering tiles

As internet connections at home can be unreliable, I decided to prerender tiles on my desktop and then upload them to the VPS. Turns out if you select the areas you want maps for, you can get away with a very manageable amount of data - single to double-digit gigabytes. Planet-scale rendering would take approximately 54 TB.

# World
docker exec "osm_run" render_list -n 4  -a  -f  -m ajt  -z 0  -Z 10
# Germany
docker exec "osm_run" ./render_list_geo.pl -m ajt -x 5.988 -X 15.017 -y 47.30249 -Y 54.9831 -z 11 -Z 13
# Any other areas...
# Your city should probably be rendered up to `-Z 20`

Serving Tiles from the VM

First, the tiles need to be uploaded to the VM.

rsync -avr ajt/ myserver:/mnt/tiles/ajt

While the tiles that are being served to the browser are small PNG files, in the filesystem they are aggregated into larger “metatiles”. The reason for these metatiles is that rendering smaller tiles would increase overhead both during rendering as well as in storage. An ocean tile can be as little as ~110 bytes large.

To serve the Metatiles directly I wrote a small parser that extracts the PNGs from the metatiles: https://github.com/nielsole/go_tile

Wrapping up

There we have it - Our very own maps of europe