Documentation

This guide covers MLT (MapLibre Tiles) support in tileserver-rs, including serving MLT tiles directly, on-the-fly transcoding between MLT and MVT formats, and configuration options.

What is MLT?

MLT (MapLibre Tiles) is a next-generation vector tile format from the MapLibre project. It is designed as a more efficient alternative to MVT (Mapbox Vector Tiles / Protobuf), offering improved compression and performance characteristics.

FeatureMLTMVT
EncodingColumn-oriented binaryProtobuf
CompressionBuilt-in, per-columnExternal (gzip)
SpecificationMapLibre Tile SpecMapbox Vector Tile v2.1
File extension.mlt.pbf / .mvt
Content-Typeapplication/vnd.maplibre-vector-tileapplication/x-protobuf

Support Phases

tileserver-rs implements MLT support in three phases:

Phase 1: Passthrough (Always Enabled)

Serve MLT tiles directly from PMTiles or MBTiles sources without any conversion. This works out of the box with no feature flags required.

  • MLT tiles are auto-detected from tile data using the MLT magic bytes
  • PMTiles and MBTiles sources containing MLT tiles are served with the correct Content-Type
  • TileJSON metadata includes encoding: "mlt" for MLT sources

Phase 2: MVT → MLT (Enabled by Default)

Convert existing MVT/PBF sources to MLT format on-the-fly. Request any MVT source as .mlt and tileserver-rs encodes it using mlt-core's encoding API. This is included in the default build — no extra feature flags needed.

Phase 3: MLT → MVT (Enabled by Default)

Convert MLT tiles to MVT/PBF format on-the-fly for backward compatibility with clients that don't yet support MLT. This is also included in the default build.

Info

Since v2.11.1, the mlt feature is enabled by default. You no longer need to pass --features mlt when building from source — MLT transcoding is always available in standard builds.

Configuration

Basic MLT Source

If you have MLT tiles in a PMTiles or MBTiles archive, configure the source normally — tileserver-rs auto-detects the tile format:

[[sources]]
id = "my-mlt-source"
type = "pmtiles"
path = "/data/tiles.pmtiles"
name = "MLT Vector Tiles"

Requesting Different Formats

Clients can request tiles in either format regardless of the source format:

# Request MLT tile (native format if source is MLT)
curl http://localhost:8080/data/my-mlt-source/14/8192/5461.mlt

# Request MVT tile (transcoded on-the-fly if source is MLT)
curl http://localhost:8080/data/my-mlt-source/14/8192/5461.pbf

Tile Endpoints

Get an MLT Tile

GET /data/{source}/{z}/{x}/{y}.mlt
curl http://localhost:8080/data/tiles/10/544/373.mlt -o tile.mlt
# Content-Type: application/vnd.maplibre-vector-tile

Get an MVT Tile (Transcoded from MLT)

GET /data/{source}/{z}/{x}/{y}.pbf

If the source contains MLT tiles, tileserver-rs automatically transcodes to MVT:

curl http://localhost:8080/data/tiles/10/544/373.pbf -o tile.pbf
# Content-Type: application/x-protobuf
Info

If transcoding fails for any reason, the original tile is served as-is with a warning logged. This ensures tile serving remains reliable even if specific tiles have encoding issues.

TileJSON Metadata

MLT sources include additional metadata in their TileJSON response:

{
  "tilejson": "3.0.0",
  "tiles": ["http://localhost:8080/data/tiles/{z}/{x}/{y}.mlt"],
  "encoding": "mlt",
  "format": "mlt"
}

The encoding field tells MapLibre GL JS and other clients that the tiles use MLT encoding.

How Transcoding Works

When a client requests a tile in a format different from the source:

  1. The get_tile handler detects the format mismatch (e.g., .pbf requested from MLT source)
  2. The tile data is decompressed if gzip-encoded
  3. MLT tile bytes are parsed using mlt-core::parse_layers() and decoded
  4. Decoded layers are converted to an intermediate FeatureCollection
  5. The collection is re-encoded as an MVT protobuf tile using prost
  6. The transcoded tile is returned with the correct Content-Type

Supported Conversions

FromToStatus
MLTMVT/PBF✅ Supported (Phase 3)
MVT/PBFMLT✅ Supported (Phase 2)
Same formatSame format✅ Passthrough (no conversion)

Building from Source

MLT transcoding is enabled by default since v2.11.1. The feature adds the following dependencies:

  • mlt-core — MLT tile parsing, decoding, and encoding
  • prost — Protobuf encoding for MVT generation
  • geo-types — Geometry types used by mlt-core
# Default build includes MLT transcoding
cargo build --release

# To explicitly disable MLT transcoding
cargo build --release --no-default-features --features postgres,raster

Performance & Benchmarks

Benchmarks use real OpenMapTiles fixtures from the maplibre-tile-spec repository — the same data used by mlt-core's own Criterion benchmarks. Run them with:

cargo bench --bench mlt

MLT Parse Throughput

Header parsing via mlt_core::parse_layers() (no geometry decode):

ZoomThroughput
z0~4.1 GiB/s
z4~6.2 GiB/s
z7~12.3 GiB/s
z13~3.3 GiB/s

MLT Full Decode Throughput

Parse + decode_all() per layer (geometry + properties):

ZoomThroughput
z0~108 MiB/s
z4~132 MiB/s
z7~87 MiB/s
z13~385 MiB/s

MLT to MVT Transcoding

Full pipeline: parse, decode, FeatureCollection, MVT protobuf encode:

ZoomThroughputTime per tile set
z0 (1 tile, 81 KB)~27 MiB/s~2.9 ms
z4 (2 tiles, 738 KB)~14.7 MiB/s~48 ms
z7 (3 tiles, 710 KB)~11.6 MiB/s~60 ms
z13 (3 tiles, 442 KB)~66 MiB/s~6.4 ms

Format Detection

InputTime
MLT tile~9.8 ns
MVT tile~2.0 ns
Same-format passthrough~7.7 ns

Comparison with Martin and mlt-core

  • mlt-core: Our mlt_parse and mlt_decode_all benchmarks use the identical fixtures and methodology as mlt-core's own Criterion benchmarks, so throughput numbers are directly comparable.
  • Martin: Has no MLT-specific benchmarks. Martin only does passthrough (format detection + correct Content-Type). Our MLT to MVT transcoding capability is unique to tileserver-rs.
Tip

For production use with heavy transcoding load, consider pre-converting your tiles to the target format using offline tools.

Troubleshooting

Transcoding Returns Original Tile

If you request .pbf from an MLT source but get back MLT data, check:

  1. Feature flag: Ensure tileserver-rs was built with MLT support (enabled by default since v2.11.1)
  2. Server logs: Look for transcoding warnings (WARN level)
  3. Tile format: Verify the source actually contains MLT tiles via TileJSON
# Check if MLT support is compiled in (look for "mlt" in features)
curl http://localhost:8080/ping | jq .

"TranscodeUnsupported" Error

If you see a transcoding error, verify the tile format is correct:

# Check TileJSON to see the source format
curl http://localhost:8080/data/my-source.json | jq '.encoding'

Both MLT→MVT and MVT→MLT conversions are supported as of v2.14.0.

Next Steps