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 (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.
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
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 | ✅ Supported (Phase 2) |
| Same format | Same 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 encodingprost— Protobuf encoding for MVT generationgeo-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):
| 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 MLT support (enabled by default since v2.11.1)
- 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
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
- Serving Vector Tiles — General vector tile serving guide
- Configuration Reference — All config options
- MapLibre Tile Spec — MLT format specification