Setting Up MapProxy with Docker and Serving Cached Tiles via Nginx

MapProxy is a powerful open-source proxy for geospatial data that allows for efficient caching and serving of map tiles. Combining MapProxy with Docker and Nginx can provide a scalable and easily manageable solution for serving cached map tiles. This guide will walk you through the process of setting up MapProxy using Docker and configuring Nginx to serve cached tiles.

Prerequisites:

Docker installed on your system

Basic understanding of Docker

Basic understanding of Nginx

If you don't meet this prerequisites yet I recommend to have a look at the following guides first:

Nginx Beginner’s Guide

Docker overview

Install Docker Engine

Set Up MapProxy with Docker

Start by creating a Network in Docker with:

sudo docker create network nginx

By adding the network nginx to the Nginx Container and the Mapproxy container the containers can communicate with each other without exposing ports on the server.

Create a Docker Compose file docker-compose.yml with the following content:

networks:
default:
external: true
name: nginx

services:
mapproxy:
image: kartoza/mapproxy
container_name: mapproxy
restart: always
environment:
PRODUCTION: true
PROCESSES: 4
CHEAPER: 2
THREADS: 8
MULTI_MAPPROXY: true
MULTI_MAPPROXY_DATA_DIR: /multi_mapproxy/configurations
ALLOW_LISTING: true
volumes:
- /data/containers/mapproxy/data:/multi_mapproxy

Save the file and run:

docker-compose up -d

This will pull the MapProxy Docker image and start a container. MapProxy will be accessible on the container with http://localhost:8080.

Set Up Nginx with Docker

Afterwards you need to create a Docker Compose file docker-compose.yml for Nginx. Here's a example:

version: '3'

networks:
default:
external: true
name: nginx

services:
nginx:
image: nginx:latest
container_name: nginx
restart: always
ports:
- "80:80"
- "443:443"
volumes:
## Config
- /data/containers/nginx/config/:/etc/nginx/
## SSL
- /etc/letsencrypt/:/etc/letsencrypt/
- /etc/ssl/:/etc/ssl/
## Logs
- /data/containers/nginx/logs/:/var/log/nginx
## Cache
- /data/containers/nginx/cache:/var/cache/nginx

Then you can create a virtual host for mapproxy under /data/containers/nginx/config/sites-available/mapproxy with:

sudo nano /data/containers/nginx/config/sites-available/mapproxy

Copy and paste the following virtual host configuration for your Mapproxy Container :

upstream mapproxy_upstream {
server mapproxy:8080;
}

server {

server_name mapproxy.domain.com;

listen 80;

## Mapproxy default
location / {
proxy_pass http://mapproxy_upstream/;
proxy_set_header Host $http_host;
}
}

After you created your configuration you need to create a symlink to /data/containers/nginx/config/sites-enabled/ with:

sudo ln -S /data/containers/nginx/config/sites-available/mapproxy /data/containers/nginx/config/sites-enabled/

In order that your domain gets resolved you have to create a A-record for your Domain under which you want to publish MapProxy which points to your server ip. Then you need to restart your Nginx Container and now should be able to access http://mapproxy.domain.com where you will be greeted by your MapProxy Instance.

Step 3: Caching the Tiles with MapProxy

So far you have set up Nginx and MapProxy with Docker and MapProxy will cache by default all served tiles with the default configuration. However there are some limitations to the MapProxy caching process because each time a tile gets requested from MapProxy, MapProxy will save the tile to it's cache. But it won't check by default if the tile is old and a newer tile could be served.

E.g. I am serving tiles from MapProxy which visualize my locations where I have ever been to. So I don't want to cache the tiles infinite because they change probably every day

But you can force MapProxy to refresh tiles from the source while serving if they are found to be expired. The validity conditions are the same as for seeding:

#Explanation
# absolute as ISO time
refresh_before:
time: 2010-10-21T12:35:00
# relative from the time of the tile request
refresh_before:
weeks: 1
days: 7
hours: 4
minutes: 15
# modification time of a given file
refresh_before:
mtime: path/to/file

So to stay at my example i added:

refresh_before:
days 1

This way MapProxy will refresh tiles everyday. This of course only makes sense for data where you want always latest data

So edit your mapproxy.yaml like this:

services:
demo:
tms:
use_grid_names: true
# origin for /tiles service
origin: 'nw'
kml:
use_grid_names: true
wmts:
wms:
md:
title: MapProxy WMS Proxy
abstract: This is a minimal MapProxy example.

layers:
- name: osm
title: Omniscale OSM WMS - osm.omniscale.net
sources: [osm_cache]

caches:
osm_cache
grids: [webmercator]
sources: [osm_wms]
refresh_before:
days: 1

sources:
osm_wms:
type: wms
req:
url: https://maps.omniscale.net/v2/demo/style.default/service?
layers: osm

grids:
webmercator:
base: GLOBAL_WEBMERCATOR

globals:

Save the file and restart your MapProxy Container.

Now MapProxy will always refresh tiles everyday.

Step 4: Access the MapProxy Tiles

Visit http://mapproxy.domain.com in your browser, and you should see MapProxy serving tiles through Nginx.

By following these steps, you've successfully set up MapProxy with Docker and configured Nginx to serve cached map tiles. This scalable solution allows for efficient geospatial data delivery with the added benefit of easy container management. Adjust the configurations based on your specific requirements and integrate this setup into your mapping projects.

If you decide to to take use of MultiMapProxy(scroll down to MultiMapProxy) you can just create more configuration files for MapProxy in /data/containers/mapproxy/data/configurations and append the cache path and location blocks like in your existing Nginx configuration for MapProxy.