Configuration
Tilegroxy is a configuration driven application. This documentation describes the various configuration options available. Configuration can be supplied as either YAML or JSON format. Documentation is primarily in YAML format however advanced YAML features are avoided to make it easy to convert to JSON.
Conventions
Parameter names (configuration keys) are case-insensitive unless indicated otherwise.
Names (see below) are always lower case.
Unknown or misspelled configuration keys are rejected as an error rather than silently ignored, both at the top level and within any entity block. See Config Check for details.
Some parameters can be specified by environment variables which must be upper case. Environment variables override config parameters which override default values.
The environment variable name is derived by joining the parameter’s full key path with _, upper-cased. For example, server.port becomes SERVER_PORT and logging.main.level becomes LOGGING_MAIN_LEVEL. This works for any scalar parameter with a fixed key, even if it’s not set in your config file at all - it does not apply to the dynamic, per-entity parameters described below (those support env.ENV_VAR_NAME value substitution instead).
Entities
Some configuration sections (authentication, provider, cache, datastores, analytics and secret) support selecting different methods of operation that change the full list of parameters available. For example, a "proxy" provider requires a url parameter to get a map tile from another server while a "static" provider takes in a image to return for every request. You select these operating modes using a parameter called name.
Since these entities are too dynamic to have fixed environment variables and frequently may require a secret to operate, any string parameters can be made to use an environment variable by specifying a value in the format of env.ENV_VAR_NAME. You can also use an external secret store if configured by specifying a value in the format secret.SECRET_NAME
Layer
A layer represents a distinct mapping layer as would be displayed in a typical web map application. Each layer can be accessed independently from other map layers. The main thing that needs to be configured for a layer is the provider described below.
The URLs of incoming requests follow a format like: /tiles/{layerName}/{z}/{x}/{y} the layer name can be one of two things: 1) the ID of the layer or 2) A string that matches a pattern. A pattern should include non-subsequent placeholder values wrapped in curly braces. Those placeholder values can be used in certain providers, such as the Proxy provider where they can be forwarded along to the upstream map layer. To understand how you can utilize patterns, see the NOAA Post-Storm example
When using a pattern you can include Regular Expressions to validate the values that match against the placeholder.
It is highly recommended that layer names be alphanumeric plus underscores. While other characters aren’t forbidden, several mechanisms (some caches and telemetry) replace potentially dangerous characters with underscores. This can lead to collisions if you have layer names that differ only by special characters.
Configuration options:
| Parameter | Description | Type | Required | Default |
|---|---|---|---|---|
id |
A url-safe identifier of the layer. Primarily used as the default path for incoming tile web requests |
string |
Yes |
None |
pattern |
A url-safe pattern with non-subsequent placeholders |
string |
No |
id |
paramValidator |
A mapping of regular expressions to use to validate the values that match against the placeholders. Each expression must match the full value (it’s automatically anchored to start and end). Specify a key of "*" to apply it to all values |
map[string]string |
No |
None |
provider |
The configuration that drives how tiles are generated |
Yes |
None |
|
client |
A Client configuration to use for this layer specifically that overrides the Client from the top-level of the configuration. See below for Client schema |
No |
None |
|
skipcache |
If true, skip reading and writing to cache |
bool |
No |
false |
skipanalytics |
If true, skip recording analytics events for this layer. Useful for internal or debugging layers you don’t want cluttering usage data |
bool |
No |
false |
Example:
layer:
id: my_layer
pattern: my_{name}_{version}
paramValidator:
"*": "^[a-zA-Z0-9]+$"
"version": "v[0-9]{1,3}"
skipCache: true
client:
userAgent: my_app/1.0
provider:
...
Provider
A provider represents the underlying functionality that "provides" the tiles that make up the mapping layer. This is most commonly an external HTTP(s) endpoint using either the "proxy" or "URL template" providers. Custom providers can be created to extract tiles from other sources.
When supplying a provider ensure you include the name parameter. Some providers require nested providers; be aware that repeated nesting has a performance cost.
Proxy
The Proxy provider is the simplest option that simply forwards tile requests to another HTTP(s) endpoint. This provider can be used for mapping services that operate in tiles (ZXY, TMS, or WMTS) or against bounds (i.e. WMS). TMS inverts the y coordinate compared to ZXY and WMTS formats, which is handled by the InvertY parameter.
The following is the typical request flow when using a proxy provider:
Name should be "proxy"
Configuration options:
| Parameter | Description | Type | Required | Default |
|---|---|---|---|---|
url |
A URL pointing to the tile server. Should contain placeholders surrounded by "{}" that are replaced on-the-fly |
string |
Yes |
None |
inverty |
Changes Y tile numbering to be South-to-North instead of North-to-South. Only impacts Y/y placeholder |
bool |
No |
false |
srid |
What projection bounds should be in. Can only be 4326 or 3857 |
uint |
No |
4326 |
The following placeholders are available in the URL:
| Placeholder | Description |
|---|---|
x or X |
The X tile coordinate from the incoming request |
y or Y |
The Y tile coordinate either from the incoming request or the "flipped" equivalent if the |
z or Z |
The Z tile coordinate from the incoming request (aka "zoom") |
xmin |
The "west" coordinate of the bounding box defined by the incoming tile coordinates. In the projection specified by |
xmax |
The "east" coordinate of the bounding box defined by the incoming tile coordinates. In the projection specified by |
ymin |
The "north" coordinate of the bounding box defined by the incoming tile coordinates. In the projection specified by |
ymax |
The "south" coordinate of the bounding box defined by the incoming tile coordinates. In the projection specified by |
env.XXX |
An environment variable whose name is XXX |
ctx.XXX |
A context variable (typically an HTTP header) whose name is XXX |
layer.XXX |
If the layer includes a pattern with a placeholder of XXX, this is the replacement value from the used layer name |
|
For an additional layer of control over what values a layer pattern will accept before they ever reach a placeholder, see |
Example:
provider:
name: proxy
url: https://tile.openstreetmap.org/{z}/{x}/{y}.png?key={env.key}&agent={ctx.User-Agent}
URL Template
The URL Template provider overlaps with the Proxy provider but is meant specifically for WMS endpoints. Instead of merely supplying tile coordinates, the URL Template provider will supply the bounding box. This provider is available mostly for compatibility, you generally should use Proxy instead.
Currently only supports EPSG:4326 and EPSG:3857
Name should be "url template"
Configuration options:
| Parameter | Description | Type | Required | Default |
|---|---|---|---|---|
template |
A URL pointing to the tile server. Should contain placeholders |
string |
Yes |
None |
width |
What to use for $width placeholder |
uint |
No |
256 |
height |
What to use for $height placeholder |
uint |
No |
256 |
srid |
What projection the bounds should be in and what to use for $srs placeholder. Can only be 4326 or 3857 |
uint |
No |
4326 |
Effect
Applies visual effects to an image generated by another provider. There’s a variety of options and many of them can have very poor performance, user beware.
This can only be used with layers that return JPEG or PNG images. The result always outputs in PNG format.
Name should be "effect"
| Parameter | Description | Type | Required | Default |
|---|---|---|---|---|
provider |
The provider to get the imagery to apply the effect to |
Provider |
Yes |
None |
mode |
The effect to apply. Examples of the modes. Possible values: "blur", "gaussian", "brightness", "contrast", "gamma", "hue", "saturation", "dilate", "edge detection", "erode", "median", "threshold", "emboss", "grayscale", "invert", "sepia", "sharpen", or "sobel" |
String |
No |
normal |
intensity |
The intensity of the effect, exact meaning/value range depends on mode. Only applicable if mode is one of: "blur", "gaussian", "brightness", "contrast", "gamma", "hue", "saturation", "dilate", "edge detection", "erode", "median", or "threshold" |
Float |
No |
0 |
Example:
provider:
name: effect
mode: grayscale
provider:
name: proxy
url: https://tile.openstreetmap.org/{z}/{x}/{y}.png
Blend
Allows you to combine the imagery from multiple providers. The simplest use case for this is to "sandwich" or "composite" semi-transparent images on top of each other. For example you can put county boundaries on top of a flood map or include a watermark on your maps. Multiple blending modes are available to fine-tune the effect.
This can only be used with layers that return JPEG or PNG images. Tiles will be scaled down to the lowest resolution to be combined and the combined result always output in PNG format.
Each downstream provider is called in parallel.
The following diagram depicts a possible request flow when using the Blend provider with the layers parameter. Note that each downstream layer is individually cached; this is useful when it’s expected for requests to also come in for each of the individual layers but an unnecessary cost if not. If you only expect requests for the blended layer, either use the providers parameter option or simply disable caching in the downstream layer(s).

Name should be "blend"
| Parameter | Description | Type | Required | Default |
|---|---|---|---|---|
providers |
The providers to blend together. Order matters |
Provider[] |
Yes |
None |
mode |
How to blend the images. Examples of the modes. Possible values: "add", "color burn", "color dodge", "darken", "difference", "divide", "exclusion", "lighten", "linear burn", "linear light", "multiply", "normal", "opacity", "overlay", "screen", "soft light", "subtract" |
String |
No |
normal |
opacity |
Only applicable if mode is "opacity". A value between 0 and 1 controlling the amount of opacity |
Float |
No |
0 |
layer |
An alternative to the |
Object - See next rows |
No |
None |
layer.pattern |
A string with one or more placeholders present wrapped in curly brackets that match the layer placeholder you want to refer towards |
String |
Yes |
None |
layer.values |
An entry per instantiation of the layer, each entry should have a value for each placeholder in the pattern with the key being the placeholder and the value being the replacement value |
{"k":"v"}[] |
Yes |
None |
Example:
provider:
name: blend
mode: normal
layer:
pattern: noaa_poststorm_{date}{version}
values:
- date: 20230902
version: a
- date: 20230901
version: b
- date: 20230901
version: a
- date: 20230831
version: b
- date: 20230831
version: a
Fallback
Delegates calls to a Primary provider, then falls back Secondary provider when an error is returned or the tile is outside the valid zoom or bounds. This is useful, for example, where you’re integrating with a system that returns an error for requests outside of the coverage area and you want to return a Static image in those cases without it being logged as an error. It especially can be useful in conjunction with the Blend provider.
Currently the preAuth method is never called for the secondary provider, therefore only authless providers should be used as fallbacks. In the future we may include calls to the preAuth method but only when the fallback logic is triggered.
The bounds parameter is only applied at a per-tile level. That is, the edge where the fallback begins to kick in will visibly change as you zoom in/out. This allows this provider to work in a format agnostic manner for both raster and vector tiles. See the Crop provider to allow raster tiles to be limited at an exact geographic area.
Name should be "fallback"
Configuration options:
| Parameter | Description | Type | Required | Default |
|---|---|---|---|---|
primary |
The provider to delegate to first |
Provider |
Yes |
None |
secondary |
The provider to delegate to if primary returns an error |
Provider |
Yes |
None |
zoom |
Zooming below or above this range will activate the fallback. Can be a single number, a range with a dash between start and end, or a comma separated list of the first two options. For example "4" "2-3" or "2,3-4" |
String |
No |
0-21 |
bounds |
Any tiles that don’t intersect with this bounds will activate the fallback |
Object with north, south, east, west |
No |
Whole world |
cache |
When to save the resulting tile to the cache. Options: always, unless-error, unless-fallback. |
string |
No |
unless-error |
Example:
provider:
name: fallback
cache: always
zoom: 4-21
bounds:
south: 51
north: 63
west: -7
east: 0.1
primary:
name: proxy
url: https://tile.openstreetmap.org/{z}/{x}/{y}.png
secondary:
name: static
color: "0000"
Static
Generates the same exact image for every single tile. This is most useful when used with either the Fallback or Blend providers.
Name should be "static"
Configuration options:
| Parameter | Description | Type | Required | Default |
|---|---|---|---|---|
image |
Either a filepath to an image on the local filesystem or one of the built-in images |
string |
Yes |
None |
color |
A hexcode (RGB or RGBA) of a color to return. Equivalent to specifying |
string |
No |
None |
Ref
Ref refers requests to another layer. This is pointless by itself but can be useful when combined with other providers to avoid repeating yourself.
For instance you can have a layer with a complex client configuration that utilizes a pattern and points to a WMS server with the WMS layer being specified by a placeholder, then several other layers using Ref that fill in the blank.
Ref has a recursion limit of 10. We attempt to detect chains that will violate this limit at startup, but not all cases can be statically detected. Any tiles generated at runtime that violate this limit will result in an error after the 10th hop. This protection only applies to recursive ref’s in a single config, for example there is no protection against using a proxy provider between two separate instances of tilegroxy.
Name should be "ref"
Configuration options:
| Parameter | Description | Type | Required | Default |
|---|---|---|---|---|
layer |
The layername to refer towards, treated the same if it were supplied in an incoming request. |
string |
Yes |
None |
Example
provider: name: ref layer: something_else
Custom
Custom providers implement your own custom logic for providing imagery from whatever source you can imagine. They require a custom Go script file interpreted using Yaegi. The main README has more detailed information on implementing custom providers and examples are available.
Name should be "custom"
Configuration options:
| Parameter | Description | Type | Required | Default |
|---|---|---|---|---|
file |
An absolute file path to find the Go code implementing the provider |
string |
Yes |
None |
Any |
Any additional parameter you include will be automatically supplied to your custom provider as-is |
Any |
No |
None |
Transform
This provider allows you to implement a function to change the RGBA value of each individual pixel in imagery from another provider. Like the "Custom" provider this is implemented using Yaegi and requires you to include your own Go code. The interface for this is however much simpler, it requires just a single function:
func transform(r, g, b, a uint8) (uint8, uint8, uint8, uint8)
You can include the logic in a dedicated file, or inline in configuration. No special types or functions are available for use besides the standard library. A package declaration and any imports are optional.
This can only be used with layers that return JPEG or PNG images. Tiles will be scaled down to the lowest resolution to be combined and the combined result always output in PNG format.
Name should be "transform"
Configuration options:
| Parameter | Description | Type | Required | Default |
|---|---|---|---|---|
file |
An absolute file path to find the Go code implementing the transformation |
string |
No |
None |
formula |
The go code implementing the transformation. Required if file isn’t included |
string |
No |
None |
provider |
The provider to get the imagery to transform |
Provider |
Yes |
None |
threads |
How many threads (goroutines) to use per tile. The typical tile has 65,536 pixels, setting this to 8 for instance means each thread has to process 8,192 pixels in parallel. This helps avoid latency becoming problematically slow. |
int |
No |
1 |
Example:
provider:
name: transform
threads: 8
formula: |
func transform(r, g, b, a uint8) (uint8, uint8, uint8, uint8) {
return g,b,r,a
}
provider:
name: proxy
url: https://tile.openstreetmap.org/{z}/{x}/{y}.png
CGI
The CGI provider allows a call-out to an external executable on the local system that’s responsible for generating the tile. This allows tilegroxy to act as the HTTP server for a CGI program like Apache httpd or nginx traditionally acts. The flagship use-case for this is to integrate with MapServer. A full example is available in examples/mapserver.
Name should be "cgi"
Configuration options:
| Parameter | Description | Type | Required | Default |
|---|---|---|---|---|
Exec |
The path to the CGI executable |
string |
Yes |
None |
Args |
Arguments to pass into the executable in standard "split on spaces" format |
[]string |
No |
None |
Uri |
The URI (path + query) to pass into the CGI for the fake request - think mod_rewrite style invocation of the CGI |
string |
Yes |
None |
Domain |
The host to pass into the CGI for the fake request |
string |
No |
localhost |
Headers |
Extra headers to pass into the CGI with the request |
map[string][]string |
No |
None |
Env |
Extra environment variables to supply to the CGI invocations. If the value is an empty string it passes along the value from the main tilegroxy invocation |
map[string]string |
No |
None |
WorkingDir |
Working directory for the CGI invocation |
string |
No |
Base dir of exec |
InvalidAsError |
If true, if the CGI response includes a content type that isn’t in the Client's list of acceptable content types then it treats the response body as an error message |
bool |
No |
false |
Cache
The cache configuration defines the datastores where tiles should be stored/retrieved. We recommended you use a multi-tiered cache with a smaller, faster "near" cache first followed by a larger, slower "far" cache.
There is no universal mechanism for expiring cache entries. Some cache options include built-in mechanisms for applying an TTL and maximum size however some require an external cleanup mechanism if desired. Be mindful of this as some options may incur their own costs if allowed to grow unchecked.
When specifying a cache ensure you include the name parameter.
Multi
Implements a multi-tiered cache.
When looking up cache entries each cache is tried in order. When storing cache entries each cache is called simultaneously. This means that the fastest cache(s) should be first and slower cache(s) last. As each cache needs to be tried before tile generation starts, it is not recommended to have more than 2 or 3 caches configured.
Name should be "multi"
Configuration options:
| Parameter | Description | Type | Required | Default |
|---|---|---|---|---|
tiers |
An array of Cache configurations. Multi should not be nested inside a Multi |
Cache[] |
Yes |
None |
Example:
cache:
name: multi
tiers:
- name: memory
maxsize: 1000
ttl: 1000
- name: disk
path: "./disk_tile_cache"
Disks
Stores the cache entries as files in a location on the filesystem.
If the filesystem is purely local then you will experience inconsistent performance if using tilegroxy in a high-availability deployment.
Files are stored in a flat structure inside the specified directory. No cleanup process is included inside of tilegroxy itself. It is recommended you use an external cleanup process to avoid running out of disk space.
Name should be "disk"
Configuration options:
| Parameter | Description | Type | Required | Default |
|---|---|---|---|---|
path |
The absolute path to the directory to store cache entries within. Directory (and tree) will be created if it does not already exist |
string |
Yes |
None |
filemode |
A Go filemode as an integer to use for all created files/directories. This might change in the future to support a more conventional unix permission notation |
uint32 |
No |
0777 |
Example:
"cache": {
"name": "disk",
"path": "./disk_tile_cache"
}
Cache key sanitization
Cache filenames are derived from the layer name and tile coordinates, e.g. mylayer_1_2_3. Since the layer name comes from the incoming request path for pattern layers, it’s sanitized before being used in a filename: every character other than ASCII letters, digits, -, and . is replaced with , and any run of two or more consecutive dots (or a lone .) is also replaced with . This prevents a crafted layer name (e.g. one containing ../) from writing or reading cache entries outside the configured path. Ordinary layer names like osm or my-layer pass through unchanged.
This is a many-to-one mapping, so two distinct layer names that differ only in unsafe characters will sanitize to the same filename and share a cache entry - for example both a/b and a b sanitize to a_b. This is an accepted tradeoff to keep cache filenames human-readable; if you rely on layer names that differ only by characters outside A-Za-z0-9-., be aware they may collide in the cache.
Memcache
Cache tiles using memcache.
Name should be "memcache"
Configuration options:
| Parameter | Description | Type | Required | Default |
|---|---|---|---|---|
host |
The host of the memcache server. A convenience equivalent to supplying |
String |
No |
127.0.0.1 |
port |
The port of the memcache server. A convenience equivalent to supplying |
int |
No |
6379 |
keyprefix |
A prefix to use for keys stored in cache. Helps avoid collisions when multiple applications use the same memcache |
string |
No |
None |
ttl |
How long cache entries should persist for in seconds. Cannot be disabled. |
uint32 |
No |
1 day |
servers |
The list of servers to connect to supplied as an array of objects, each with a host and key parameter. This should only have a single entry when operating in standalone mode. If this is unspecified it uses the standalone |
Array of |
No |
host and port |
Example:
cache:
name: memcache
host: 127.0.0.1
port: 11211
Memory
A local in-memory cache. This stores the tiles in the memory of the tilegroxy daemon itself.
This is not recommended for production use. It is meant for development and testing use-cases only. Setting this cache too high can cause stability issues for the service and this cache is not distributed so can cause inconsistent performance when deploying in a high-availability production environment.
Name should be "memory"
Configuration options:
| Parameter | Description | Type | Required | Default |
|---|---|---|---|---|
maxsize |
Maximum number of tiles to hold in the cache. Must be at least 10. Setting this too high can cause out-of-memory panics. This is not a guaranteed setting, which entry is evicted when exceeding this size is an implementation detail and the size can temporarily grow somewhat larger. |
uint16 |
No |
100 |
ttl |
Maximum time to live for cache entries in seconds |
uint32 |
No |
3600 |
Example:
cache:
name: memory
maxsize: 1000
ttl: 1000
Redis
Cache tiles using redis or another redis-compatible key-value store.
Name should be "redis"
Configuration options:
| Parameter | Description | Type | Required | Default |
|---|---|---|---|---|
host |
The host of the redis server. A convenience equivalent to supplying |
String |
No |
127.0.0.1 |
port |
The port of the redis server. A convenience equivalent to supplying |
int |
No |
6379 |
db |
Database number, defaults to 0. Unused in cluster mode |
int |
No |
0 |
keyprefix |
A prefix to use for keys stored in cache. Serves a similar purpose as |
string |
No |
None |
username |
Username to use to authenticate with redis |
string |
No |
None |
password |
Password to use to authenticate with redis |
string |
No |
None |
mode |
Controls operating mode of redis. Can be |
string |
No |
standalone |
ttl |
How long cache entries should persist for in seconds. Cannot be disabled. |
uint32 |
No |
1 day |
servers |
The list of servers to connect to supplied as an array of objects, each with a host and key parameter. This should only have a single entry when operating in standalone mode. If this is unspecified it uses the standalone |
Array of |
No |
host and port |
Example:
{
"name": "redis"
"mode": "ring",
"servers": [
{
"host": "127.0.0.1",
"port": 6379
},
{
"host": "127.0.0.1",
"port": 6380
}
],
"ttl": 3600
}
S3
Cache tiles as objects in an AWS S3 bucket.
Ensure the user you’re using has proper permissions for reading and writing objects in the bucket. The permissions required are the minimal set you’d expect: GetObject and PutObject. It’s highly recommended to also grant ListBucket permissions, otherwise the log will contain misleading 403 error messages for every cache miss. Also ensure the user has access to the KMS key if using bucket encryption.
If you’re using a Directory Bucket AKA Express One Zone there’s a few things to configure:
-
Ensure
storageclassis set to "EXPRESS_ONEZONE" -
The bucket contains the full name including suffix. For example:
my-tilegroxy-cache--use1-az6--x-s3
Name should be "s3"
Configuration options:
| Parameter | Description | Type | Required | Default |
|---|---|---|---|---|
bucket |
The name of the bucket to use |
string |
Yes |
None |
path |
The path prefix to use for storing tiles |
string |
No |
/ |
region |
The AWS region containing the bucket. Required if region is not specified via other means. Consult AWS documentation for possible values |
string |
No |
None |
access |
The AWS Access Key ID to authenticate with. This is not recommended; it is offered as a fallback authentication method only. Consult AWS documentation for better options |
string |
No |
None |
secret |
The AWS Secret Key to authenticate with. This is not recommended; it is offered as a fallback authentication method only. Consult AWS documentation for better options |
string |
No |
None |
profile |
The profile to use to authenticate against the AWS API. Consult AWS documentation for specifics |
string |
No |
None |
storageclass |
The storage class to use for the object. You probably can leave this blank and use the bucket default. Consult AWS documentation for an overview of options. The following are currently valid: STANDARD REDUCED_REDUNDANCY STANDARD_IA ONEZONE_IA INTELLIGENT_TIERING GLACIER DEEP_ARCHIVE OUTPOSTS GLACIER_IR SNOW EXPRESS_ONEZONE |
string |
No |
STANDARD |
endpoint |
Override the S3 API Endpoint we talk to. Useful if you’re using S3 outside AWS or using a directory bucket |
string |
No |
AWS Auto |
Example:
cache:
name: s3
bucket: my-cache--use1-az6--x-s3
endpoint: "https://s3express-use1-az6.us-east-1.amazonaws.com"
storageclass: EXPRESS_ONEZONE
region: us-east-1
profile: tilegroxy_s3_user
Authentication
Implements incoming auth schemes. This is primarily meant for authentication but does include some authorization by limiting access to specific layers via JWT or custom schemes.
Requests that do not comply with authentication requirements will receive a 401 Unauthorized HTTP status code.
When supplying authentication ensure you include the name parameter.
None
No incoming authentication, all requests are allowed. Ensure you have an external authentication solution before exposing this to the internet.
Name should be "none"
Static Key
Requires incoming requests have a specific key supplied as a "Bearer" token in a "Authorization" Header.
It is recommended you employ caution with this option. It should be regarded as a protection against casual web scrapers but not true security. It is recommended only for development and internal ("intranet") use-cases. Does not include any authz logic.
Name should be "static key"
Configuration options:
| Parameter | Description | Type | Required | Default |
|---|---|---|---|---|
key |
The bearer token to require be supplied. If not specified |
string |
No |
Auto |
JWT
Requires incoming requests include a JSON Web Token (JWT). The signature of the token is verified against a fixed secret and grants are validated.
Currently this implementation only supports a single key specified against a single signing algorithm. The key can either be stored in configuration or supplied via environment variable. Support for multiple keys and keys pulled from secret stores is a desired future roadmap item.
The following claims are supported/enforced:
| Claim | Implementation |
|---|---|
exp |
Ensure the JWT hasn’t expired and it’s no further than a certain amount of time from now |
aud |
Validate it matches a specific value |
sub |
Validate it matches a specific value |
iss |
Validate it matches a specific value |
scope |
Validate it contains a specific scope OR ensure a given prefix plus the layer in the current request is contained in scope |
geohash |
Validate the current tile being requested is partially contained in the geohash. See the crop provider to limit raster imagery layers to precise areas |
Name should be "jwt"
Configuration options:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
Key |
The key for verifying the signature. The public key if using asymmetric signing. If the value starts with "env." the remainder is interpreted as the name of the Environment Variable to use to retrieve the verification key. |
string |
Yes |
None |
Algorithm |
Algorithm to allow for JWT signature. One of: "HS256", "HS384", "HS512", "RS256", "RS384", "RS512", "ES256", "ES384", "ES512", "PS256", "PS384", "PS512", "EdDSA" |
string |
Yes |
None |
HeaderName |
The header to extract the JWT from. If this is "Authorization" it removes "Bearer " from the start. Make sure this is in "canonical case" e.g. X-Header - auth will always fail otherwise |
string |
No |
Authorization |
MaxExpiration |
How many seconds from now can the expiration be. JWTs more than X seconds from now will result in a 401 |
uint32 |
No |
1 day |
ExpectedAudience |
Require the "aud" grant to be this string |
string |
No |
None |
ExpectedSubject |
Require the "sub" grant to be this string |
string |
No |
None |
ExpectedIssuer |
Require the "iss" grant to be this string |
string |
No |
None |
ExpectedScope |
Require the "scope" grant to contain this string |
string |
No |
None |
LayerScope |
If true the "scope" grant is used to whitelist access to layers |
bool |
No |
false |
ScopePrefix |
If true this prefix indicates scopes to use. For example a prefix of "tile/" will mean a scope of "tile/test" grants access to "test". Doesn’t impact ExpectedScope |
string |
No |
Empty string |
UserId |
Use the specified grant as the user identifier. This is just used for logging by default but it’s made available to custom providers |
string |
No |
sub |
Example:
authentication: name: jwt key: env.JWT_KEY algorithm: HS256
Custom
Allows you to specify your own logic controlling how auth tokens should be extracted and validated. This, like the custom provider, utilizes Yaegi to allow you to supply your own custom code.
To help mitigate the performance impact of calling the interpreted validate method, a cache is utilized by default. In turn, to avoid concurrent requests that utilize the same token from causing repetitive calls to validate, a pool of locks are utilized when the cache is enabled. The size of the lock pool is equal to the number of CPUs.
For more details on implementing the code for a custom authentication, see Extensibility
Name should be "custom"
Configuration options:
| Parameter | Description | Type | Required | Default |
|---|---|---|---|---|
token |
How to extract the auth token from the request. Each Key/Value should be one of the options in the table above |
map[string]string |
Yes |
None |
cachesize |
Configures the size of the cache of already verified tokens used to avoid re-verifying every request. Set to -1 to disable |
int |
No |
100 |
file |
Contains the path to the file containing the go code to perform validation of the auth token as a file |
string |
No |
None |
Example:
authentication:
name: custom
file: examples/auth/custom_from_file.go
token:
header: X-Token
Datastores
The Datastores configuration defines connections to shared resources, primarily databases, that are used by certain providers and analytics modules. Each datastore definition creates a connection pool when the application is running. Like other entities, the datastores configuration uses a parameter called "name" to dictate the type of datastore which controls the specific list of configuration parameters available. Every datastore configuration must also have an "ID" defined which can be any string and is used by a corresponding provider or analytics configuration, usually as a datastore parameter. See the following sections for the list of supported datastores.
Datastore configurations are only used by providers and analytics, caches must have their connection information defined inline.
ClickHouse
Defines a connection pool to a ClickHouse database. Utilizes clickhouse-go under the hood.
Currently used by the clickhouse analytics module.
Name should be "clickhouse"
Configuration options:
| Parameter | Description | Type | Required | Default |
|---|---|---|---|---|
ID |
The unique identifier of the datastore used to reference this datastore elsewhere in configuration |
string |
Yes |
None |
Host |
The hostname to use to connect to the database |
string |
No |
127.0.0.1 |
Port |
The port to use to connect to the database. Note the native protocol and HTTP protocol use different ports; 9000 and 8123 respectively |
uint16 |
No |
9000 |
User |
The user to use to authenticate with ClickHouse |
string |
No |
default |
Password |
The password to use to authenticate with ClickHouse |
string |
No |
None |
Database |
The name of the database to connect to |
string |
No |
default |
Protocol |
The wire protocol to use. Can be |
string |
No |
native |
Secure |
Whether to connect over TLS |
bool |
No |
false |
MinConnections |
The maximum number of idle connections to keep in reserve in the connection pool. Unlike the postgresql datastore this is a ceiling on idle connections rather than a floor |
int |
No |
5 |
MaxConnections |
The maximum number of connections to allow in the connection pool |
int |
No |
10 |
Lifetime |
The maximum amount of time (in seconds) to allow a connection to live in the pool |
int32 |
No |
1 hour |
Example:
datastores:
- name: clickhouse
id: ch-0
host: clickhouse.internal
database: analytics
user: tilegroxy
password: env.CLICKHOUSE_PASSWORD
PostgreSQL
Defines a connection pool to a PostgreSQL database. Utilizes pgx under the hood which supports PostgreSQL 12 and higher.
Name should be "postgresql"
Configuration options:
| Parameter | Description | Type | Required | Default |
|---|---|---|---|---|
ID |
The unique identifier of the datastore used to reference this datastore in a provider |
string |
Yes |
None |
Host |
The hostname to use to connect to the database |
string |
No |
localhost |
Port |
The port to use to connect to the database |
uint16 |
No |
5432 |
User |
The user to use to authenticate with postgresql |
string |
No |
postgres |
Password |
The password to use to authenticate with postgresql |
string |
No |
None |
Database |
The name of the database to connect to |
string |
No |
postgres |
MinConnections |
The minimum number of connections to keep in reserve in the connection pool |
int |
No |
10 |
MaxConnections |
The maximum number of connections to allow in the connection pool. Ensure your postgresql instance’s |
int |
No |
30 |
IdleTimeout |
The amount of time (in seconds) to allow a connection to sit idle before it is removed from the pool |
int |
No |
10 minutes |
Lifetime |
The maximum amount of time (in seconds) to allow a connection to live in the pool. A jitter of 10% is automatically applied. |
int |
No |
1 day |
Analytics
The Analytics configuration defines where tilegroxy records lightweight events describing successful tile usage. Where telemetry aggregates counters for operating the service, analytics records individual events that can be attributed to a layer, a coordinate and optionally a user, written to a destination you own and can query. This is useful for understanding which layers are actually consumed, attributing usage to tenants or users, and tracking consumption of a metered upstream provider.
Like other entities, the analytics configuration uses a parameter called "name" to dictate the type of analytics module which controls the specific list of configuration parameters available.
Analytics is disabled by default; with no analytics section configured the none module is used, no events are produced and the cost to each request is a single check.
An event is produced when, and only when, a tile is successfully served over HTTP. Requests that fail, such as a bad layer name, invalid coordinates, an out-of-bounds tile or a provider error, produce no event, nor do requests rejected by authentication. Tiles produced by the seed and test commands and health check tiles produce no events since they don’t represent real usage. A layer rendered indirectly through the ref provider isn’t recorded separately; only the layer that was actually requested is. A cache hit still produces an event; the event reflects that a user consumed a tile, not that a provider generated one.
The event is emitted after the response has been written, so recording never adds latency to the request. Individual layers can be excluded by setting skipAnalytics on the layer, see Layer.
Analytics is best-effort. A failing or slow destination must never degrade tile serving, therefore errors from an analytics module are logged and discarded; they never reach the user and never change a response. Events are buffered in memory and written in batches. If the buffer fills because the destination cannot keep up, events are dropped by default instead of delaying requests. Set batch.onFull to block if you would rather apply backpressure than lose events. Buffered events are flushed on shutdown and when configuration is hot reloaded, so an orderly restart doesn’t lose the current batch, however events still buffered during an abrupt termination are lost. If you need a complete, auditable record of every request, use access logs instead.
Every module accepts the same batch parameters. A batch is written when either maxSize events have accumulated or the oldest event in the buffer reaches maxAge. Note the age of a partial batch is only checked periodically so a batch may be written up to 25% later than maxAge.
| Parameter | Description | Type | Required | Default |
|---|---|---|---|---|
MaxSize |
Write a batch once this many events have accumulated |
uint |
No |
1000 |
MaxAge |
Write a partial batch after this many seconds so low-traffic layers still report promptly |
uint |
No |
10 |
QueueSize |
The number of events held in memory awaiting a write. Raise this to absorb larger bursts at the cost of memory |
uint |
No |
10000 |
Workers |
How many batches may be written concurrently |
uint |
No |
1 |
OnFull |
What to do when the queue is full. Can be |
string |
No |
drop |
Event Contents
Every event always includes the following, which cannot be disabled:
| Field | Description |
|---|---|
time |
When the tile was served |
layer |
The ID of the layer as configured. When a layer uses a pattern this is the configured ID, not the name from the URL, matching how per-layer telemetry metrics are recorded |
z, x, y |
The tile coordinates |
user |
The authenticated user, or empty when the request was anonymous. Only the jwt and custom authentication modules establish a user identity |
Additional attributes are opt-in via fields, which accepts any of the following names:
| Name | Description |
|---|---|
layername |
The layer name exactly as it appeared in the URL, which differs from the layer ID when the layer uses a pattern |
layerparams |
The placeholder values extracted from a patterned layer name, as a map |
ip |
The client IP address |
useragent |
The User-Agent header |
referer |
The Referer header |
host |
The Host header |
method |
The HTTP method |
path |
The request path |
query |
The raw query string |
duration |
How long the request took, in milliseconds |
bytes |
The size of the served tile in bytes |
contenttype |
The MIME type of the served tile |
An unrecognized name causes startup to fail, so a typo surfaces when you run tilegroxy config check instead of producing a column of empty values.
Be aware that ip, useragent and user IDs are personal data in many jurisdictions. They are off by default; enable them only if you have a basis to record them and consider your retention policy for the destination.
Custom Attributes
The extraFields parameter records arbitrary additional attributes. Each key is the attribute name in the destination and each value selects where it comes from:
| Value form | Description |
|---|---|
|
A value from the request context. Includes |
|
A request header. The name is canonicalized to Header-Case before lookup |
anything else |
Used as a literal constant, useful for tagging events with an environment or region name |
Because env. and secret. are handled generically for all configuration, they also work here and resolve once at startup instead of per request.
Metrics
When telemetry is enabled, analytics reports three counters: tilegroxy.analytics.recorded for events successfully written to a destination, tilegroxy.analytics.dropped for events discarded because a queue was full, and tilegroxy.analytics.error for events in batches that failed to write. A steadily increasing dropped or error count means analytics is losing data even though tiles are being served normally, so alert on these separately from tile metrics.
Example:
analytics:
name: clickhouse
id: warehouse
datastore: ch-0
table: tile_events
fields:
- duration
- bytes
extraFields:
tenant: hdr.X-Tenant-Id
environment: production
batch:
maxSize: 5000
maxAge: 30
A runnable demo combining tilegroxy, a PostgreSQL destination, a map and a dashboard over the recorded events is available in examples/analytics-demo.
None
Records nothing. Since analytics is disabled by default this is rarely needed, but it is useful for temporarily disabling a configured destination without deleting its configuration and as a placeholder in templated configurations.
Name should be "none"
Configuration options:
No configuration options of its own. The id, fields, extraFields and batch parameters common to every analytics module are still accepted and validated but have no effect.
Example:
analytics: name: none
ClickHouse
Writes events to a ClickHouse table using its native batch insert. ClickHouse is well suited to this workload since analytics events are append-only, high volume, and usually queried in aggregate.
Requires a clickhouse datastore to supply the connection.
Name should be "clickhouse"
Table Setup
Tilegroxy never issues DDL, you must create the table yourself before starting tilegroxy. This keeps the credentials tilegroxy uses free of schema privileges and leaves partitioning and retention under your control.
The following matches the default column names:
CREATE TABLE tile_events (
time DateTime,
layer LowCardinality(String),
z UInt8,
x UInt32,
y UInt32,
user_id String,
extra Map(String, String)
) ENGINE = MergeTree()
ORDER BY (layer, time)
The extra column receives everything selected via fields and extraFields, with values rendered as strings. Using a map instead of dedicated columns means changing which fields you collect never requires a schema migration.
For a table that expires old data automatically, add a TTL:
ALTER TABLE tile_events MODIFY TTL time + INTERVAL 90 DAY
Configuration options:
Also accepts the batching parameters described in Analytics.
| Parameter | Description | Type | Required | Default |
|---|---|---|---|---|
Datastore |
The ID of the datastore to use. The datastore must have a type of "clickhouse". Also see the Datastores documentation. |
string |
Yes |
None |
Table |
The table to insert events into. May be schema qualified. Must be a plain identifier |
string |
Yes |
None |
ID |
An identifier for this destination, used in logs to attribute analytics messages |
string |
No |
clickhouse |
Columns |
Overrides for the default column names. Keys are the logical field names: time, layer, z, x, y, user_id, extra |
map[string]string |
No |
None |
Fields |
Additional attributes to record. See Analytics |
string[] |
No |
None |
ExtraFields |
Arbitrary additional attributes. See Analytics |
map[string]string |
No |
None |
Example:
datastores:
- name: clickhouse
id: ch-0
host: clickhouse.internal
database: analytics
user: tilegroxy
password: env.CLICKHOUSE_PASSWORD
analytics:
name: clickhouse
datastore: ch-0
table: tile_events
fields:
- duration
- bytes
batch:
maxSize: 5000
maxAge: 30
Using an existing table with different column names:
analytics:
name: clickhouse
datastore: ch-0
table: analytics.map_usage
columns:
layer: layer_id
user_id: uid
extra: attributes
PostgreSQL
Writes events to a PostgreSQL table. A reasonable choice when you already run PostgreSQL and your event volume is moderate; for high volume analytics consider ClickHouse instead.
Requires a postgresql datastore to supply the connection. The same datastore can be shared with the postgismvt provider, though pointing analytics at a separate database avoids adding write load to the one serving tiles.
Name should be "postgres"
Table Setup
Tilegroxy never issues DDL, you must create the table yourself before starting tilegroxy. This keeps the credentials tilegroxy uses free of schema privileges.
The following matches the default column names:
CREATE TABLE tilegroxy_analytics (
time TIMESTAMPTZ NOT NULL,
layer TEXT NOT NULL,
z INTEGER NOT NULL,
x INTEGER NOT NULL,
y INTEGER NOT NULL,
user_id TEXT,
extra JSONB
);
CREATE INDEX ON tilegroxy_analytics (layer, time);
The extra column receives everything selected via fields and extraFields as a JSON object. Using JSONB instead of dedicated columns means changing which fields you collect never requires a schema migration.
This table grows without bound, so consider partitioning it by time and dropping old partitions on a schedule. Tilegroxy does not expire events.
Configuration options:
Also accepts the batching parameters described in Analytics.
| Parameter | Description | Type | Required | Default |
|---|---|---|---|---|
Datastore |
The ID of the datastore to use. The datastore must have a type of "postgresql". Also see the Datastores documentation. |
string |
Yes |
None |
Table |
The table to insert events into. May be schema qualified. Must be a plain identifier |
string |
Yes |
None |
ID |
An identifier for this destination, used in logs to attribute analytics messages |
string |
No |
postgres |
Columns |
Overrides for the default column names. Keys are the logical field names: time, layer, z, x, y, user_id, extra |
map[string]string |
No |
None |
Fields |
Additional attributes to record. See Analytics |
string[] |
No |
None |
ExtraFields |
Arbitrary additional attributes. See Analytics |
map[string]string |
No |
None |
Example:
datastores:
- name: postgresql
id: pg-analytics
host: localhost
user: tilegroxy
password: env.PGPASSWORD
database: analytics
analytics:
name: postgres
datastore: pg-analytics
table: tilegroxy_analytics
fields:
- duration
- contenttype
extraFields:
environment: production
Custom
Sends events wherever you want by way of a Go script interpreted at runtime. Use this to reach a destination tilegroxy doesn’t support natively, such as a webhook, a message queue, or a file in a bespoke format, without building tilegroxy yourself.
See Extensibility for background on how custom modules work and their limitations. Custom scripts run unrestricted and can use the entire standard library including os/exec, so be as cautious using a third party analytics script as you would be executing any other third party software. Scripts cannot import third party libraries, therefore a destination requiring a vendor SDK needs a native module instead of a custom one. net/http is available which is enough for most HTTP APIs.
Name should be "custom"
The Script
Your script must be in package custom and define a function named record:
package custom
import (
"tilegroxy/tilegroxy"
)
func record(ctx tilegroxy.Context, events []tilegroxy.AnalyticsEvent, params map[string]interface{}, msgs tilegroxy.ErrorMessages) error {
// ...
return nil
}
The record function is called once per batch of events, not once per event, because interpreting Go carries meaningful per-call overhead. Configure batch.maxSize and batch.maxAge to control how often it runs.
Each AnalyticsEvent has Time, LayerID, LayerName, Z, X, Y, UserID and Fields, the last being a map of whatever you selected via fields and extraFields.
The params argument receives every configuration parameter that isn’t one of the module’s own, which is how you pass a URL, a filename or credentials to the script. Note the name parameter is included in this map.
Returning an error causes the batch to be logged as failed and discarded. The error never reaches the user.
Configuration options:
Also accepts the batching parameters described in Analytics.
| Parameter | Description | Type | Required | Default |
|---|---|---|---|---|
File |
An absolute file path to find the Go code implementing the module. Mutually exclusive with Script |
string |
No |
None |
Script |
The Go code implementing the module, supplied inline. Mutually exclusive with File |
string |
No |
None |
ID |
An identifier for this destination, used in logs to attribute analytics messages |
string |
No |
custom |
Fields |
Additional attributes to record. See Analytics |
string[] |
No |
None |
ExtraFields |
Arbitrary additional attributes. See Analytics |
map[string]string |
No |
None |
Any |
Any additional parameter you include will be automatically supplied to your custom module as-is |
Any |
No |
None |
One of File or Script is required.
Example:
analytics:
name: custom
file: examples/analytics/custom_webhook.go
url: https://example.com/usage
fields:
- duration
batch:
maxSize: 100
maxAge: 60
A complete working script is available in examples/analytics.
Secret
Configuring a Secret source allows you to pull keys, passwords, or any other sensitive value from an external secret store rather than placing them in your configuration directly. This is similar to the way you can reference environmental variables. Secrets loaded in this way are never written to disk. Properly externalizing secrets allows you to place your configuration into source control without modification.
Once a Secret source is configured, you can utilize it by including a configuration value like secret.key-name. The secret source will then be queried for a secret named key-name. If the secret store has no secret by that name, it will prevent the application from starting up. This effect applies to any other "entity" (cache, authentication, provider), for obvious reasons you can’t use a secret in the configuration for your secret source.
AWS Secrets Manager
This secret source utilizes the AWS Secrets Manager service.
This source includes a cache for values. This avoids repeatedly querying for the same value e.g. if it’s used by multiple providers. Currently secrets are only pulled at once at startup, however in the future this might be changed to re-pull periodically or upon configuration changes. Therefore it is advised to think through how often your secrets might rotate when configuring the TTL value, even though currently it only need to be a small value to span the initial start-up of the application.
Secrets Manager places secrets inside a JSON structure if configured through the console. To support that keys should be in the structure of id:key with id being the ID of the secret as a whole and key being the key from the JSON Object stored in the secret. If the secret isn’t JSON then you only need to supply the id by itself. You can override the separator used to be any value rather than a colon (:).
This supports the standard means of authenticating with AWS. Ensure your user/role includes permission both to retrieve the secrets as well as permission to use the relevant KMS key to decrypt it.
Name should be "awssecretsmanager"
Configuration options:
| Parameter | Description | Type | Required | Default |
|---|---|---|---|---|
separator |
How to separate the Id of the secret from the JSON key in the secret name as a whole |
string |
No |
: |
ttl |
How long to cache secrets in seconds. Cache disabled if less than 0. |
int |
No |
1 hour |
region |
The AWS region containing the bucket. Required if region is not specified via other means. Consult AWS documentation for possible values |
string |
No |
None |
access |
The AWS Access Key ID to authenticate with. This is not recommended; it is offered as a fallback authentication method only. Consult AWS documentation for better options |
string |
No |
None |
secret |
The AWS Secret Key to authenticate with. This is not recommended; it is offered as a fallback authentication method only. Consult AWS documentation for better options |
string |
No |
None |
profile |
The profile to use to authenticate against the AWS API. Consult AWS documentation for specifics |
string |
No |
None |
Server
Configures how the HTTP server should operate
Configuration options:
| Parameter | Description | Type | Required | Default |
|---|---|---|---|---|
BindHost |
IP address to bind HTTP server to |
string |
No |
127.0.0.1 |
Port |
Port to bind HTTP server to |
int |
No |
8080 |
RootPath |
The root HTTP Path to serve all requests under. |
string |
No |
/ |
TilePath |
The HTTP Path to serve tiles under in addition to RootPath. The defaults will result in a path that looks like /tiles/{layer}/{z}/{x}/{y} |
string |
No |
tiles |
Headers |
Include these headers in all response from server |
map[string]string |
No |
None |
Production |
Hardens operation for usage in production. For instance, controls serving splash page, documentation, x-powered-by header. |
bool |
No |
false |
Timeout |
How long (in seconds) a request can be in flight before we cancel it and return an error |
uint |
No |
60 |
Gzip |
Whether to gzip compress HTTP responses |
bool |
No |
false |
Encrypt |
Configuration for enabling TLS (HTTPS). Don’t specify to operate without encryption (the default) |
No |
None |
|
Health |
Configuration to turn on endpoints for validating the health of the server on a secondary port |
No |
None |
The following can be supplied as environment variables:
| Configuration Parameter | Environment Variable |
|---|---|
BindHost |
SERVER_BINDHOST |
Port |
SERVER_PORT |
RootPath |
SERVER_ROOTPATH |
TilePath |
SERVER_TILEPATH |
Production |
SERVER_PRODUCTION |
Timeout |
SERVER_TIMEOUT |
Gzip |
SERVER_GZIP |
Client
Configures how the HTTP client should operate for tile requests that require calling an external HTTP(s) server.
Configuration options:
| Parameter | Description | Type | Required | Default |
|---|---|---|---|---|
UserAgent |
The user agent to include in outgoing http requests. |
string |
No |
tilegroxy/VERSION |
MaxLength |
The maximum Content-Length to allow incoming responses |
int |
No |
10 MiB |
UnknownLength |
Allow responses that are missing a Content-Length header, this could lead to excessive memory usage |
bool |
No |
false |
ContentTypes |
The content-types to allow remote servers to return. Anything else will be interpreted as an error |
string[] |
No |
image/png, image/jpg |
StatusCodes |
The status codes from the remote server to consider successful |
int[] |
No |
200 |
Headers |
Include these headers in requests |
map[string]string |
No |
None |
RewriteContentTypes |
Replaces |
map[string]string |
No |
{"application/octet-stream": ""} |
The following can be supplied as environment variables:
| Configuration Parameter | Environment Variable |
|---|---|
UserAgent |
CLIENT_USERAGENT |
MaxLength |
CLIENT_MAXLENGTH |
UnknownLength |
CLIENT_UNKNOWNLENGTH |
ContentTypes |
CLIENT_CONTENTTYPES |
StatusCodes |
CLIENT_STATUSCODES |
Log
Configures how the application should log during operation.
The Logging section supports two keys: main and access which are objects described respectively below.
Example:
logging:
access:
console: false
path: access.log
main:
level: debug
format: json
Main Logs
Configures application log messages.
These log messages output in a structured log format, either with Key=Value attributes in plain (text) mode or as JSON. In either mode attributes are available driven by the HTTP request that is being processed. We try to avoid plain mode logs being overly verbose for readability, which means if you want all the attributes you’ll need to explicitly enable them. In JSON mode we assume you’re ingesting them into a system that handles formatting so include more attributes by default.
In order to avoid logging secrets you need to specify the headers to log. If you’re including auth information via the URL (not recommended) you should make sure Request is false to avoid logging those.
Level controls the verbosity of logs. There is no guarantee as to the specific log messages that will be outputted so you might see more or fewer log messages between versions of the application, especially at higher verbosity levels. Here are the general rules of what to expect for each level (from least to most verbose):
-
error: Only messages for things that are definitely a problem with your setup or the application itself. It’s recommended to configure alerts/notifications for error logs and if the issue is not User Error, please open a ticket for it: https://github.com/Michad/tilegroxy/issues
-
warn: Includes messages for things that might be an issue but isn’t critical to the core functioning of the system. For example an issue talking to your configured cache will come through as a warning.
-
info: Includes messages that allow you to see what’s happening in real time but without being overwhelmed with minutiae. Expect one or two log messages per request, including messages indicating requests with something unusual.
-
debug: Includes messages to help understand what’s happening for a given request execution. Expect a few log messages per request. This is more than you probably want in prod but can be useful when first integrating with the system.
-
trace: Includes messages for every level of the application as a request flows between layers. Expect several log messages per request, more for complex setups/layers. Very noisy but shouldn’t be a huge performance impact.
-
absurd: Includes more information than you will probably ever need. In some cases this can produce thousands of messages per request and will have a substantial performance cost.
Configuration options:
| Parameter | Description | Type | Required | Default |
|---|---|---|---|---|
Console |
Whether to write application logs to standard out |
bool |
No |
true |
Path |
The file location to write logs to. Log rotation is not built-in, use an external tool to avoid excessive growth |
string |
No |
None |
Format |
The format to output application logs in. Applies to both standard out and file out. Possible values: plain, json |
string |
No |
plain |
Level |
The most-detailed log level that should be included. Possible values: debug, info, warn, error, trace, absurd |
string |
No |
info |
Request |
Whether to include any extra attributes based on request parameters (excluding explicitly requested). If auto (default) it defaults true if format is json, false otherwise |
bool |
No |
auto |
Headers |
Headers to include as attributes in structured log messages. Attribute key will be in all lowercase. |
string[] |
No |
None |
The following can be supplied as environment variables:
| Configuration Parameter | Environment Variable |
|---|---|
Console |
LOGGING_MAIN_CONSOLE |
Path |
LOGGING_MAIN_PATH |
Format |
LOGGING_MAIN_FORMAT |
Level |
LOGGING_MAIN_LEVEL |
Request |
LOGGING_MAIN_REQUEST |
Headers |
LOGGING_MAIN_HEADERS |
Access Logs
Configures logs for incoming HTTP requests. Primarily outputs in standard Apache Access Log formats.
Configuration options:
| Parameter | Description | Type | Required | Default |
|---|---|---|---|---|
Console |
Whether to write access logs to standard out |
bool |
No |
true |
Path |
The file location to write logs to. Log rotation is not built-in, use an external tool to avoid excessive growth |
string |
No |
None |
Format |
The format to output access logs in. Applies to both standard out and file out. Possible values: common, combined |
string |
No |
common |
The following can be supplied as environment variables:
| Configuration Parameter | Environment Variable |
|---|---|
Console |
LOGGING_ACCESS_CONSOLE |
Path |
LOGGING_ACCESS_PATH |
Format |
LOGGING_ACCESS_FORMAT |
Telemetry
Configures telemetry using OpenTelemetry (OTEL). This configuration is currently limited to turning telemetry on and off, you must use standard OTEL environment variables to point the HTTP exporter to your collector.
More details on Telemetry capabilities can be found in Telemetry.adoc.
Configuration options:
| Parameter | Description | Type | Required | Default |
|---|---|---|---|---|
Enabled |
Turns on/off telemetry |
bool |
No |
false |
The following can be supplied as environment variables:
| Configuration Parameter | Environment Variable |
|---|---|
Enabled |
TELEMETRY_ENABLED |
Error
Configures how errors are returned to users.
There are four primary operating modes:
None: Errors are logged but not returned to users. In fact, nothing is returned to the users besides a relevant HTTP status code.
Text: Errors are returned in plain text in the HTTP response body
Image: The error message itself isn’t returned but the user receives an image indicating the general category of error. The images can be customized.
Image with Header : The same images are returned but the error message itself is returned as a special header: x-error-message.
It is highly recommended you use the Image mode for production usage. Returning an Image provides the most user friendly experience as it provides feedback to the user in the map they’re looking at that something is wrong. More importantly, it avoids exposing the specific error message to the end user, which could contain information you don’t want exposed. "Image with error" is useful for development workflows, it gives the same user experience but allows you to easily get to the error messages.
Error responses set a Content-Type matching the mode: text/plain; charset=utf-8 for Text mode, image/png for Image and Image with Header modes. None mode sets no body and thus no Content-Type.
Every error response also sets Cache-Control: no-store, regardless of mode. This matters most in combination with AlwaysOk below: with AlwaysOk set, an error is returned with HTTP status 200 just like a real tile, so without an explicit no-store a CDN or browser sitting in front of tilegroxy could cache a "tile unavailable" response as if it were a legitimate tile - and keep serving that cached error long after the underlying provider issue is resolved. Cache-Control: no-store prevents that regardless of AlwaysOk.
Configuration options:
| Parameter | Description | Type | Required | Default |
|---|---|---|---|---|
Mode |
The error mode as described above. One of: text none image image+header |
string |
No |
image |
Messages |
Controls the error messages returned as described below |
ErrorMessages |
No |
Various |
Images |
Controls the images returned for errors as described below |
ErrorImages |
No |
Various |
AlwaysOk |
If set we always return 200 regardless of what happens |
bool |
No |
false |
The following can be supplied as environment variables:
| Configuration Parameter | Environment Variable |
|---|---|
Mode |
ERROR_MODE |
AlwaysOk |
ERROR_ALWAYSOK |
Error Images
When using the image or image+header modes you can configure the images you want to be returned to the user. Either use a built-in image or an image provided yourself on the local filesystem via relative or absolute file path.
Configuration options:
| Parameter | Description | Type | Required | Default |
|---|---|---|---|---|
OutOfBounds |
The image to display for requests outside the extent of the layer |
string |
No |
embedded:transparent.png |
Authentication |
The image to display for auth errors |
string |
No |
embedded:unauthorized.png |
Provider |
The image to display for errors returned by the layer’s provider |
string |
No |
embedded:error.png |
Other |
The image to display for all other errors |
string |
No |
embedded:error.png |
The following can be supplied as environment variables:
| Configuration Parameter | Environment Variable |
|---|---|
OutOfBounds |
ERROR_IMAGES_OUTOFBOUNDS |
Authentication |
ERROR_IMAGES_AUTHENTICATION |
Provider |
ERROR_IMAGES_PROVIDER |
Other |
ERROR_IMAGES_OTHER |
Image Options
In addition to an image on the filesystem you can refer to a static color or a built-in image.
There are currently 4 built-in images available:
| Image name | Description | Preview |
|---|---|---|
transparent.png |
A fully transparent image meant to be used for requests outside the valid range of a layer |
|
red.png |
A semi-transparent solid red image |
|
error.png |
A semi-transparent solid red image with the word "Error" in white |
|
unauthorized.png |
A semi-transparent solid red image with the words "Not Authorized" in white |
|
To utilize them prepend "embedded:" before the name. For example embedded:transparent.png
You can also reference any color by including an hex code prefixed by "color:". The color code can be RGB or RGBA and have single or double hex digits. For example the following all generate an identical violet tile: color:FF00FFFF, color:FF00FF, color:F0F, color:F0FF
Error Messages
The templates used for error messages for the majority of errors can be configured. Since tilegroxy is a backend service the main time you see words coming from it is in error messages, so it’s all the more important to be flexible with those words. This is most useful for those whose primary language is not English and want to decrease how often they need to deal with translating. Unfortunately, many lower-level errors can return messages not covered by these string.
The following are currently supported:
NotAuthorized InvalidParam RangeError ServerError ProviderError ParamsBothOrNeither ParamsMutuallyExclusive EnumError



