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

Structure

The following is the top-level configuration structure. All top-level keys are optional besides layers:

server: …​
client: …​
logging: …​
telemetry: …​
error: …​
secret: …​
authentication: …​
cache: …​
datastores:
- …​
analytics:
- …​
layers:
- …​

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

Provider

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

Client

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:

Proxy flow diagram

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 invertY parameter is specified.

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 srid.

xmax

The "east" coordinate of the bounding box defined by the incoming tile coordinates. In the projection specified by srid.

ymin

The "north" coordinate of the bounding box defined by the incoming tile coordinates. In the projection specified by srid. Not impacted by the invertY parameter.

ymax

The "south" coordinate of the bounding box defined by the incoming tile coordinates. In the projection specified by srid. Not impacted by the invertY parameter.

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

env.XXX values come from server-side environment variables and are inserted into the URL as-is, unescaped - this is intentional, since an environment variable is operator-controlled and is the documented way to inject something like an entire base URL (scheme, host, and path) via {env.XXX}.

ctx.XXX and layer.XXX values are request-derived (an HTTP header/context value, or a match against the incoming layer pattern, respectively) and are therefore percent-encoded before being inserted into the URL - path-escaped if the placeholder appears before the first ? in the url template, query-escaped if after it. This prevents a request from injecting a ?, #, or ../ into the upstream URL to rewrite its path or smuggle in extra query parameters. One consequence: if a ctx.XXX or layer.XXX value legitimately needs to contain a /, it will be encoded as %2F rather than treated as a path separator.

For an additional layer of control over what values a layer pattern will accept before they ever reach a placeholder, see paramValidator on the Layer configuration.

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 $xmin $xmax $ymin and $ymax for tile bounds and can also contains $srs $width and $height

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). Blend request flow

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 providers parameter for specifying references to other layers that utilize patterns. Equivalent to specifying a number of Ref providers in providers

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 image with this value with a prefix of "color:"

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.

None

Disables the cache.

Name should be "none" or "test"

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 servers with a single entry. Do not supply both this and servers

String

No

127.0.0.1

port

The port of the memcache server. A convenience equivalent to supplying servers with a single entry. Do not supply both this and servers

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 host and port parameters as a default, therefore this shouldn’t be specified at the same time as those

Array of host and port

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 servers with a single entry. Do not supply both this and servers

String

No

127.0.0.1

port

The port of the redis server. A convenience equivalent to supplying servers with a single entry. Do not supply both this and servers

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 db in avoiding collisions when multiple applications use the same redis

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 standalone, ring or cluster. Standalone is a single redis server. Ring distributes entries to multiple servers without any replication (more details). Cluster is a proper redis cluster.

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 host and port parameters as a default, therefore this shouldn’t be specified at the same time as those

Array of host and port

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 storageclass is 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 tilegroxy will generate a random token at startup and output it in logs

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 native or http

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 max_connections is configured high enough to accommodate this setting

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

Example:

datastores:
  - name: postgresql
    id: pg-database-0
    host: localhost
    user: postgres
    password: password
    database: postgres

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 drop or block. Drop discards the event and logs a periodic warning; block makes the request wait for room

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

ctx.<key>

A value from the request context. Includes ip, path, method, host, query, query-string, proto and uri

hdr.<name>

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)

Encryption

No

None

Health

Configuration to turn on endpoints for validating the health of the server on a secondary port

Health

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 Content-Types that match the key with the value. This is to handle servers returning a generic content type. Mapping to an empty string that will cause tilegroxy to intuit the Content-Type by inspecting the contents - this may be inaccurate for MVT. This only applies after the check that Content-Type is valid according to the ContentTypes parameter meaning your original Content-Type will need to be in both parameters to be used

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

transparent

red.png

A semi-transparent solid red image

red

error.png

A semi-transparent solid red image with the word "Error" in white

error

unauthorized.png

A semi-transparent solid red image with the words "Not Authorized" in white

unauthorized

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