METADATA ======= Title: Docker label reference Description: How Clearplane applies Docker labels, with worked examples and the rules the configuration catalog does not cover. Human URL: /docs/configuration/docker-labels/ Last Updated: 2026-09-28 Clearplane discovers proxied services and standalone redirects from Docker labels. Clearplane-owned containers can also read validated runtime settings from labels. See the [environment variable reference](/docs/configuration/environment-variables/) for bootstrap settings and secrets. > Using an LLM or coding agent? Open the [plain-text Docker label reference](/llms/configuration/docker-labels.txt), the [LLM documentation index](/llms.txt), or the [complete documentation file](/llms/llms-full.txt). The [complete configuration catalog](/docs/configuration/catalog/) lists every label with its type, accepted values, default, and apply behavior. Four labels are required to publish a container over HTTPS. The example also includes the optional `clearplane.proxy.path` label to make its catch-all route explicit. ```yaml services: app: image: example/app:latest labels: clearplane.proxy.enabled: "true" clearplane.proxy.host: "app.example.com" clearplane.proxy.port: "8080" clearplane.proxy.path: "/{**catch-all}" clearplane.proxy.certificate.enabled: "true" ``` The example publishes the container on `app.example.com`, forwards requests to port `8080`, and requests an ACME certificate. ## How labels apply {#how-labels-are-resolved} The shipped Compose file passes its effective project name to Core. When that setting is omitted, Core resolves the project from the running Core container. Clearplane accepts labels on its own containers only when they belong to the verified project. Label names mirror the API concepts in kebab-case under their documented hierarchy, and label values keep the same meaning as the corresponding API/UI field. The transport syntax differs only where labels cannot represent the JSON shape directly: lists are whitespace-delimited, nested policy fields use dotted groups such as `cache.key`, and repeated object entries use indexed labels. The [catalog](/docs/configuration/catalog/) is the canonical name map. Every label value follows these rules: - **Enumerations** are written like the REST API and the UI: `On`, `Prevention`, `MemoryOnly`. Matching ignores case; numeric backing values are not accepted. Fields documented with literal numeric choices, such as redirect status codes, accept those listed numbers instead. - **Booleans** are `true` or `false`, ignoring case. Anything else, such as `yes` or `1`, is invalid. - **Lists** are whitespace-delimited. When the catalog marks a list as unique, duplicate tokens are invalid; labels do not silently collapse them. - **An invalid runtime-setting value** on a Clearplane-owned container is reported as a warning. The setting retains its previous valid label value; when none exists, the invalid label supplies no value and normal source resolution applies. - **An invalid service-resource value** rejects the discovered container and keeps its previous route. An unrecognized owner label on a Clearplane container (`clearplane.core.*`, `clearplane.edge.*`, `clearplane.ui.*`, or `clearplane.container-proxy.*`) is ignored and reported as a warning. It does not replace or preserve the known runtime label it may have been intended to spell: removing a known label removes its override, so the setting resumes the next source in the documented resolution order. An unrecognized `clearplane.*` service-resource key on a discovered application or redirect container rejects that container and keeps its previous route. Unrecognized keys under `clearplane.proxy.cache.*`, `compression.*`, `error-page.*`, `group.*`, `redirect.*`, and `clearplane.redirect.group.*` cannot weaken protection. Clearplane ignores them, builds the route from the remaining labels, and reports each ignored key as a warning in the container's status dialog and in Logs. Cache-key labels (`clearplane.proxy.cache.key.*`) are the exception: a lost `key.headers` entry could share responses between users, so an unknown cache-key label rejects the container. The [catalog](/docs/configuration/catalog/#resolution-and-ownership) gives the resolution order. [Configuration ownership](/docs/concepts/configuration-ownership/#docker-label-ownership) describes what label changes and removal do to discovered resources, and [Policies and scope](/docs/concepts/policies-and-scope/#route-policy-state) describes how a route's policy state selects its policies. ## Proxy route labels {#proxy-route-labels} Apply these labels to an application container that Clearplane should publish. ### Match paths {#proxy-route-paths} `clearplane.proxy.path` is the same `path` field stored in the database and shown by the route UI and REST API. Clearplane preserves the complete route pattern; it does not append or remove segments. The default is `/{**catch-all}`. | Value | Matches | |---|---| | `/api` | Only the literal `/api` path | | `/api/{**catch-all}` | `/api`, `/api/orders`, and everything below `/api` | | `/{**catch-all}` | Every path on the route's host | For example: ```yaml labels: clearplane.proxy.path: "/api/{**catch-all}" ``` The equivalent REST request uses `"path": "/api/{**catch-all}"`, the UI field displays `/api/{**catch-all}`, and the `ProxyRoutes.Path` database field stores `/api/{**catch-all}`. ### Policy labels {#policy-labels} Every policy family has one root label that sets the route's state for that family: ```yaml clearplane.proxy.: Inherit | On | Off # default: Inherit ``` | Root label | Policies applied to the route | Other `clearplane.proxy..*` labels | |---|---|---| | missing | Same as `Inherit` | Present: the container is rejected. Add `clearplane.proxy.: On`. | | `Inherit` | The family's enabled Global policy or policies, if any | Kept and ignored | | `On` | The route's own policies, from `.policy` or from inline settings. They replace the Global ones. | Used | | `Off` | None, even when a Global policy exists | Kept and ignored | The families are `access-control`, `authentication`, `auto-ban`, `cache`, `compression`, `cors`, `rate-limit`, `request-headers`, `response-headers` and `waf`. - `.policy` references existing route-scoped policies by system name. Cache, CORS, compression, request headers, response headers and WAF accept one name. Access control, authentication, automatic bans and rate limit accept a space-separated list. - Inline settings create a policy for this route alone. A setting you leave out takes its documented default; values are never copied from the Global policy. - `On` without `policy` or inline settings creates an inline policy with every setting at its default. Authentication is the exception: it has no meaningful defaults, so an inline authentication policy needs `type` and its credentials or signing keys. - `clearplane.proxy.authentication.combine-mode` (`Or` or `And`) is a route setting, not part of a policy, so it can be combined with `authentication.policy`. - Combining `policy` with inline settings rejects the container, and so does naming a policy twice. - A referenced policy that is disabled stops applying to the route, and discovery reports a warning. The route never falls back to the Global policy. Reference existing policies: ```yaml labels: clearplane.proxy.access-control: "On" clearplane.proxy.access-control.policy: "office-only" clearplane.proxy.authentication: "On" clearplane.proxy.authentication.policy: "partner-api-keys" clearplane.proxy.auto-ban: "On" clearplane.proxy.auto-ban.policy: "scanner-bans" clearplane.proxy.cache: "On" clearplane.proxy.cache.policy: "static-assets" clearplane.proxy.compression: "On" clearplane.proxy.compression.policy: "web-responses" clearplane.proxy.cors: "On" clearplane.proxy.cors.policy: "app-origins" clearplane.proxy.rate-limit: "On" clearplane.proxy.rate-limit.policy: "login-throttle api-burst" clearplane.proxy.request-headers: "On" clearplane.proxy.request-headers.policy: "tenant-headers" clearplane.proxy.response-headers: "On" clearplane.proxy.response-headers.policy: "security-headers" clearplane.proxy.waf: "On" clearplane.proxy.waf.policy: "strict-api" ``` Define a policy inline, or turn a family off for one route: ```yaml labels: clearplane.proxy.cors: "On" clearplane.proxy.cors.allowed-origins: "https://app.example.com" clearplane.proxy.rate-limit: "On" clearplane.proxy.rate-limit.request-limit: "300" clearplane.proxy.rate-limit.window-seconds: "60" clearplane.proxy.waf: "Off" ``` ### Preserve the original hostname {#preserve-host-header} For applications that strictly validate their public hostname, add this label to the application container: ```yaml labels: clearplane.proxy.preserve-host-header: "true" ``` Additional hosts and wildcard routes preserve the actual requested hostname. For example, a request for `api.example.com:8443` reaches the container with `Host: api.example.com:8443`. The label does not change the destination address or disable upstream certificate validation. For routes managed through the UI, enable **Preserve host header** in the route form. ### Upstream activity timeout {#activity-timeout} To allow up to five minutes of inactivity for one application, add this label to its container: ```yaml labels: clearplane.proxy.upstream.activity-timeout-seconds: "300" ``` The value is a whole number from `1` through `86400`, matching the UI/API `activityTimeoutSeconds` field. Omitting it uses the documented 60-second default. ### Web application firewall — `clearplane.proxy.waf.*` {#proxy-waf} > **Experimental:** These settings expose the experimental web application firewall. Use [service automatic bans](/docs/guides/protect-service/) for behavior-based protection. WAF settings live in [WAF policies](/docs/security/waf-rulesets/#waf-policies). A container without `clearplane.proxy.waf` inherits the enabled Global policy, which is **Default WAF** in Detection on a new installation, and `clearplane.proxy.waf: "Off"` opts the route out. With `clearplane.proxy.waf: "On"`, `clearplane.proxy.waf.policy` assigns an existing Route policy by system name. Otherwise the WAF labels create an inline policy named `-waf` that the container owns. `waf.mode` defaults to `Detection`, which records findings; `Prevention` blocks. ```yaml labels: clearplane.proxy.waf: "On" clearplane.proxy.waf.mode: "Prevention" clearplane.proxy.waf.opt-out-rulesets: "clearplane-scanners" clearplane.proxy.waf.request-body.maximum-bytes: "4194304" clearplane.proxy.waf.request-context.header-capture-mode: "RedactSensitive" ``` The inline policy's labels mirror the policy form, grouped by section: | Section | Labels | |---|---| | Scoring | `mode`, `profile`, `scoring-mode`, `threshold`, `paranoia-level`, `detection-paranoia-level` | | Rulesets | `opt-out-rulesets`, `exclusion-rulesets`, `allowed-methods`, `allowed-request-content-types` | | `request-body.*` | `inspection-mode`, `maximum-bytes`, `oversize-action`, `maximum-spooled-bytes` | | `websocket.*`, `grpc.*` | `websocket.enabled`, `websocket.maximum-message-bytes`, `grpc.maximum-message-bytes`, `grpc.maximum-messages` | | `response.*` | `inspection`, `threshold`, `maximum-body-bytes` | | `challenge.*` | `mode`, `threshold`, `difficulty`, `clearance-minutes` | | `request-context.*` | `enabled` and `header-capture-mode`, which choose what the route's [detections record](/docs/security/waf-detections/#request-context-and-redaction) | The [catalog](/docs/configuration/catalog/#proxy-directives) lists each label's accepted values and default. Disabled rules, target exclusions and per-ruleset thresholds have no labels, because labels cannot address an individual rule. Set them on the inline policy under **Firewall → WAF policies**; saving there changes only those three, and discovery keeps them when it updates the policy. Every labelled setting stays owned by the container. Opting a whole ruleset out stays a label (`clearplane.proxy.waf.opt-out-rulesets`). ### Caching — `clearplane.proxy.cache.*` {#proxy-caching} Caching is a reusable policy. Create Global or Route policies in **Performance → Cache policies**. One enabled Global policy caches every proxied route that inherits caching; a route's own policy replaces it. A discovered proxy route can select an existing Route policy by system name: ```yaml labels: clearplane.proxy.cache: "On" clearplane.proxy.cache.policy: "static-assets" ``` Omit `policy` to create an inline policy named `-cache` from the route's other `clearplane.proxy.cache.*` labels. A label you leave out takes its [catalog](/docs/configuration/catalog/#proxy-directives) default, and an invalid value rejects the container. A policy with no other settings caches like a CDN. With `eligibility: StaticFiles`, only paths ending in one of the `file-extensions` use the cache; the catalog lists the default set, which includes `wasm` but not `html` or `json`. Every other request reaches the upstream and is reported as `DYNAMIC`. Set `eligibility: Everything` to cache HTML, API and extensionless paths as well. Under `StaticFiles`, a response is not stored when its `Content-Type` is HTML, JSON, XML or plain text and the extension does not name that format, so an application that answers `/account/profile.css` with the profile page cannot have that page cached (`BYPASS / ContentTypeMismatch`). `path-rules` gives parts of an application their own caching. It is an ordered, whitespace-separated list of `pattern=action` rules, and the first rule whose pattern matches the request path applies. A pattern starts with `/`, and `*` matches any run of characters, including `/`: `/api/*` matches `/api/` and `/api/v1/orders` but not `/api`, and `/` matches only the root. `cache` caches the path whatever the eligibility and extensions say, and skips the extension/content-type check. `bypass` never caches it (`DYNAMIC`). A `cache` rule can add `ttl=` to replace the default lifetime and `ignore-upstream-cache-control` to discard the upstream's lifetime and `no-cache`. Any rule can add `browser-ttl=` to send `Cache-Control: max-age=` to the client. Per-status lifetimes, bypass cookies, `Authorization` handling and the `private`, `no-store` and `Set-Cookie` stops still apply. ```yaml labels: clearplane.proxy.cache: "On" clearplane.proxy.cache.path-rules: "/_framework/*=cache,ttl=31536000,ignore-upstream-cache-control /api/*=bypass /=cache" ``` Every route that uses a policy shares its storage pool. `maximum-total-size-megabytes` sizes that pool for all of those routes together, and eviction removes the least recently used entries across the pool. Entries stay separate per route, so one route never serves another route's response. The global per-tier cache limit counts each enabled policy once, however many routes use it. If an inline policy would exceed that limit, Clearplane saves it disabled, keeps the route's cache state On, and reports a warning in the container's status dialog. The route is not cached until capacity is available on a later discovery pass. A response that declares its own lifetime through `Cache-Control`, `CDN-Cache-Control`, `Clearplane-CDN-Cache-Control` or `Expires` is clamped into the range between the minimum and maximum lifetimes. A response that declares none is held for the default lifetime, or for a matching `status-codes-time-to-live` entry. Precedence follows HTTP: `s-maxage` wins over `max-age`, which wins over `Expires`. Following RFC 9213, an upstream can address Clearplane alone with `Clearplane-CDN-Cache-Control`. When it is present and valid, its directives replace `Cache-Control` for Clearplane's lifetime, `no-cache`, revalidation, stale windows and `Authorization` consent. `CDN-Cache-Control` does the same with lower precedence while `honor-cdn-cache-control` is on. `Clearplane-CDN-Cache-Control` never reaches the client; `CDN-Cache-Control` is passed through. `private` or `no-store` in `Cache-Control` still stops storage. The browser-only pattern is `Cache-Control: no-cache` with `Clearplane-CDN-Cache-Control: max-age=3600`: Clearplane caches for an hour and answers the browser's revalidations from cache. An upstream `stale-while-revalidate` or `stale-if-error` directive replaces the policy's window for that response. `must-revalidate`, `proxy-revalidate` and `s-maxage` forbid serving a stale copy unless the response also carries one of those stale directives. `ignore-upstream-cache-control` keeps the policy's windows. `ignore-upstream-cache-control` discards a declared lifetime; `require-upstream-cache-control` caches only responses that declare one. The two cannot both be set. Neither overrides `no-store`, `private` or `Set-Cookie`, which are never stored. Request cookies neither skip the cache nor enter the cache key, and they still reach the upstream. List cookie names in `bypass-cookies` to send requests carrying them straight to the upstream; a trailing `*` matches a name prefix and `*` alone matches any cookie. A request with `Authorization` uses and stores only responses the upstream marked `public`, `s-maxage` or `must-revalidate`. A stored response is served to every request that passes the route's access checks, so an upstream that protects static files with its own login cookie must send `Cache-Control: private` or have that cookie listed in `bypass-cookies`. A visitor's `Cache-Control: no-cache` or `no-store` does not skip the cache unless `ignore-request-no-cache` or `ignore-request-no-store` is turned off. Clearplane answers `If-None-Match` and `If-Modified-Since` from the cache with `304 Not Modified` and never forwards them when it fills the cache. It revalidates a stale entry, or one stored with `no-cache`, by sending the entry's `ETag` and `Last-Modified` to the upstream; a `304` renews the entry without downloading the body and is reported as `REVALIDATED`. Requests with `If-Match` or `If-Unmodified-Since` bypass the cache, and so do requests carrying `X-Original-URL`, `X-Rewrite-URL`, `X-HTTP-Method-Override`, `X-HTTP-Method`, `X-Method-Override` or `X-Host`, which some frameworks use to serve a different resource than the URL names. Byte ranges are answered from cache. A GET with a single `bytes` range receives `206 Partial Content` from a stored `200` response, or `416` when the range lies beyond it; a request for several ranges receives the full response. `If-Range` must match the stored `ETag` by strong comparison, or equal the stored `Last-Modified`. When nothing is stored yet, the range request goes to the upstream unchanged (`BYPASS / Range`), so a video player never waits for the whole file. If the upstream answers `206` with a complete length within `maximum-response-size-megabytes`, Clearplane fetches the full object in the background, and later ranges are hits. Clearplane records the tags the upstream lists in `Cache-Tag` (comma-separated) and `Surrogate-Key` (space-separated), ignoring case, and removes both headers from client responses. A deploy can then purge every entry carrying a tag, from the cache page, `POST /api/cache/purge` or [`clearplane cache purge tag`](/docs/cli/#purge-the-cache). A response with a tag longer than 1,024 characters or more than 16 KB of tags is not stored (`BYPASS / CacheTagLimit`), because a partial tag list would let a purge miss it. With `purge-on-redeploy` (on by default), Clearplane purges a discovered route's cached responses when discovery sees its container recreated, for example after `docker compose up` with a new image. A restart of the same container does not purge. With `vary-handling: Honor`, Clearplane stores a separate copy for each combination of the request headers the upstream lists in `Vary`, up to 32 per URL; further combinations reach the upstream without being stored. `Ignore` stores one copy regardless of `Vary`. `Vary: *`, `Cookie` or `Authorization` is never stored in either mode. `Accept-Encoding` and the headers in `key.headers` are already part of the cache key. For an image that varies on `Accept`, Clearplane stores one copy per image-format bucket instead of per `Accept` value: the set of AVIF, WebP and JPEG XL the browser accepts, so at most eight copies. The upstream still receives the browser's own `Accept`. Add `Accept` to `key.headers` to key on the raw value instead. A cacheable request reaches the upstream with `Accept-Encoding` reduced to `br`, `gzip`, or `identity`, and Clearplane caches the Brotli, gzip, and plain responses separately, so precompressed assets are fetched once per encoding. When the upstream answers a Brotli or gzip request with a plain body, that plain copy serves every client until it expires. Without a compression policy, requests that bypass the cache keep the client's `Accept-Encoding`. [Compression](#proxy-compression) adds Clearplane's own compression on top. ### Compression — `clearplane.proxy.compression.*` {#proxy-compression} Compression is a reusable policy, independent from response caching. Create Global or Route policies in **Performance → Compression policies**. One enabled Global policy can provide the default for every proxied route that inherits compression; a route's own policy replaces it. A discovered proxy route can select an existing Route policy by system name. A route without `clearplane.proxy.compression` inherits the Global policy: ```yaml labels: clearplane.proxy.compression: "On" clearplane.proxy.compression.policy: "web-responses" ``` In `content-types`, exclusions always win, regardless of order, and response parameters such as `charset=utf-8` are ignored while matching. With an effective policy, Clearplane reduces the client's `Accept-Encoding` to the one representation it selected — `br`, `gzip`, or `identity` — and forwards that value upstream. A Brotli or gzip response that matches the forwarded value is served as the upstream sent it, and on a cache-enabled route it is cached under that encoding, so precompressed assets are fetched once per encoding and never compressed again. A plain response is compressed by Clearplane; on a cache-enabled route Clearplane derives compressed copies from the cached plain copy only after the upstream has answered a Brotli or gzip request with it. A plain copy fetched for a client that accepts neither never stands in for the upstream's own compressed file. Clients that accept neither Brotli nor gzip never receive a cached compressed copy; Clearplane asks the upstream for a plain one. A response in any other encoding, including one Clearplane does not support, passes through unchanged and is never cached. `HEAD`, bodyless and partial responses, `Cache-Control: no-transform`, missing or excluded content types, and bodies below the minimum length remain uncompressed. If an effective policy exists and a non-empty body-bearing upstream response is known, Clearplane returns `406 Not Acceptable` when the client rejects Brotli, gzip, and identity. When no compression policy applies, Clearplane leaves compression to the upstream service; on a cache-enabled route, cacheable requests still use the reduced `Accept-Encoding` described under [Caching](#proxy-caching). ### Request headers — `clearplane.proxy.request-headers.*` {#proxy-request-headers} Each header transform is a complete indexed entry. `Set` and `Append` require `value`; `Remove` requires it to be omitted: ```yaml labels: clearplane.proxy.request-headers: "On" clearplane.proxy.request-headers.entries.1.header-name: "X-Tenant" clearplane.proxy.request-headers.entries.1.action: "Set" clearplane.proxy.request-headers.entries.1.value: "example" clearplane.proxy.request-headers.entries.3.header-name: "X-Legacy" clearplane.proxy.request-headers.entries.3.action: "Remove" ``` ## Standalone redirect labels {#standalone-redirect-labels} Apply these labels to a container that declares an external redirect. The container needs no published port and does not have to join the proxy network. Clearplane answers HTTP-01 challenges before applying the redirect. ```yaml services: old-domain: image: alpine:latest command: ["sleep", "infinity"] labels: clearplane.redirect.enabled: "true" clearplane.redirect.source-host: "old.example.com" clearplane.redirect.additional-source-hosts: "www.old.example.com" clearplane.redirect.path: "/{**catch-all}" clearplane.redirect.target-uri: "https://new.example.com" clearplane.redirect.preserve-path-and-query: "true" clearplane.redirect.certificate.enabled: "true" ``` `clearplane.redirect.path` has the same exact-pattern contract and `/{**catch-all}` default as `clearplane.proxy.path`. `methods` limits the redirect to the listed HTTP methods, and `status-code` chooses 301, 302, 307 or 308 (default 308). A container cannot enable both `clearplane.proxy.enabled` and `clearplane.redirect.enabled`. ## Gateway runtime labels {#gateway-runtime-labels} Clearplane's own containers accept runtime settings as labels. A label locks the matching field in **System → Settings** until you remove it. - **Core container:** `clearplane.core.*`, for example the Let's Encrypt account, retention periods and country geolocation. - **Edge container:** `clearplane.edge.*`, for example trusted proxies, connection and request limits, cache capacity and the HTTPS protocols (`clearplane.edge.http1.enabled`, `http2.enabled`, `http3.enabled`). ```yaml services: clearplane-edge: labels: clearplane.edge.trusted-proxies: "203.0.113.0/24" clearplane.edge.http1.enabled: "false" ``` The management UI and API routes are ordinary proxy routes on the Core and UI containers. The shipped Compose file gives both one YAML anchor with their policy labels; set `CLEARPLANE_MANAGEMENT_ALLOW_ADDRESSES` to the addresses that may reach them. See [Exposure and networks](/docs/security/exposure-and-networks/#management-route-defaults).