From a local vector file to a live slippy map in one command — on-the-fly Mapbox Vector Tiles with a built-in MapLibre preview.
tile-server-lite is a minimal FastAPI server that reads any
geopandas-readable vector file — GeoPackage, GeoParquet, GeoJSON,
Shapefile — and serves it as XYZ Mapbox Vector Tiles (MVT), cut on the fly. Point it at a file,
open your browser, and you get a working MapLibre GL JS map fit to your
data's bounds.
Maintained by python-geospatial.com, a knowledge base for the modern Python geospatial stack.
Static vector tile pipelines (Tippecanoe, PMTiles) are the right call for large, published datasets.
But while you are exploring — checking a fresh export, sharing a quick preview, iterating on a
layer — pre-baking tiles is friction. tile-server-lite cuts tiles per request straight from your
file, so there is no build step between "I have a file" and "I have a map".
It complements, rather than replaces, the static PMTiles workflow covered on the site: use this server for local development and previews, then bake to PMTiles for production hosting (see How it works).
Not published to PyPI — install straight from the repository:
pip install "git+https://github.com/python-geospatial/tile-server-lite.git"Or clone and install in editable mode:
git clone https://github.com/python-geospatial/tile-server-lite.git
cd tile-server-lite
pip install -e ".[dev]"Serve a single GeoPackage:
tile-server-lite serve parcels.gpkgServe several files at once (each layer is named after its file stem), on a chosen host/port and zoom window:
tile-server-lite serve parcels.gpkg sensors.geoparquet \
--host 0.0.0.0 --port 8080 --min-zoom 6 --max-zoom 18Example startup output:
tile-server-lite
┏━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ Layer ┃ Geometry ┃ Features ┃ TileJSON ┃
┡━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│ parcels │ Polygon │ 1 240 │ http://127.0.0.1:8080/tilejson/parcels… │
│ sensors │ Point │ 317 │ http://127.0.0.1:8080/tilejson/sensors… │
└─────────┴──────────┴──────────┴──────────────────────────────────────────┘
Preview map: http://127.0.0.1:8080/ · Served by python-geospatial.com
Then:
- open http://127.0.0.1:8080/ for the MapLibre preview,
- fetch a tile at
GET /tiles/parcels/16/35208/21489.mvt, - fetch the TileJSON at
GET /tilejson/parcels.json.
Example TileJSON response (abridged):
{
"tilejson": "3.0.0",
"name": "parcels",
"scheme": "xyz",
"tiles": ["http://127.0.0.1:8080/tiles/parcels/{z}/{x}/{y}.mvt"],
"minzoom": 6,
"maxzoom": 18,
"bounds": [13.4028, 52.5185, 13.4098, 52.5232],
"vector_layers": [
{"id": "parcels", "geometry_type": "Polygon",
"fields": {"parcel_id": "Integer", "zoning": "String"}}
]
}A single-file Dockerfile is included. Build the image, then mount a directory of vector files at
/data:
docker build -t tile-server-lite .
docker run --rm \
-v $(pwd)/data:/data \
-p 8080:8080 \
tile-server-lite serve /data/parcels.gpkg --host 0.0.0.0 --port 8080The map is then live at http://localhost:8080/. Binding to 0.0.0.0
inside the container lets the published port reach the server; keep it on 127.0.0.1 when running
directly on your machine.
- Any geopandas-readable input — GeoPackage, GeoParquet, GeoJSON, Shapefile, and more.
- True MVT — geometries clipped to each tile (with an edge buffer) and encoded with
mapbox-vector-tilein the standard 0–4096 grid. - Spatial-index-backed selection — only features intersecting a tile are considered.
- Valid TileJSON 3.0.0 — with 4326 bounds, zoom window, and
vector_layersmetadata. - Built-in MapLibre preview — served at
/, auto-styled per geometry type, fit to bounds. - Correct CRS handling — reprojects once to Web Mercator for tiling; refuses files with a missing CRS instead of guessing silently.
- One-file Docker deploy and a friendly
click+richCLI.
For each requested z/x/y tile the server:
- computes the tile's EPSG:3857 bounding box with
mercantile, - queries the layer's spatial index for intersecting features,
- clips them to the (buffered) tile box with Shapely,
- affine-transforms the clipped coordinates into the MVT 0–4096 grid (origin top-left, y down),
- encodes the result with
mapbox-vector-tile.
The source file is reprojected to EPSG:3857 exactly once, at load time, because web map tiles are defined in Web Mercator. That is correct here — but do not reuse EPSG:3857 for metric analysis (areas, distances, buffers), where it distorts badly; use a projected/UTM CRS for measurement instead. For production, bake your explored layers into static PMTiles and serve those; this server is for the fast local loop before that step.
Deep dives on the concepts behind this tool, from python-geospatial.com:
- Vector Tile Pipelines with PMTiles
- Generating PMTiles from GeoParquet
- Serving GeoJSON to MapLibre GL JS
- MapLibre GL Vector Web Maps
- Cloud-Native Geospatial Formats
git clone https://github.com/python-geospatial/tile-server-lite.git
cd tile-server-lite
pip install -e ".[dev]"
ruff check .
pytest -qTests generate tiny in-memory geometries in tmp_path and run entirely offline — no network access
and no large fixtures required.
MIT · Copyright (c) 2026 python-geospatial.com