Benchmarks Tile servers

Candidates

Martin vs go-pmtiles vs tileserver-gl vs mbtileserver vs BBOX

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 Street level z14

Mean latency over one connection. The densest kind of tile: a 1.5 km Prenzlauer Berg tile with every building, road, POI and landuse polygon of the OpenMapTiles schema. (LOWER ms = FASTER)

Street level z14 (ms)
Martin (PMTiles)1.2 ms
Martin (MBTiles)1.2 ms
go-pmtiles2.0 ms
tileserver-gl-light14.3 ms
mbtileserver1.1 ms
BBOX (PMTiles)2.3 ms
BBOX (MBTiles)2.7 ms
Street level z14 in ms
Martin (PMTiles)1.2 ms
Martin (MBTiles)1.2 ms
go-pmtiles2.0 ms
tileserver-gl-light14.3 ms
mbtileserver1.1 ms
BBOX (PMTiles)2.3 ms
BBOX (MBTiles)2.7 ms

Test 2 District z12

Mean latency over one connection. Inner-city tile covering Prenzlauer Berg and Mitte: roads, landuse, water and place labels, buildings dropped. (LOWER ms = FASTER)

District z12 (ms)
Martin (PMTiles)1.1 ms
Martin (MBTiles)0.8 ms
go-pmtiles1.4 ms
tileserver-gl-light3.6 ms
mbtileserver0.9 ms
BBOX (PMTiles)1.4 ms
BBOX (MBTiles)1.4 ms
District z12 in ms
Martin (PMTiles)1.1 ms
Martin (MBTiles)0.8 ms
go-pmtiles1.4 ms
tileserver-gl-light3.6 ms
mbtileserver0.9 ms
BBOX (PMTiles)1.4 ms
BBOX (MBTiles)1.4 ms

Test 3 Region z9

Mean latency over one connection. Low-zoom tile covering most of Berlin: boundaries, major roads, water, place names. (LOWER ms = FASTER)

Region z9 (ms)
Martin (PMTiles)1.0 ms
Martin (MBTiles)0.8 ms
go-pmtiles1.0 ms
tileserver-gl-light2.7 ms
mbtileserver0.8 ms
BBOX (PMTiles)1.0 ms
BBOX (MBTiles)1.0 ms
Region z9 in ms
Martin (PMTiles)1.0 ms
Martin (MBTiles)0.8 ms
go-pmtiles1.0 ms
tileserver-gl-light2.7 ms
mbtileserver0.8 ms
BBOX (PMTiles)1.0 ms
BBOX (MBTiles)1.0 ms

Test 4 Overview z5

Mean latency over one connection. Small tile at the top of the pyramid: the extract's footprint in one tile, mostly boundaries and a few labels. (LOWER ms = FASTER)

Overview z5 (ms)
Martin (PMTiles)1.0 ms
Martin (MBTiles)0.9 ms
go-pmtiles0.8 ms
tileserver-gl-light1.6 ms
mbtileserver0.7 ms
BBOX (PMTiles)0.6 ms
BBOX (MBTiles)0.6 ms
Overview z5 in ms
Martin (PMTiles)1.0 ms
Martin (MBTiles)0.9 ms
go-pmtiles0.8 ms
tileserver-gl-light1.6 ms
mbtileserver0.7 ms
BBOX (PMTiles)0.6 ms
BBOX (MBTiles)0.6 ms

Winner mbtileserver (1.1 ms)

Throughput under concurrency

Test 1 Throughput, 10 connections

Successful street level z14 tiles per second with 10 closed-loop clients. (HIGHER req/s = BETTER)

Throughput, 10 connections (req/s)
Martin (PMTiles)11,289.1 req/s
Martin (MBTiles)11,145.5 req/s
go-pmtiles2,471.6 req/s
tileserver-gl-light300.7 req/s
mbtileserver5,908.0 req/s
BBOX (PMTiles)3,884.6 req/s
BBOX (MBTiles)3,509.3 req/s
Throughput, 10 connections in req/s
Martin (PMTiles)11,289.1 req/s
Martin (MBTiles)11,145.5 req/s
go-pmtiles2,471.6 req/s
tileserver-gl-light300.7 req/s
mbtileserver5,908.0 req/s
BBOX (PMTiles)3,884.6 req/s
BBOX (MBTiles)3,509.3 req/s

Test 2 Throughput, 100 connections

Successful street level z14 tiles per second with 100 closed-loop clients. (HIGHER req/s = BETTER)

Throughput, 100 connections (req/s)
Martin (PMTiles)12,634.2 req/s
Martin (MBTiles)9,185.5 req/s
go-pmtiles4,513.5 req/s
tileserver-gl-light281.2 req/s
mbtileserver7,856.6 req/s
BBOX (PMTiles)3,961.5 req/s
BBOX (MBTiles)2,202.2 req/s
Throughput, 100 connections in req/s
Martin (PMTiles)12,634.2 req/s
Martin (MBTiles)9,185.5 req/s
go-pmtiles4,513.5 req/s
tileserver-gl-light281.2 req/s
mbtileserver7,856.6 req/s
BBOX (PMTiles)3,961.5 req/s
BBOX (MBTiles)2,202.2 req/s

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)

p99 latency, 10 connections (ms)
Martin (PMTiles)1.7 ms
Martin (MBTiles)1.6 ms
go-pmtiles14.8 ms
tileserver-gl-light48.9 ms
mbtileserver7.1 ms
BBOX (PMTiles)4.1 ms
BBOX (MBTiles)5.5 ms
p99 latency, 10 connections in ms
Martin (PMTiles)1.7 ms
Martin (MBTiles)1.6 ms
go-pmtiles14.8 ms
tileserver-gl-light48.9 ms
mbtileserver7.1 ms
BBOX (PMTiles)4.1 ms
BBOX (MBTiles)5.5 ms

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)

p99 latency, 100 connections (ms)
Martin (PMTiles)203.1 ms
Martin (MBTiles)13.8 ms
go-pmtiles108.8 ms
tileserver-gl-light449.4 ms
mbtileserver34.3 ms
BBOX (PMTiles)31.3 ms
BBOX (MBTiles)65.0 ms
p99 latency, 100 connections in ms
Martin (PMTiles)203.1 ms
Martin (MBTiles)13.8 ms
go-pmtiles108.8 ms
tileserver-gl-light449.4 ms
mbtileserver34.3 ms
BBOX (PMTiles)31.3 ms
BBOX (MBTiles)65.0 ms

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)

Failed requests, 100 connections (%)
Martin (PMTiles)0 %
Martin (MBTiles)0 %
go-pmtiles0 %
tileserver-gl-light0 %
mbtileserver0 %
BBOX (PMTiles)0 %
BBOX (MBTiles)0 %
Failed requests, 100 connections in %
Martin (PMTiles)0 %
Martin (MBTiles)0 %
go-pmtiles0 %
tileserver-gl-light0 %
mbtileserver0 %
BBOX (PMTiles)0 %
BBOX (MBTiles)0 %

Winner Martin (PMTiles) (12,634.2 req/s)

Random tile walk

Test 1 Throughput, 10 connections

Successful tiles per second when 10 closed-loop clients each request a random z14 tile from a 256-tile block of central Berlin. (HIGHER req/s = BETTER)

Throughput, 10 connections (req/s)
Martin (PMTiles)12,992.8 req/s
Martin (MBTiles)13,724.0 req/s
go-pmtiles4,533.1 req/s
tileserver-gl-light793.2 req/s
mbtileserver7,110.4 req/s
BBOX (PMTiles)8,292.2 req/s
BBOX (MBTiles)6,503.4 req/s
Throughput, 10 connections in req/s
Martin (PMTiles)12,992.8 req/s
Martin (MBTiles)13,724.0 req/s
go-pmtiles4,533.1 req/s
tileserver-gl-light793.2 req/s
mbtileserver7,110.4 req/s
BBOX (PMTiles)8,292.2 req/s
BBOX (MBTiles)6,503.4 req/s

Test 2 p99 latency, 10 connections

Median across passes of each pass's p99 response time on the random walk. A server that completed no request in a pass is charged the whole 12 s pass. (LOWER ms = FASTER)

p99 latency, 10 connections (ms)
Martin (PMTiles)1.5 ms
Martin (MBTiles)1.4 ms
go-pmtiles8.5 ms
tileserver-gl-light33.8 ms
mbtileserver5.8 ms
BBOX (PMTiles)2.8 ms
BBOX (MBTiles)3.5 ms
p99 latency, 10 connections in ms
Martin (PMTiles)1.5 ms
Martin (MBTiles)1.4 ms
go-pmtiles8.5 ms
tileserver-gl-light33.8 ms
mbtileserver5.8 ms
BBOX (PMTiles)2.8 ms
BBOX (MBTiles)3.5 ms

Winner Martin (MBTiles) (13,724.0 req/s)

Viewport burst

Test 1 Viewport, mean

Mean wall time until all four z14 tiles have arrived, requested in parallel. (LOWER ms = FASTER)

Viewport, mean (ms)
Martin (PMTiles)1.2 ms
Martin (MBTiles)1.2 ms
go-pmtiles1.9 ms
tileserver-gl-light18.7 ms
mbtileserver2.2 ms
BBOX (PMTiles)1.9 ms
BBOX (MBTiles)2.6 ms
Viewport, mean in ms
Martin (PMTiles)1.2 ms
Martin (MBTiles)1.2 ms
go-pmtiles1.9 ms
tileserver-gl-light18.7 ms
mbtileserver2.2 ms
BBOX (PMTiles)1.9 ms
BBOX (MBTiles)2.6 ms

Test 2 Viewport, slowest

The slowest of the measured viewport loads. (LOWER ms = FASTER)

Viewport, slowest (ms)
Martin (PMTiles)1.7 ms
Martin (MBTiles)1.9 ms
go-pmtiles4.8 ms
tileserver-gl-light22.3 ms
mbtileserver11.6 ms
BBOX (PMTiles)3.2 ms
BBOX (MBTiles)5.7 ms
Viewport, slowest in ms
Martin (PMTiles)1.7 ms
Martin (MBTiles)1.9 ms
go-pmtiles4.8 ms
tileserver-gl-light22.3 ms
mbtileserver11.6 ms
BBOX (PMTiles)3.2 ms
BBOX (MBTiles)5.7 ms

Winner Martin (PMTiles) (1.2 ms)

Cold start and footprint

Test 1 Time to first tile

Mean time from container process start until the street-level tile is served, including opening the archive and reading its metadata, over five starts. (LOWER ms = FASTER)

Time to first tile (ms)
Martin (PMTiles)158.0 ms
Martin (MBTiles)153.0 ms
go-pmtiles157.0 ms
tileserver-gl-light276.0 ms
mbtileserver137.0 ms
BBOX (PMTiles)118.0 ms
BBOX (MBTiles)150.0 ms
Time to first tile in ms
Martin (PMTiles)158.0 ms
Martin (MBTiles)153.0 ms
go-pmtiles157.0 ms
tileserver-gl-light276.0 ms
mbtileserver137.0 ms
BBOX (PMTiles)118.0 ms
BBOX (MBTiles)150.0 ms

Test 2 Idle memory

Container RSS five seconds after the first tile, before any load. (LOWER MB = FASTER)

Idle memory (MB)
Martin (PMTiles)9.1 MB
Martin (MBTiles)9.8 MB
go-pmtiles13.1 MB
tileserver-gl-light46.2 MB
mbtileserver5.5 MB
BBOX (PMTiles)11.4 MB
BBOX (MBTiles)11.3 MB
Idle memory in MB
Martin (PMTiles)9.1 MB
Martin (MBTiles)9.8 MB
go-pmtiles13.1 MB
tileserver-gl-light46.2 MB
mbtileserver5.5 MB
BBOX (PMTiles)11.4 MB
BBOX (MBTiles)11.3 MB

Test 3 Memory after load

Container RSS right after the concurrency passes. (LOWER MB = FASTER)

Memory after load (MB)
Martin (PMTiles)167.7 MB
Martin (MBTiles)63.2 MB
go-pmtiles44 MB
tileserver-gl-light308.7 MB
mbtileserver66.2 MB
BBOX (PMTiles)34.9 MB
BBOX (MBTiles)68.9 MB
Memory after load in MB
Martin (PMTiles)167.7 MB
Martin (MBTiles)63.2 MB
go-pmtiles44 MB
tileserver-gl-light308.7 MB
mbtileserver66.2 MB
BBOX (PMTiles)34.9 MB
BBOX (MBTiles)68.9 MB

Winner BBOX (PMTiles) (117.6 ms)

Test conditions: rig, corpus, protocol
Rig
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); one tile server container at a time (4 CPUs, 4 GB) with both archives bind-mounted read-only; oha 1.16.0 on the host
Protocol
Warmups
3 unmeasured passes
Measured
20 passes per tool
Process
One server container at a time reading one of two archives with identical tiles (berlin.mbtiles from Planetiler 0.10.2, berlin.pmtiles converted from it by go-pmtiles); 4 workers where the concept exists; 5 cold starts, 20 single-tile passes of 20 requests per tile, 20 four-tile viewport bursts, 8 passes of 12 s at 10 and at 100 connections on one tile, 8 passes of 12 s at 10 connections over 256 random tiles
Cache
No server-side tile cache configured anywhere (go-pmtiles keeps its default 64 MB directory cache); archives in the page cache after warmups; every request accepts gzip, as map clients do, except the identity variant
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

Mean of each section's samples for the candidates you picked. The best value in each column is marked in red.
CandidateSingle-tile latency (ms, lower is better)Throughput under concurrency (req/s, higher is better)Random tile walk (req/s, higher is better)Viewport burst (ms, lower is better)Cold start and footprint (ms, lower is better)
Martin (PMTiles)1.2 ms12,634.2 req/s12,992.8 req/s1.2 ms158.4 ms
Martin (MBTiles)1.2 ms9,185.5 req/s13,724.0 req/s1.2 ms153.2 ms
go-pmtiles2.0 ms4,513.5 req/s4,533.1 req/s1.9 ms157.4 ms
tileserver-gl-light14.3 ms281.2 req/s793.2 req/s18.7 ms276.4 ms
mbtileserver1.1 ms7,856.6 req/s7,110.4 req/s2.2 ms137.2 ms
BBOX (PMTiles)2.3 ms3,961.5 req/s8,292.2 req/s1.9 ms117.6 ms
BBOX (MBTiles)2.7 ms2,202.2 req/s6,503.4 req/s2.6 ms149.8 ms

Martin is a project of the MapLibre organization. go-pmtiles and the PMTiles format are developed by Protomaps. tileserver-gl is developed by MapTiler. mbtileserver is developed by the Conservation Biology Institute. BBOX is developed by Sourcepole. Planetiler is developed by Michael Barry and contributors; the tile schema is OpenMapTiles. Map data: OpenStreetMap contributors, ODbL, via the Geofabrik Berlin extract of 2026-01-01; water polygons from OSMData, Natural Earth public domain.