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
[[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) |
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.
Source Configuration
File-based sources (PMTiles, MBTiles) 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 or mbtiles | 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"
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
├── 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/
└── ...
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.
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 |
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, 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.