Benchmarks Tile servers
Candidates
Martin vs Tegola vs BBOX vs pg_tileserv vs TiPg vs ldproxy
Tick the candidates you care about and the charts, winners and table redraw for just them: same run, same rig, same samples. Winners come from the numbers alone; the story is in the post.
Single-tile latency
Test 1 Buildings z14
Mean latency over one connection. Dense polygon tile: every building footprint in a 1.5 km Prenzlauer Berg tile (about 2,000 polygons). (LOWER ms = FASTER)
Test 2 Roads z14
Mean latency over one connection. Line tile: every highway=* way in the same Prenzlauer Berg tile (about 2,500 lines). (LOWER ms = FASTER)
Test 3 Points z14
Mean latency over one connection. Point tile: amenity, shop and tourism nodes in the same Prenzlauer Berg tile (about 2,800 points). (LOWER ms = FASTER)
Test 4 Boundaries z9
Mean latency over one connection. Low-zoom polygon tile: the 55 administrative boundary polygons covering most of Berlin, city outline included. (LOWER ms = FASTER)
Test 5 Buildings z14, gzip
Mean latency over one connection when the client accepts gzip, as browsers do: the buildings z14 tile including whatever compression the server applies. (LOWER ms = FASTER)
Winner Martin (13.6 ms)
Throughput under concurrency
Test 1 Throughput, 10 connections
Successful buildings z14 tiles per second with 10 closed-loop clients. (HIGHER req/s = BETTER)
Test 2 Throughput, 100 connections
Successful buildings z14 tiles per second with 100 closed-loop clients. (HIGHER req/s = BETTER)
Test 3 p99 latency, 10 connections
Median across passes of each pass's p99 response time at 10 connections. A server that completed no request in a pass is charged the whole 12 s pass. (LOWER ms = FASTER)
Test 4 p99 latency, 100 connections
Median across passes of each pass's p99 response time at 100 connections. A server that completed no request in a pass is charged the whole 12 s pass. (LOWER ms = FASTER)
Test 5 Failed requests, 100 connections
Non-2xx responses plus connection errors and 60 s timeouts as a share of all requests at 100 connections. (LOWER % = FASTER)
Winner Martin (467.4 req/s)
Viewport burst
Test 1 Viewport, mean
Mean wall time until all four z15 building tiles have arrived, requested in parallel. (LOWER ms = FASTER)
Test 2 Viewport, slowest
The slowest of the measured viewport loads. (LOWER ms = FASTER)
Winner pg_tileserv (5.3 ms)
Cold start and footprint
Test 1 Time to first tile
Mean time from container process start until the building tile is served, including schema discovery, over five starts. (LOWER ms = FASTER)
Test 2 Idle memory
Container RSS five seconds after the first tile, before any load. (LOWER MB = FASTER)
Test 3 Memory after load
Container RSS right after the concurrency passes. (LOWER MB = FASTER)
Winner Martin (133.0 ms)
Test conditions: rig, corpus, protocol
- Machine
- MacBook Pro
- Chip
- Apple M2 Max
- Cores
- 12 CPU cores (8 performance, 4 efficiency)
- Memory
- 96 GB
- OS
- macOS 26.6.2 (arm64)
- Runtime
- Docker 29.4.0 (OrbStack); PostGIS 18 / 3.6.1 in its own container (6 CPUs, 12 GB); one tile server container at a time (4 CPUs, 4 GB); oha 1.16.0 on the host
- Warmups
- 3 unmeasured passes
- Measured
- 20 passes per tool
- Process
- One server container at a time against a shared PostGIS container; 8 database connections per server; 4 workers where the concept exists; 5 cold starts, 20 single-tile passes of 5 requests per tile, 20 four-tile viewport bursts, 8 passes of 12 s at 10 and at 100 connections
- Cache
- No server-side tile cache anywhere; PostgreSQL buffer cache warm after warmups; tiles requested without compression
- Output
- oha 1.16.0 JSON (latency percentiles, request counts, status codes); Bun fetch for cold start, parity and viewport bursts; docker stats for RSS
Scorecard
| Candidate | Single-tile latency (ms, lower is better) | Throughput under concurrency (req/s, higher is better) | Viewport burst (ms, lower is better) | Cold start and footprint (ms, lower is better) |
|---|---|---|---|---|
| Martin | 13.6 ms | 467.4 req/s | 5.5 ms | 133.0 ms |
| Tegola | 15.6 ms | 254.6 req/s | 7.5 ms | 142.2 ms |
| BBOX | 15.2 ms | 394.6 req/s | 6.6 ms | 146.8 ms |
| pg_tileserv | 13.7 ms | 169.3 req/s | 5.3 ms | 148.8 ms |
| TiPg | 13.8 ms | 456.2 req/s | 10.2 ms | 508.0 ms |
| ldproxy | 120.8 ms | 24.6 req/s | 51.7 ms | 3.709 s |
| ldproxy (PGIS_TILES) | 18.9 ms | 226.0 req/s | 10.5 ms | 3.686 s |
Martin is a project of the MapLibre organization. Tegola is a project of the Go Spatial organization. BBOX is developed by Sourcepole. pg_tileserv is developed by Crunchy Data. TiPg is developed by Development Seed. ldproxy is developed by interactive instruments GmbH. Map data: OpenStreetMap contributors, ODbL, via the Geofabrik Berlin extract of 2026-01-01.