Tileserver RS uses a TOML configuration file to define tile sources, styles, and server settings.
Configuration File
Create a config.toml file. Note that root-level options (fonts, files) must come before any [section] headers due to TOML parsing rules:
# Root-level options (must come before [sections])
fonts = "/data/fonts"
files = "/data/files"
[server]
host = "0.0.0.0"
port = 8080
cors_origins = ["*"]
[telemetry]
enabled = false
# endpoint = "http://localhost:4317"
# metrics_enabled = true
# metrics_export_interval_secs = 60
[render]
pool_size = 4
render_timeout_secs = 30
[[sources]]
id = "openmaptiles"
type = "pmtiles"
path = "/data/tiles.pmtiles"
name = "OpenMapTiles"
attribution = "© OpenMapTiles © OpenStreetMap contributors"
[[sources]]
id = "terrain"
type = "mbtiles"
path = "/data/terrain.mbtiles"
name = "Terrain Data"
[[styles]]
id = "osm-bright"
path = "/data/styles/osm-bright/style.json"
Root-level keys like fonts and files must appear before any section headers ([server], [telemetry], etc.) in the TOML file. Otherwise they will be incorrectly parsed as part of the preceding section.
Server Configuration
| Option | Description | Default |
|---|---|---|
host | IP address to bind to | 0.0.0.0 |
port | Port number | 8080 |
cors_origins | Allowed CORS origins | ["*"] |
admin_bind | Admin server bind address for hot-reload | 127.0.0.1:0 (disabled) |
upload_dir | Directory for uploaded files | - |
upload_max_size_mb | Maximum upload file size in MB | 500 |
Admin Server
The admin server exposes POST /__admin/reload on a separate port for hot-reloading configuration without restarting the server.
[server]
admin_bind = "127.0.0.1:9099"
When admin_bind is set to anything other than 127.0.0.1:0, the admin server starts on that address. You can also trigger a reload by sending SIGHUP to the server process.
See the Hot Reload Guide for details.
File Uploads
Enable file uploads for the drag-and-drop visualization feature:
[server]
upload_dir = "/data/uploads"
upload_max_size_mb = 500
When upload_dir is set, the server exposes upload endpoints (POST /api/upload, GET /api/upload, DELETE /api/upload/{id}) for streaming geospatial files (MBTiles, SQLite, COG) to disk. Uploaded files are registered as temporary tile sources — they persist until deleted or the server restarts.
See the File Drop Guide and Upload API reference for details.
Source Configuration
File-based sources (PMTiles, MBTiles, GeoParquet) are configured in [[sources]] arrays. PostgreSQL sources are configured separately in [postgres].
Each file source requires:
| Option | Description | Required |
|---|---|---|
id | Unique identifier | Yes |
type | pmtiles, mbtiles, geoparquet, or duckdb | Yes |
path | Path to tile file (local or URL) | Yes |
name | Display name | No |
attribution | Map attribution | No |
PMTiles Sources
[[sources]]
id = "world"
type = "pmtiles"
path = "/data/world.pmtiles"
# Or from a URL (requires http feature)
# path = "https://example.com/tiles.pmtiles"
MBTiles Sources
[[sources]]
id = "local-data"
type = "mbtiles"
path = "/data/local.mbtiles"
GeoParquet Sources
GeoParquet support requires the geoparquet feature flag when building from source.
Serve vector tiles on-the-fly from GeoParquet files — no preprocessing or tile generation step required.
[[sources]]
id = "buildings"
type = "geoparquet"
path = "/data/overture-buildings.parquet"
name = "Overture Buildings"
layer_name = "buildings"
geometry_column = "geometry"
minzoom = 0
maxzoom = 14
| Option | Description | Default |
|---|---|---|
layer_name | MVT layer name | Source id |
geometry_column | Name of the geometry column | Auto-detected |
minzoom | Minimum zoom level | 0 |
maxzoom | Maximum zoom level | 14 |
GeoParquet 1.1 files with bbox covering columns enable fast spatial filtering via row group pruning.
DuckDB Sources
DuckDB support requires the duckdb feature flag when building from source.
Generate vector tiles on-the-fly from SQL queries against embedded DuckDB databases or GeoParquet files.
id = "places" type = "duckdb" path = "/data/overture.duckdb" query = """ SELECT name, geometry FROM places WHERE bbox.xmin <= {bbox_xmax} AND bbox.xmax >= {bbox_xmin} AND bbox.ymin <= {bbox_ymax} AND bbox.ymax >= {bbox_ymin} """ layer_name = "places"
SQL templates support {z}, {x}, {y}, and {bbox} placeholders that are substituted per tile request. Set path to an empty string for in-memory DuckDB.
PostgreSQL Configuration
PostgreSQL support requires the postgres feature flag when building from source.
Configure PostgreSQL connection and sources in the [postgres] section:
[postgres]
connection_string = "postgresql://user:pass@localhost:5432/tiles"
pool_size = 20
# Table sources (recommended - auto-generates optimized SQL)
[[postgres.tables]]
id = "points"
table = "my_points"
geometry_column = "geom"
minzoom = 0
maxzoom = 14
# Function sources (for custom SQL logic)
[[postgres.functions]]
id = "custom_tiles"
function = "get_tiles"
minzoom = 0
maxzoom = 14
Connection Options
| Option | Description | Default |
|---|---|---|
connection_string | PostgreSQL connection URL | Required |
pool_size | Maximum connections | 20 |
ssl_cert | Path to SSL certificate | - |
ssl_key | Path to SSL key | - |
ssl_root_cert | Path to SSL root certificate | - |
Table Sources
Table sources auto-discover geometry columns and generate optimized tile queries with spatial index filtering.
[[postgres.tables]]
id = "buildings"
schema = "public"
table = "buildings"
geometry_column = "geom" # Optional, auto-detected
id_column = "id" # Optional, for feature IDs
properties = ["name", "type"] # Optional, defaults to all columns
minzoom = 0
maxzoom = 14
bounds = [-180, -85, 180, 85] # Optional, auto-detected
extent = 4096 # MVT extent (default: 4096)
buffer = 64 # Tile buffer in pixels (default: 64)
max_features = 10000 # Optional feature limit per tile
| Option | Description | Default |
|---|---|---|
id | Unique source identifier | Required |
schema | PostgreSQL schema | public |
table | Table name | Required |
geometry_column | Geometry column name | Auto-detected |
id_column | Column for feature IDs | - |
properties | Columns to include | All non-geometry columns |
minzoom | Minimum zoom level | 0 |
maxzoom | Maximum zoom level | 22 |
bounds | Bounds [west, south, east, north] | Auto-detected |
extent | MVT tile extent | 4096 |
buffer | Tile buffer in pixels | 64 |
max_features | Max features per tile | Unlimited |
Ensure your geometry column has a spatial index (GIST) for optimal performance. The server will warn if no index is found.
Function Sources
Function sources call PostgreSQL functions that return MVT tiles directly.
[[postgres.functions]]
id = "dynamic_tiles"
schema = "public"
function = "get_tiles"
minzoom = 0
maxzoom = 14
bounds = [-180, -85, 180, 85]
The function must have one of these signatures:
-- Simple (z, x, y)
CREATE FUNCTION get_tiles(z integer, x integer, y integer)
RETURNS bytea AS $$ ... $$ LANGUAGE plpgsql;
-- With query parameters (z, x, y, query)
CREATE FUNCTION get_tiles(z integer, x integer, y integer, query json)
RETURNS bytea AS $$ ... $$ LANGUAGE plpgsql;
| Option | Description | Default |
|---|---|---|
id | Unique source identifier | Required |
schema | PostgreSQL schema | public |
function | Function name | Required |
minzoom | Minimum zoom level | 0 |
maxzoom | Maximum zoom level | 22 |
bounds | Bounds [west, south, east, north] | - |
Table vs Function Sources
| Aspect | Table Source | Function Source |
|---|---|---|
| Setup | Minimal config | Requires SQL function |
| Performance | Optimized (uses spatial index) | Depends on function |
| Flexibility | Fixed schema | Custom SQL logic |
| Use case | Standard tables | Complex queries, joins |
Style Configuration
[[styles]]
id = "bright"
path = "/data/styles/bright/style.json"
name = "Bright Style" # Optional display name
Styles should include sprites alongside the style.json:
styles/
└── bright/
├── style.json
├── sprite.json
├── sprite.png
├── [email protected]
└── [email protected]
Font Configuration
Fonts are required for rendering text labels. Configure the fonts directory:
fonts = "/data/fonts"
The fonts directory should contain subdirectories for each font family with PBF glyph files:
fonts/
├── Noto Sans Regular/
│ ├── 0-255.pbf
│ ├── 256-511.pbf
│ └── ...
├── Noto Sans Medium/
│ ├── 0-255.pbf
│ └── ...
└── Open Sans Bold/
└── ...
Font glyph PBF files can be generated using tools like node-fontnik or downloaded from OpenMapTiles fonts.
Static Files Configuration
Optionally serve static files from a directory:
files = "/data/files"
Files in this directory will be accessible at /files/{filepath}. This is useful for:
- GeoJSON overlays
- Custom marker icons
- Other static assets
Telemetry Configuration
tileserver-rs supports OpenTelemetry for exporting traces and metrics via OTLP gRPC.
[telemetry]
enabled = true
endpoint = "http://localhost:4317"
service_name = "tileserver-rs"
sample_rate = 1.0
metrics_enabled = true
metrics_export_interval_secs = 60
| Option | Description | Default |
|---|---|---|
enabled | Enable OpenTelemetry (traces + metrics) | false |
endpoint | OTLP gRPC collector endpoint | http://localhost:4317 |
service_name | Service name for traces and metrics | tileserver-rs |
sample_rate | Trace sampling rate (0.0 to 1.0) | 1.0 |
metrics_enabled | Enable metrics export (requires enabled = true) | true |
metrics_export_interval_secs | How often metrics are pushed to the collector | 60 |
When enabled, the following metrics are exported:
| Metric | Type | Unit | Description |
|---|---|---|---|
http.server.request.count | Counter | requests | Total HTTP requests |
http.server.request.duration | Histogram | seconds | Request duration |
http.server.response.body.size | Histogram | bytes | Response body size |
Each metric includes attributes: http.request.method, http.response.status_code, url.path.
When telemetry is disabled (the default), metrics recording has zero overhead — all instruments are no-ops.
See the Telemetry Guide for setup examples with Grafana, Jaeger, and other backends.
Render Pool Configuration
Configure the native MapLibre renderer pool for server-side raster tile generation:
[render]
pool_size = 4
render_timeout_secs = 30
| Option | Description | Default |
|---|---|---|
pool_size | Number of concurrent renderer worker threads | 4 |
render_timeout_secs | Render timeout in seconds — requests exceeding this fail | 30 |
Each worker thread maintains persistent MapLibre Native instances with their own EGL contexts. Increase pool_size for higher concurrent raster tile throughput.
The [render] section is optional. When omitted, the renderer pool starts with default values. If the server was built without MapLibre Native (stub mode), the pool initializes but returns placeholder images.
Environment Variables
| Variable | Description | Default |
|---|---|---|
RUST_LOG | Log level (error, warn, info, debug, trace) | info |
CONFIG_PATH | Path to config file | config.toml |
HOST | Override server host | - |
PORT | Override server port | - |
CLI Options
tileserver-rs --help
Usage: tileserver-rs [PATH] [OPTIONS]
Arguments:
[PATH] Path to a tile file or directory to auto-detect sources/styles from
Options:
-c, --config <FILE> Path to configuration file [env: TILESERVER_CONFIG]
--host <HOST> Host to bind to [env: TILESERVER_HOST]
-p, --port <PORT> Port to bind to [env: TILESERVER_PORT]
--public-url <URL> Public URL for tile URLs in TileJSON [env: TILESERVER_PUBLIC_URL]
--ui Enable the web UI (default: true) [env: TILESERVER_UI]
--no-ui Disable the web UI
-v, --verbose Enable verbose logging
-h, --help Print help
-V, --version Print version
Config Resolution Priority
The server resolves configuration in this order:
--config <FILE>— explicit config path (fails if file doesn't exist)- Positional
PATH— auto-detect sources/styles from that path - Default locations:
./config.toml,/etc/tileserver-rs/config.toml - CWD auto-detect — scan the current directory
Zero-config auto-detect discovers .pmtiles, .mbtiles, style.json, fonts directories, and GeoJSON files. See the Auto-Detect Guide for details.
Cargo Features
When building from source, you can enable optional features:
| Feature | Description | Default |
|---|---|---|
postgres | PostgreSQL tile sources (tables + functions) | Yes |
raster | GDAL-based raster/COG tile support | Yes |
mlt | MLT transcoding (MLT to MVT and MVT to MLT) | Yes |
geoparquet | Serve tiles from GeoParquet files on-the-fly | No |
frontend | Embed the Nuxt web UI into the binary | No |
| Feature | Description | Default |
| ---------- | ----------------------------------------------- | ------- |
postgres | PostgreSQL tile sources (tables + functions) | Yes |
raster | GDAL-based raster/COG tile support | Yes |
mlt | MLT transcoding (MLT to MVT and MVT to MLT) | Yes |
duckdb | SQL-driven tile generation via embedded DuckDB | No |
frontend | Embed the Nuxt web UI into the binary | No |
# API-only (no web UI, no GDAL, no PostgreSQL):
cargo build --release --no-default-features
# Default features (postgres + raster + mlt, no web UI):
cargo build --release
# Everything including the web UI:
cargo build --release --all-features
The frontend feature is intentionally not a default feature so the backend can be compiled without building the Nuxt frontend first. Pre-built binaries, Docker images, and CI release builds always include it.