# PMTiles Export [PMTiles](https://github.com/protomaps/PMTiles) packs an entire tileset into a single file that clients read with HTTP range requests. Put it on S3, R2, or any static host and a map works with no tile server, no SQLite, and no running process. ```bash tilefeed export basemap.mbtiles basemap.pmtiles ``` ``` PMTiles archive: basemap.pmtiles Zoom levels: 0-14 Tiles: 41532 Directory entries: 38104 (3428 saved by runs) Distinct blobs: 31905 (6199 deduplicated) Leaf directories: 4 Size: 184203841 bytes Verified: 64 tiles re-read and compared against the source ``` The command reads an MBTiles file directly, so it needs neither the database nor the config — it composes with `inspect` and `diff` the same way. It refuses to write over its own input. ## Serving it Upload the file and point a client at it. Nothing else is required: ```bash aws s3 cp basemap.pmtiles s3://my-bucket/ --content-type application/octet-stream ``` ```html ``` The bucket must allow range requests (S3 and R2 do by default) and CORS if the page is on another origin. ## What the export does **Deduplicates.** Tiles with identical bytes are stored once. Ocean, empty land, and unchanged areas at low zoom collapse hard — the `Distinct blobs` line reports how many were saved. **Collapses runs.** Consecutive tile IDs sharing one blob become a single directory entry with a run length, rather than one entry each. **Orders tiles along a Hilbert curve.** Tiles that are near each other on the map end up near each other in the file, so a client panning around tends to hit bytes it has already fetched. **Builds leaf directories when needed.** The header and root directory have to fit in 16 KiB so a client can fetch both in one request. Small tilesets need only a root; past that, entries are split into leaves and the root indexes them. `Leaf directories: 0` means everything fit in the root. ## Format notes The writer implements [PMTiles v3](https://github.com/protomaps/PMTiles/blob/main/spec/v3/spec.md): a 127-byte header, then the root directory, JSON metadata, leaf directories, and tile data. Directories are stored as varint columns and gzipped. - **Tile type** is always MVT. - **Tile compression is always gzip.** One MBTiles can hold both raw and gzipped tiles — Tippecanoe with `no_tile_compression` writes raw, while incremental updates always gzip — and PMTiles has a single header field describing all of them. Raw tiles are compressed on the way in so the header is never a lie. - **Metadata** comes from the MBTiles `metadata` table. The `json` blob that Tippecanoe writes is flattened into the top level, so `vector_layers` lands where clients expect. - **Bounds and center** come from the MBTiles metadata, falling back to the whole world. - Archives are marked **clustered**, since tile data is written in tile-ID order. Every archive is read back before `export` reports success. Two levels of check: - **Structural** — the header parses, directories parse, and every entry points inside the tile data section. - **Against the source** — up to 64 tiles spread across the archive are re-read and compared byte-for-byte with the MBTiles they came from. A structural check alone cannot catch a tile written at the wrong coordinate: such an archive is perfectly self-consistent and completely wrong. The `Verified:` line in the output reports how many tiles were compared. ## Verifying an archive The writer was checked against the reference implementation by hand during development — the Python `pmtiles` package reads its archives, and every tile ID matches `zxy_to_tileid` across z0–z15. The repository's own tests do not depend on that package, so you can repeat the check yourself: ```bash pip install pmtiles pmtiles-show basemap.pmtiles # header and metadata pmtiles-show basemap.pmtiles 14 8188 5448 # one tile ``` ## Limits - Tile blobs are streamed through a staging file next to the output, so memory scales with the *number* of tiles (a directory entry each) rather than their size. Expect roughly 40 bytes per tile plus the tile currently being processed — a 164 MB archive of 40,000 tiles peaks at about 28 MB. - The source is opened read-only, so exporting from read-only media or an immutable artifact works and never leaves `-wal`/`-shm` files behind. - One level of leaf directories, which the spec recommends and which covers tilesets into the tens of millions of tiles. - Export is a point-in-time snapshot: incremental updates land in MBTiles, so re-export after an update to refresh a published archive.