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 (Not Yet Available)

Convert existing MVT/PBF sources to MLT format on-the-fly. This phase is pending the mlt-core crate adding an encoding API (currently only decoding is supported in v0.1.x).

Phase 3: MLT → MVT (Feature-Gated)

Convert MLT tiles to MVT/PBF format on-the-fly for backward compatibility with clients that don't yet support MLT. This requires the mlt cargo feature flag.

# Build with MLT transcoding support
cargo build --release --features mlt

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

With the mlt feature enabled, 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 and the mlt feature is enabled, 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🚧 Pending (Phase 2, awaiting mlt-core encoding API)
Same formatSame format✅ Passthrough (no conversion)

Building from Source

MLT transcoding is an optional feature that adds the following dependencies:

  • mlt-core — MLT tile parsing and decoding
  • prost — Protobuf encoding for MVT generation
  • geo-types — Geometry types used by mlt-core
# Without MLT transcoding (passthrough still works)
cargo build --release

# With MLT transcoding
cargo build --release --features mlt

# With all optional features
cargo build --release --features http,mlt

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 --features mlt --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 --features mlt
  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

This error occurs when requesting MVT→MLT conversion, which is not yet supported:

400 Bad Request: Transcoding from pbf to mlt is not supported

This will be resolved when mlt-core adds an encoding API in a future release.

Next Steps