Choosing between Martin and TiTiler is a common decision for teams serving geospatial imagery. One pre-generates tiles for maximum serving performance, while the other generates tiles dynamically from Cloud Optimized GeoTIFFs.
We built a benchmarking platform to compare both approaches across processing time, latency, CPU consumption, and memory usage.
What we built
It’s a small app that walks the whole flow end to end — download an image, process it, visualise it, right there in one place. We use the Python package phidown to pull Sentinel imagery fast, so you can go from “I want this scene” to “it’s on a map” without leaving the app.
On top of that, we wrote our own JavaScript-based load tester. You process an image, then hammer it with real-time load tests and watch what happens. Every run gets logged to a study board that pools all the results, so you can actually see which service burned what resources and how long it took to respond, instead of guessing.
Try the app
Explore the full workflow — from Sentinel image download to live tile server load testing — in our hosted app. Launch the app
The diagram below outlines the end-to-end workflow the app implements:
The two contenders
Martin serves tiles that are pre-baked into MBTiles (or PMTiles). All the heavy lifting happens upfront, before anyone ever requests a tile.
TiTiler takes the opposite approach, it loads Cloud Optimized GeoTIFFs (COGs) and converts them to tiles on the fly, per request.
Two totally different philosophies. So which one holds up?
Benchmark Methodology
All tests were executed on a D8as_v5 Azure VM, with each tile server running in an isolated container to prevent resource contention between runs.
For every dataset (Sentinel-2 scenes, e.g. S2B_MSIL2A_20260313T064619..., S2A_MSIL2A_20260330T065321...), we ran multiple benchmark passes at a fixed zoom level (z9) across a range of concurrency configurations, denoted zoom · users × tiles-per-user — for example 10u × 20t (10 concurrent users, 20 tile requests each → 200 total requests) up to 100u × 200t (100 concurrent users, 200 tile requests each → 20,000 total requests). This let us compare casual single-user browsing against sustained, high-concurrency load. Each run measured:
Request count (REQ)
Average latency (AVG)
P95 latency
P99 latency
Error rate (ERR%)
CPU peak
Memory peak
Total wall-clock time
Both services were tested against the same source imagery, same zoom level, and same request patterns to keep the comparison fair. Cache behaviour was held constant across both systems, and processing time (COG creation vs MBTiles baking) was measured separately from serving performance to isolate the two cost centers — pre-processing vs per-request compute.
Results were aggregated across multiple runs per dataset and logged to a shared study board rather than reported from a single sample, so the numbers below reflect consistent trends rather than one-off measurements.
What the numbers told us
Here’s where it got interesting.
TiTiler provided faster ingestion times. Building a COG averaged around 29 seconds, while baking MBTiles for Martin took roughly 50 seconds — about 42% slower. The culprit is the rio-mbtiles step, which churns through the full zoom pyramid and ate up nearly half the entire Martin pipeline on its own. If you just want to get something on a map quickly, TiTiler gets you there faster.
Serving tiles for casual browsing? Basically a dead heat. Live map sessions landed at ~297ms for TiTiler and ~250ms for Martin. Close enough that you wouldn’t feel the difference panning around a map.
Under concurrent load (Entries with high amount of REQ showed on Figure 1), Martin demonstrated lower latency and substantially lower resource consumption. At the z9 · 100u × 200t configuration — 100 concurrent users, 20,000 total requests, on the same imagery dataset — Martin averaged 433ms while TiTiler averaged 1,193ms, nearly 3x slower. The gap widens further at the tail: Martin’s P99 held at 1,204ms versus TiTiler’s 2,712ms.
The resource gap is the part that genuinely surprised us. Across the high-concurrency runs, Martin’s CPU peak stayed in the single digits — as low as 3.0% and never exceeding ~6.2% — with memory peaking around 25–33MB. TiTiler, under the same load, spiked to 45.3%–188.2% CPU (i.e. using more than one full core) and 350–470MB of memory. That’s not a small difference — that’s an order of magnitude, and it holds consistently across multiple runs and datasets, not just one outlier.
At lower concurrency (z9 · 10u × 20t, 200 requests), the picture is closer to a coin flip — in some runs Martin was faster on average latency, in others TiTiler was — but even at this light load, TiTiler’s CPU usage was consistently far higher than Martin’s (often 45%+ vs. Martin’s ~2–3%), suggesting the resource-efficiency gap shows up before the latency gap does.
It makes sense once you think about it:
Martin's architecture benefits from serving pre-generated tiles, reducing per-request processing overhead compared with dynamic tile generation from COGs.
TiTiler has to warp and resample from the COG on every single request, and that cost stacks up fast when a bunch of users pile on at once.
Limitations
These results reflect our specific workloads, datasets, and infrastructure. Different imagery resolutions, storage backends, caching layers, network conditions, or rendering configurations may produce different outcomes. The benchmark should therefore be viewed as a comparison of representative deployment patterns rather than an absolute ranking.


So which should you reach for?
It really comes down to what you’re optimising for.
Go with TiTiler if you’re iterating quickly, want to tweak rescaling or symbology on the fly without re-baking anything, and don’t expect a crowd. One COG file is also just easier to archive, version, and hand off to other GDAL tools.
Go with Martin if you’re serving a high-scale, highly-concurrent userbase. Yes, you pay more upfront to bake the tiles, and yes, you have to rebuild them if you want to change how they look — but once that pyramid exists, it’s rock-solid, predictable, and astonishingly cheap to serve.
The trade-off ultimately comes down to where you want to pay the cost. Martin shifts computation to an upfront tile-generation step and rewards that investment with lower serving overhead. TiTiler minimizes preprocessing and offers greater flexibility, but incurs additional work on every request. Neither approach is universally better; the right choice depends on whether your priority is rapid iteration or high-scale serving efficiency.




