Skip to content
Talk to our solutions team

Gateway Configuration

Embedded examples cover every feature, start from these:

Terminal window
gateway.svc config list # 17 annotated examples
gateway.svc config generate quickstart # print one
gateway.svc config generate all -d dir/ # write them all

Profiles include: bootstrap, quickstart, common, single-app, multi-tenant, static-site, spa, spa-http, acme-http01, acme-dns01, dashboard, multi-port, url-rewrite, headers-expr, load-balancing, ratelimit, exposure.

Before deploying any of them, gateway.svc config exposure -f .gateway.yaml prints what the configuration publishes per zone and fails if the gateway would refuse it.

Top level of the bootstrap file (-f, default .gateway.yaml):

KeyDefaultPurpose
name, host, port—Identity + public listener
internal.port—Mesh listener (required unless entrypoints: is used)
internal.trustedips—Peers allowed to set X-Forwarded-Host
entrypoints:—Custom listener list (replaces port/internal): per-entry `name, address, protocol (http
timeouts.read/write/idle10s / 0s / 180sListener-level fallbacks. A write timeout above 0 cuts long-lived streams
zerotrust—mTLS: client certs on the internal listener + mTLS on the gateway→backend leg
svccert, svckey, rootcacert—Service certificate material
gatewayrole.cert, gatewayrole.key—Gateway role credential presented to backends, so they see the original caller (Exposure)
jwt_public_key—Key for local JWT verification (kisai-auth)
config.url / config.apikey / config.cache—Remote config from config.svc (SSE hot-reload)
config.path—Local chassis service-config folder
config.router.dir—Gateway-only: local directory of routing YAML
servicediscovery_url / servicediscovery_apikey—Discovery client for the backend watcher
provider.kisai.throttleduration15sRouter config push throttle
provider.kisai.watcher.refreshinterval5sDiscovery poll interval
router.loglevel—Embedded router’s own log verbosity, independent of loglevel
log, loglevel—stdout or fluentd host:port; level
vault.url, datacenter, cluster, sid—Platform wiring
domainmaps:
- name: acme-prod
zone: external # external | internal
domains: [app.acme.com, www.acme.com] # MUST be a list, not a scalar
customer: acme
product: forge
environment: prod
tenant: main
corsconfig: # Traefik headers keys
accessControlAllowMethods: [GET, POST]
accessControlAllowOriginList: ["https://app.acme.com"]
backends:
- backend: # note the nested wrapper
name: forge-api
urls: ["https://10.0.0.11:7102", "https://10.0.0.12:7102"]
https: true
profile: steady # optional LB profile
# identity: "spiffe://acme.com/dc1/c1/forge-api/*" # pin the server certificate
# sensitive: true # internal by default (vault, horus, config, meta are)
# allow / deny # default patterns for its endpoints
endpoints:
- name: api
path: { prefix: /api }
backend: forge-api
exposure: both # internal | external | both
allow: # publish only these paths under /api
- { path: "/v1/**", methods: [GET, POST] }
deny: ["/v1/admin/**"]
# cors: false | { ... } # replaces the domainmap's corsconfig
# sources: [10.0.0.0/8] # client addresses allowed
# csrf: { trustedorigins: [...], bypass: [...] } | false (on by default)
middlewares: [kisai-auth, customer-domain-mapper]
rewrite:
stripprefix: [/api]
headers:
request:
set: { X-Request-Src: "${gateway.cluster}" }
security_headers: strict # strict | relaxed | none
routes:
- name: acme-public
domainmap: acme-prod
entrypoint: websecure
certresolver: letsencrypt # or tlsoptions / tlspassthrough
endpoints: [api]
staticpaths: [webapp]
extramiddlewares: [strict-rate-limit]
staticpaths:
- name: webapp
type: spa # spa = index fallback; static = 404 page
folderpath: ./public
indexpath: index.html
path: { prefix: / }

Rules enforced at load: redirect: is mutually exclusive with backend:/rewrite:; rewrite.path is exclusive with strip/add prefix; middleware order in middlewares: is the execution order. The exposure keys (allow, deny, exposure, zone, sensitive, cors, sources, csrf, identity) have their own rules and refusals: see Exposure.

Header values accept ${…} expressions from the namespaces request.*, tenant.*, auth.*, response.*, upstream.*, gateway.*. A missing variable resolves empty and the header is stripped; CRLF is stripped (request-smuggling defense).

loadbalance:
default: steady
profiles:
steady:
algo: wrr # wrr | sticky | weighted | mirror | failover | adaptive
healthcheck: { path: /health, interval: 10s }
canary:
algo: adaptive
formula: available_capacity # inflight_count | available_capacity | cpu_then_memory

Adaptive profiles run a weight-reconciliation controller per backend, restarted on every reload.

With config.router.dir, every *.yaml/*.yml in the directory is merged:

  • List keys (routes, domainmaps, backends, endpoints, staticpaths, tlsoptions) concatenate; everything else last-wins.
  • A file’s namespace: prefixes its declared names and bare same-file references (forge.tenant-a); dotted cross-file references pass through untouched; tlsoptions names are never namespaced.
  • Boot-plane keys (port, entrypoints, zerotrust, config, …) found in routing files are stripped with a warning; a bootstrap file physically inside the dir is skipped.
  • Validation is fail-fast: duplicate names, list-type mismatches, unresolved route→domainmap/endpoint/staticpath/tlsoptions references, unresolved endpoint→backend references, unknown certresolver or extramiddlewares: all abort the (re)load.

Anything the kis.ai model doesn’t cover can be declared verbatim under traefik.static / traefik.dynamic, e.g. ACME resolvers, an :80 redirect entrypoint, or a rate-limit middleware:

traefik:
dynamic:
http:
middlewares:
strict-rate-limit:
rateLimit: { average: 100, burst: 50 }

Attach passthrough middlewares to routes via extramiddlewares:. Security-sensitive paths (e.g. the insecure dashboard API) are protected and rejected at boot.

  • External: per route: certresolver (ACME), tlsoptions (custom), or tlspassthrough. Works with or without zerotrust.
  • Internal (zerotrust): zerotrust: true requires client certificates on websecureinternal and uses mTLS on the gateway→backend leg. A route on an entrypoint with zone: internal must name a tlsoptions profile with clientAuth.clientAuthType: RequireAndVerifyClientCert. The tlsoptions names default and kisaitlsinternal are reserved.