Documentation

tileserver-rs is designed for high-performance tile serving. This page documents benchmark results for PMTiles, MBTiles, PostgreSQL, and Cloud Optimized GeoTIFF (COG) sources.

Test Environment

  • Hardware: Apple Silicon (M-series) MacBook
  • Runtime: All servers running in Docker containers (ARM64 native)
  • Test Tool: autocannon (Node.js HTTP benchmarking)
  • Configuration: 100 concurrent connections, 10 seconds per endpoint

Test Data

SourceFileAreaZoom RangeSize
PMTilesprotomaps-sample.pmtilesFlorence, Italy0-156.3 MB
MBTileszurich_switzerland.mbtilesZurich, Switzerland0-1434 MB
PostgreSQLbenchmark_points tableZurich, Switzerland0-1450,000 points
COGbenchmark-rgb.cog.tifWorld (Web Mercator)0-2290 MB

Summary Results

SourceAvg Requests/secAvg ThroughputAvg Latency
PMTiles1,047 req/s93.18 MB/s171ms
MBTiles1,133 req/s92.62 MB/s181ms

Both formats deliver ~93 MB/s throughput with 1,000+ requests/second under heavy load (100 concurrent connections).

Detailed Results by Zoom Level

PMTiles (Florence, Italy)

ZoomLocationRequests/secThroughputAvg LatencyP99 Latency
z0World23697.88 MB/s461ms1,190ms
z4Europe40395.51 MB/s264ms607ms
z8Italy1,07191.72 MB/s99ms191ms
z10Tuscany1,29089.81 MB/s81ms158ms
z12Florence1,67593.62 MB/s62ms119ms
z14City Center1,60590.54 MB/s63ms121ms

MBTiles (Zurich, Switzerland)

ZoomLocationRequests/secThroughputAvg LatencyP99 Latency
z0World3,44189.82 MB/s29ms55ms
z4Europe99089.84 MB/s104ms207ms
z8Switzerland42692.90 MB/s252ms669ms
z10Zurich Region59092.33 MB/s180ms361ms
z12Zurich City1,08891.47 MB/s97ms191ms
z14City Center26699.37 MB/s425ms1,166ms

Analysis

Key Insights

  • Throughput is consistent at ~90-100 MB/s regardless of zoom level
  • Latency scales with tile size - low zoom (large tiles) = higher latency
  • High zoom requests are fastest - z10-z14 achieve 1,000-3,400 req/s

PMTiles Performance

  • Consistent performance across zoom levels
  • Best at city zoom (z12-z14): 1,600+ req/s with 62ms latency
  • Memory-mapped file access provides predictable performance

MBTiles Performance

  • Fastest at z0: 3,441 req/s with only 29ms latency
  • SQLite overhead more visible at high-detail tiles (z14: 425ms)
  • Good for local development and smaller datasets

Format Comparison

AspectPMTilesMBTiles
Best forProduction, CDN, cloudDevelopment, local
ConsistencyMore predictableVariable by tile size
High-zoom perfExcellentGood
Low-zoom perfGoodExcellent

Running Benchmarks

To reproduce these benchmarks with fair Docker-to-Docker comparison:

# Build tileserver-rs Docker image for your platform
docker build -t tileserver-rs:local .

# Update benchmarks/docker-compose.yml to use local image
# Then start all servers
docker compose -f benchmarks/docker-compose.yml up -d

# Run benchmarks
cd benchmarks
bun install
node run-benchmarks.js --duration 10 --connections 100

Benchmark Options

# Test only PMTiles
node run-benchmarks.js --format pmtiles

# Test only MBTiles
node run-benchmarks.js --format mbtiles

# Test PostgreSQL table sources
node run-benchmarks.js --format postgres

# Test PostgreSQL function sources
node run-benchmarks.js --format postgres_function

# Test COG/raster sources
node run-benchmarks.js --format cog --connections 10

# Test specific server
node run-benchmarks.js --server tileserver-rs

# Longer test with more connections
node run-benchmarks.js --duration 30 --connections 200

# Generate markdown report
node run-benchmarks.js --markdown

Server Comparison

We benchmarked tileserver-rs against martin and tileserver-gl using the same test data. All servers ran in Docker containers on ARM64 for a fair apples-to-apples comparison.

PMTiles Performance (Florence, Italy)

ServerAvg Req/secAvg LatencyThroughput
tileserver-rs1,40979ms92 MB/s
tileserver-gl1,27491ms81 MB/s
martin531,783ms6 MB/s

tileserver-rs is ~10% faster than tileserver-gl and ~26x faster than martin for PMTiles serving.

MBTiles Performance (Zurich, Switzerland)

ServerAvg Req/secAvg LatencyThroughput
martin876128ms179 MB/s
tileserver-gl756198ms89 MB/s
tileserver-rs736188ms90 MB/s

All three servers perform competitively for MBTiles. Martin leads on throughput due to in-memory caching; tileserver-rs and tileserver-gl are within ~3% of each other.

PostgreSQL Performance (Zurich Points)

Benchmarks run with native ARM64 PostgreSQL 16.13 + PostGIS 3.4.4 (compiled from source for fair benchmark).

ServerAvg Req/secAvg LatencyThroughput
tileserver-rs3,596209ms422 MB/s
martin3,674209ms429 MB/s

Both servers are effectively PostGIS-bound — performance is nearly identical. The PostgreSQL query and ST_AsMVT encoding dominate the response time, not the tile server overhead.

PostgreSQL by Zoom Level (tileserver-rs vs martin)

Zoomtileserver-rsmartinWinner
z10189 req/s192 req/s~tie
z11338 req/s346 req/s~tie
z12866 req/s874 req/s~tie
z133,442 req/s3,532 req/s~tie
z1413,144 req/s13,423 req/s~tie
Success

Both servers hit the same PostgreSQL bottleneck — performance is virtually identical across all zoom levels, confirming the database is the limiting factor, not the tile server.

PostgreSQL Optimizations

tileserver-rs includes several PostgreSQL performance optimizations:

  • Connection pool pre-warming - All connections established at startup
  • Prepared statement caching - Tile queries pre-prepared on all connections
  • ST_TileEnvelope margin - PostGIS 3.1+ margin parameter for better tile edge clipping
  • SRID-aware envelope transformation - Transform tile envelope to table SRID instead of every geometry
  • Moka-based tile cache - LRU cache with configurable size and TTL

Feature Comparison

Featuretileserver-rstileserver-glmartin
LanguageRustNode.jsRust
PMTiles
MBTiles
MLT (MapLibre Tiles)
PostGIS
Raster Rendering✅ Native✅ Node
Static Images
PMTiles Req/sec1,4091,27453
MBTiles Req/sec736756876
PostGIS Req/sec3,596-3,674

tileserver-rs provides the best balance: fastest PMTiles performance (~10% faster than tileserver-gl, 26x faster than martin), matching PostgreSQL performance (both PostGIS-bound at ~3,600 req/s), native MapLibre rendering for raster tiles, static image generation - all in a single binary.

COG/Raster Performance

tileserver-rs supports Cloud Optimized GeoTIFF (COG) serving with on-the-fly reprojection and PNG encoding via GDAL.

Test Configuration

  • COG File: 4096x4096 RGB image in Web Mercator (EPSG:3857)
  • Connections: 10 concurrent (COG processing is CPU-intensive)
  • Output: 256x256 PNG tiles

COG Benchmark Results (tileserver-rs)

ZoomRequests/secThroughputAvg LatencyP99 Latency
z02 req/s276 KB/s2,568ms4,343ms
z17 req/s1 MB/s1,349ms2,261ms
z220 req/s3.4 MB/s478ms1,177ms
z338 req/s7.6 MB/s258ms595ms

Key Observations

  • Latency scales with tile complexity - Lower zoom levels read more data from the COG
  • Higher zoom = faster - z3+ tiles achieve 38+ req/s with sub-300ms latency
  • CPU-bound - COG processing (GDAL read + PNG encode) limits throughput
  • LZW compression - The benchmark COG uses LZW compression; uncompressed COGs are faster

Running COG Benchmarks

# Test COG performance only
node run-benchmarks.js --format cog --connections 10

# Compare with TiTiler (Python COG server)
docker compose -f benchmarks/docker-compose.yml up -d titiler tileserver-rs
node run-benchmarks.js --format cog --server tileserver-rs
node run-benchmarks.js --format cog --server titiler

TiTiler Comparison

TiTiler is a Python-based COG tile server by Development Seed, built on rio-tiler/GDAL and FastAPI. We benchmarked both servers with the same COG file (4096×4096 RGB, EPSG:3857) at 10 concurrent connections.

ServerAvg Req/secAvg LatencyMemory UsageNotes
TiTiler184 req/s54ms200 MBRio-tiler/GDAL, Python (FastAPI)
tileserver-rs19 req/s1,217ms624 MBGDAL-based, Rust

COG Performance by Zoom Level

Zoomtileserver-rsTiTilerWinner
z03 req/s (3,016ms)157 req/s (63ms)TiTiler
z15-8 req/s (1,174-2,054ms)170-194 req/s (51-58ms)TiTiler
z222-23 req/s (419-453ms)196-199 req/s (50ms)TiTiler
z353 req/s (189ms)187 req/s (53ms)TiTiler
Info

Honest assessment: TiTiler wins on pure COG serving — it's purpose-built for raster data with a mature rio-tiler/rasterio stack optimized specifically for Cloud Optimized GeoTIFF access patterns. tileserver-rs COG support uses raw GDAL bindings which haven't been optimized to the same degree yet.

Why tileserver-rs Still Wins Overall

TiTiler only serves COG/raster data. tileserver-rs is a unified tile server that handles everything in a single binary:

Capabilitytileserver-rsTiTiler
PMTiles (vector)✅ 1,409 req/s
MBTiles (vector)✅ 736 req/s
PostGIS (vector)✅ 3,337 req/s
COG/Raster✅ 19 req/s✅ 184 req/s
MLT Transcoding
Native Raster Rendering✅ MapLibre Native
Static Map Images
Style JSON Serving
Font Serving
Hot Reload
Success

Bottom line: If you only need COG serving, TiTiler is excellent. If you need a unified tile server that handles vector tiles, raster rendering, PostGIS, static images, and COG — all in a single binary with 1,400+ req/s PMTiles performance — tileserver-rs is the clear choice.

Run docker compose -f benchmarks/docker-compose.yml up -d to start both servers for comparison.

Optimization Tips

  1. Use release builds - cargo build --release is 10-50x faster than debug
  2. Use PMTiles for production - cloud-native, HTTP range request friendly
  3. Use MBTiles for development - easy to generate and inspect with SQLite tools
  4. Enable compression - gzipped tiles reduce bandwidth significantly
  5. Use CDN caching - tiles are immutable, cache with long TTLs
  6. Match connections to cores - more connections than CPU cores adds overhead
  7. For COG serving - use internal tiling and overviews for faster access