Documentation

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"
Warning

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

OptionDescriptionDefault
hostIP address to bind to0.0.0.0
portPort number8080
cors_originsAllowed CORS origins["*"]
admin_bindAdmin server bind address for hot-reload127.0.0.1:0 (disabled)
upload_dirDirectory for uploaded files-
upload_max_size_mbMaximum upload file size in MB500

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:

OptionDescriptionRequired
idUnique identifierYes
typepmtiles, mbtiles, geoparquet, or duckdbYes
pathPath to tile file (local or URL)Yes
nameDisplay nameNo
attributionMap attributionNo

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

Info

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
OptionDescriptionDefault
layer_nameMVT layer nameSource id
geometry_columnName of the geometry columnAuto-detected
minzoomMinimum zoom level0
maxzoomMaximum zoom level14

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

Info

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

OptionDescriptionDefault
connection_stringPostgreSQL connection URLRequired
pool_sizeMaximum connections20
ssl_certPath to SSL certificate-
ssl_keyPath to SSL key-
ssl_root_certPath 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
OptionDescriptionDefault
idUnique source identifierRequired
schemaPostgreSQL schemapublic
tableTable nameRequired
geometry_columnGeometry column nameAuto-detected
id_columnColumn for feature IDs-
propertiesColumns to includeAll non-geometry columns
minzoomMinimum zoom level0
maxzoomMaximum zoom level22
boundsBounds [west, south, east, north]Auto-detected
extentMVT tile extent4096
bufferTile buffer in pixels64
max_featuresMax features per tileUnlimited
Warning

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;
OptionDescriptionDefault
idUnique source identifierRequired
schemaPostgreSQL schemapublic
functionFunction nameRequired
minzoomMinimum zoom level0
maxzoomMaximum zoom level22
boundsBounds [west, south, east, north]-

Table vs Function Sources

AspectTable SourceFunction Source
SetupMinimal configRequires SQL function
PerformanceOptimized (uses spatial index)Depends on function
FlexibilityFixed schemaCustom SQL logic
Use caseStandard tablesComplex 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
    ├── sprite@2x.json
    └── sprite@2x.png

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/
    └── ...
Info

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 two independent observability pipelines: an OTLP push pipeline (OpenTelemetry traces + metrics over gRPC) and a Prometheus pull-based /metrics scrape endpoint. Both are off by default and can run independently.

[telemetry]
# OTLP push pipeline
enabled = true
endpoint = "http://localhost:4317"
service_name = "tileserver-rs"
sample_rate = 1.0
metrics_enabled = true
metrics_export_interval_secs = 60

# Prometheus pull endpoint (independent of `enabled` above)
prometheus_bind = "127.0.0.1:9100"
prometheus_path = "/metrics"
metrics_label_cardinality = "strict"
OptionDescriptionDefault
enabledEnable OpenTelemetry OTLP push (traces + metrics)false
endpointOTLP gRPC collector endpointhttp://localhost:4317
service_nameService name for traces and metricstileserver-rs
sample_rateTrace sampling rate (0.0 to 1.0)1.0
metrics_enabledEnable OTLP metrics push (requires enabled = true)true
metrics_export_interval_secsHow often metrics are pushed to the collector60
prometheus_bindBind address for the Prometheus scrape listener (unset = disabled)unset
prometheus_pathHTTP path for the Prometheus exposition endpoint/metrics
metrics_label_cardinalityLabel cardinality strategy: "strict" (production) or "verbose""strict"

The same instruments feed both pipelines:

MetricTypeUnitLabelsDescription
http_requests_totalcounterroute, status_classHTTP requests by matched route + status
http_request_duration_secondshistogramsroute, status_classPer-request latency
http_requests_in_flightup-downIn-flight HTTP requests
tile_requests_totalcountersource, format, z_bucket, outcomeTile lookups (hit/miss/not_found/error)
tile_request_duration_secondshistogramssource, format, z_bucket, outcomeEnd-to-end tile latency
tile_request_byteshistogramBysource, formatTile response payload size
tile_cache_hits_totalcountersourceIn-process tile cache hits
tile_cache_misses_totalcountersourceIn-process tile cache misses
render_duration_secondshistogramsstyle, formatNative MapLibre raster render duration
render_errors_totalcounterstyle, reasonNative render failures bucketed by reason
Info

When neither enabled = true nor prometheus_bind is set (the default), all instruments are no-ops — recording a metric is a single atomic load with negligible overhead.

Warning

Cardinality matters. metrics_label_cardinality = "strict" (the default) collapses zoom into low (0–6), mid (7–12), high (13+) buckets and drops tile x/y coordinates entirely. Switching to "verbose" exposes raw zoom 0–22, which can blow up Prometheus storage on large globe-scale sources. Use "verbose" only for short-window debugging.

See the Telemetry Guide for setup examples with Grafana, Jaeger, Prometheus, 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
OptionDescriptionDefault
pool_sizeNumber of concurrent renderer worker threads4
render_timeout_secsRender timeout in seconds — requests exceeding this fail30

Each worker thread maintains persistent MapLibre Native instances with their own EGL contexts. Increase pool_size for higher concurrent raster tile throughput.

Info

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.

Tile Cache Configuration

tileserver-rs includes an in-process tile cache backed by moka. When enabled, tiles are cached in memory with byte-weighted eviction and per-entry TTL. This eliminates repeated disk I/O or network calls for frequently requested tiles.

[cache]
enabled = true
max_size_mb = 512
ttl_seconds = 3600
OptionDescriptionDefault
enabledEnable the global tile cachefalse
max_size_mbMaximum cache size in megabytes512
ttl_secondsTime-to-live for cache entries (seconds)3600
Info

The global cache applies to PMTiles, MBTiles, GeoParquet, and DuckDB sources. PostgreSQL sources have their own dedicated cache configured via [postgres.cache].

Warning

Cache is invalidated on config reload. Sending POST /__admin/reload or SIGHUP drops the entire cache along with the old configuration.

Environment Variables

VariableDescriptionDefault
RUST_LOGLog level (error, warn, info, debug, trace)info
CONFIG_PATHPath to config fileconfig.toml
HOSTOverride server host-
PORTOverride 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:

  1. --config <FILE> — explicit config path (fails if file doesn't exist)
  2. Positional PATH — auto-detect sources/styles from that path
  3. Default locations: ./config.toml, /etc/tileserver-rs/config.toml
  4. CWD auto-detect — scan the current directory
Info

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:

FeatureDescriptionDefault
postgresPostgreSQL tile sources (tables + functions)Yes
rasterGDAL-based raster/COG tile supportYes
mltMLT transcoding (MLT to MVT and MVT to MLT)Yes
geoparquetServe tiles from GeoParquet files on-the-flyNo
frontendEmbed the Nuxt web UI into the binaryNo
FeatureDescriptionDefault
----------------------------------------------------------------
postgresPostgreSQL tile sources (tables + functions)Yes
rasterGDAL-based raster/COG tile supportYes
mltMLT transcoding (MLT to MVT and MVT to MLT)Yes
duckdbSQL-driven tile generation via embedded DuckDBNo
frontendEmbed the Nuxt web UI into the binaryNo
# 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
Info

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.