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.
| Feature | MLT | MVT |
|---|---|---|
| Encoding | Column-oriented binary | Protobuf |
| Compression | Built-in, per-column | External (gzip) |
| Specification | MapLibre Tile Spec | Mapbox Vector Tile v2.1 |
| File extension | .mlt | .pbf / .mvt |
| Content-Type | application/vnd.maplibre-vector-tile | application/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
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:
- The
get_tilehandler detects the format mismatch (e.g.,.pbfrequested from MLT source) - The tile data is decompressed if gzip-encoded
- MLT tile bytes are parsed using
mlt-core::parse_layers()and decoded - Decoded layers are converted to an intermediate
FeatureCollection - The collection is re-encoded as an MVT protobuf tile using
prost - The transcoded tile is returned with the correct
Content-Type
Supported Conversions
| From | To | Status |
|---|---|---|
| MLT | MVT/PBF | ✅ Supported (Phase 3) |
| MVT/PBF | MLT | 🚧 Pending (Phase 2, awaiting mlt-core encoding API) |
| Same format | Same format | ✅ Passthrough (no conversion) |
Building from Source
MLT transcoding is an optional feature that adds the following dependencies:
mlt-core— MLT tile parsing and decodingprost— Protobuf encoding for MVT generationgeo-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):
| Zoom | Throughput |
|---|---|
| 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):
| Zoom | Throughput |
|---|---|
| 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:
| Zoom | Throughput | Time 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
| Input | Time |
|---|---|
| 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_parseandmlt_decode_allbenchmarks 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.
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:
- Feature flag: Ensure tileserver-rs was built with
--features mlt - Server logs: Look for transcoding warnings (
WARNlevel) - 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
- Serving Vector Tiles — General vector tile serving guide
- Configuration Reference — All config options
- MapLibre Tile Spec — MLT format specification