# tileserver-gl: a self-hosted map tile server with server-side raster rendering

> TileServer GL serves vector and raster tiles from MBTiles files and GL style JSON, rendering raster images on the server with MapLibre GL Native. It suits teams that already have OpenMapTiles data and want a WMTS endpoint without running a full GIS stack.

**maptiler/tileserver-gl** — Vector and raster maps with GL styles. Server side rendering by MapLibre GL Native. Map tile server for MapLibre GL JS, Android, iOS, Leaflet, OpenLayers, GIS via WMTS, etc.

- Repository: https://github.com/maptiler/tileserver-gl
- Website: https://tileserver.readthedocs.io/en/latest/
- Stars: 2,905 · Forks: 712
- Language: JavaScript
- License: NOASSERTION
- Published: 2026-09-24 · Updated: 2026-09-24 · Language: en
- Canonical page: https://hysenlabs.com/projects/maptiler-tileserver-gl

## What tileserver-gl does that a plain static file server does not

A directory of PNG tiles can be served by nginx. TileServer GL exists for the case where the tiles are not PNGs yet. You hold vector tiles in an MBTiles file plus a GL style JSON, and clients such as MapLibre GL JS, Leaflet, OpenLayers, Android or iOS need either the raw vector tiles or raster images produced from them. The package description in package.json puts it plainly: a map tile server for JSON GL styles, serving vector tiles and server side generated raster tiles.

The audience is narrower than the tagline suggests. If you have no tiles, this project gives you nothing to serve. The README points readers to OpenMapTiles to download vector tiles, which tells you where the data is expected to come from. The second audience is GIS clients that speak WMTS, since the topics list includes wmts alongside docker and gl-styles. A third group is anyone who wants the MapLibre demo pages and inspection tools running locally, since the build copies maplibre-gl, maplibre-gl-inspect, leaflet and mapbox-gl-rtl-text into public/resources.

## Two packages, one difference: where rasterization happens

The repository ships two distributions. The main tileserver-gl package renders raster tiles on the server using MapLibre GL Native, which means native libraries: the Dockerfile installs libcairo2-dev, libpango1.0-dev, libpng-dev, libjpeg-dev, libgif-dev, librsvg2-dev, libglfw3-dev and libuv1-dev before it can build. The alternative, tileserver-gl-light, is described in the README as pure javascript with no native dependencies, able to run anywhere, but without server-side rasterization through MapLibre GL Native. The Docker Hub image maptiler/tileserver-gl-light:latest mirrors that split.

That is the whole trade-off, and it is worth stating without hedging. Light runs on a host where you cannot install a Cairo and Pango toolchain. In exchange, any client that expects a raster tile URL, including WMTS clients that do not render vector tiles themselves, has nothing to consume. The Dockerfile also rebuilds canvas from source with npm_config_build_from_source=true for Ubuntu Noble, which hints at how fragile the prebuilt binary path can be on newer distributions.

## Installing tileserver-gl with npm and serving your first MBTiles file

The README requires Node.js 20 or above, with Node 24 recommended, and warns that running without Docker needs the native dependencies installed first. Check your version before anything else.

```bash
node -v
```

The README says this should print something like v24.x.x. If it does not, install a newer Node before continuing, because the global install will not give you a working raster renderer otherwise.

With Node in place, the global install is a single command.

```bash
npm install -g tileserver-gl
```

Now fetch the sample data the README uses and start the server against it.

```bash
wget https://github.com/maptiler/tileserver-gl/releases/download/v1.3.0/zurich_switzerland.mbtiles
tileserver-gl --file zurich_switzerland.mbtiles
```

The README says to visit http://[server ip]:8080 in a browser. Port 8080 is the default the examples use throughout. If you want the config.json route instead, the README downloads test_data.zip, unzips it, and runs tileserver-gl with no arguments, which picks up the configuration in the current directory.

The Docker path avoids the native toolchain entirely. From the directory holding your data:

```bash
docker run --rm -it -v $(pwd):/data -p 8080:8080 maptiler/tileserver-gl:latest --file zurich_switzerland.mbtiles
```

The container mounts your working directory at /data and publishes 8080. For a config file kept elsewhere, the README substitutes the host path: docker run --rm -it -v /your/local/config/path:/data -p 8080:8080 maptiler/tileserver-gl:latest.

## Host header poisoning and the two settings that close it

The security section of the README is the most specific operational guidance in the repository, and it describes a real failure mode rather than a theoretical one. When the server starts without --public_url, the URLs it emits in WMTS responses, TileJSON and style JSON are constructed from the request's Host and X-Forwarded-* headers. If an attacker can influence those headers, the server returns URLs pointing at a host the attacker controls, and clients that follow those URLs are sent elsewhere.

The mitigation is to stop deriving the host from the request. Setting --public_url to your canonical address does that:

```bash
tileserver-gl --public_url https://your-domain.com/ --file your.mbtiles
```

The second option keeps request-derived hosts but constrains them. TILESERVER_GL_ALLOWED_HOSTS takes a comma-separated list, and the README notes the default is *, meaning no restriction. If the request host is not on the list, the server returns path-only URLs instead of absolute ones, so a poisoned host cannot propagate.

```bash
export TILESERVER_GL_ALLOWED_HOSTS="localhost,map.example.com"
tileserver-gl --file your.mbtiles
```

My reading is that --public_url is the cleaner choice for a fixed deployment and the environment variable is for setups where the same instance answers on several names. Leaving both unset is defensible only on a host nobody else can reach.

## Where tileserver-gl stops being the right tool

This is a serving layer, not a data layer. Everything downstream of your MBTiles file is handled; everything upstream is not. If your tiles are still in PostGIS, or you need to generate them from shapefiles on a schedule, tileserver-gl has no part in that work, and the README does not claim otherwise.

The native dependency chain is the second boundary. The Dockerfile installs a long list of development headers and rebuilds canvas from source, which is a signal that the raster path is sensitive to the base image. A team that cannot pin an Ubuntu 24.04 based image, or that runs on a platform where those libraries are unavailable, is pushed to the light package and loses raster output. That is a functional loss, not a cosmetic one.

Third, the release history in the repository shows pre-release tags: v5.7.0-pre.1, v5.7.0-pre.0, v5.6.1-pre.0. A project publishing pre-releases as its most recent tags is telling you something about the stability of the surface you are installing. The last push to the default branch was on 2026-09-10, so the code is moving, but check which version your package manager resolves before you build a deployment around it.

Finally, if you need a browser-based editor for styles or data, this is not it. The bundled MapLibre and Leaflet assets under public/resources are viewers and inspection tools, not authoring software.

## tileserver-gl compared with Martin and with MapTiler Server

Martin is the comparison people search for, and the difference is architectural. Martin is a Rust tile server that reads directly from a PostgreSQL or PostGIS database and serves vector tiles from PostGIS functions and tables. TileServer GL reads MBTiles files and GL style JSON, and adds server-side raster rendering through MapLibre GL Native. If your data lives in PostGIS and you want tiles straight from the database, Martin matches that shape and tileserver-gl does not. If you have MBTiles and need raster images or WMTS, the reverse holds.

The other alternative is named in the README itself: MapTiler Server, described there as a map server with easy setup and a user-friendly interface. That is the commercial path, with a graphical setup and support behind it. The difference is not capability so much as who operates it. Self-hosting tileserver-gl means you own the Docker image, the native libraries and the host header configuration. MapTiler Server moves that work to a vendor. Neither is wrong; they suit different teams.

## Licence, maintenance and what an upgrade costs

The repository's licence field resolves as NOASSERTION, and the top-level entries include LICENSE.md, so the terms exist in the tree but are not expressed in a form the metadata recognises. Read LICENSE.md directly before you redistribute the software or ship it inside a product. Nothing here is legal advice, and the distinction between the main package and tileserver-gl-light may matter if the two carry different terms.

Maintenance looks current. The last push to master was on 2026-09-10, and the most recent tags are from 2026-08-31 and earlier in the year. The repository is not archived. That said, the tag list is dominated by pre-releases, so the practical upgrade question is which channel you follow.

The upgrade cost concentrates in two places. First, Node: the README requires 20 or above and recommends 24, and the Dockerfile pins node_24.x, so a major Node bump means revisiting your base image. Second, the native libraries: the Dockerfile rebuilds canvas from source for Ubuntu Noble, and a distribution change can break that build. The package.json prepare script copies maplibre-gl, maplibre-gl-inspect, mapbox-gl-rtl-text, leaflet and leaflet-hash into public/resources, so those front-end assets move with the package version too. Pinning the Docker image tag rather than tracking latest is the lower-risk habit here.

## Conclusion

Adopt tileserver-gl if you already hold MBTiles data, need WMTS or TileJSON endpoints, and can run the native MapLibre GL Native dependencies; pick tileserver-gl-light instead when you only serve vector tiles and cannot install libcairo, libpango and the rest. Do not adopt it expecting a map editing interface or a data pipeline. Before rollout, verify that your deployment sets --public_url or TILESERVER_GL_ALLOWED_HOSTS, because the README states that without them response URLs are built from request headers. Then confirm the exact version you install, since the releases listed are pre-releases such as v5.7.0-pre.1.

## FAQ

### how to install tileserver gl

The README gives two routes. With Node.js 20 or above installed, run npm install -g tileserver-gl, which needs the native dependencies in place first. Alternatively run the Docker image maptiler/tileserver-gl:latest with your data directory mounted at /data and port 8080 published.

### what is tileserver gl

It is a map tile server for JSON GL styles, serving vector tiles and server-side generated raster tiles. It renders raster tiles with MapLibre GL Native and exposes them to MapLibre GL JS, Leaflet, OpenLayers, mobile SDKs and WMTS clients.

### tileserver gl vs tileserver gl light

The light package is pure JavaScript with no native dependencies and runs anywhere, but it does not include server-side rasterization through MapLibre GL Native. The main package adds that raster rendering and requires the native libraries the Dockerfile installs.

### is tileserver gl free

The repository metadata reports the licence as NOASSERTION and the tree contains a LICENSE.md file, so the terms are defined in the repository rather than by a recognised identifier. Read LICENSE.md before redistributing or embedding it.

### tileserver gl vs geoserver

A direct comparison is not something this article can support, because GeoServer is not described in the repository. What can be said is that tileserver-gl serves MBTiles files and GL style JSON, and its README points to OpenMapTiles for obtaining vector tiles.

## Sources

- [Issues](https://github.com/maptiler/tileserver-gl/issues)
- [maptiler/tileserver-gl on GitHub](https://github.com/maptiler/tileserver-gl)
- [Project website](https://tileserver.readthedocs.io/en/latest/)
- [README](https://github.com/maptiler/tileserver-gl/blob/master/README.md)
- [Releases](https://github.com/maptiler/tileserver-gl/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/maptiler-tileserver-gl
