Traefik#

If Traefik is the ingress controller of your cluster, nplus offers two ways to publish an instance. Both are fully supported — they differ in what kind of objects the charts create, and therefore in how much of Traefik you can reach from the values.

backend: traefikprovider: traefik
ObjectsIngress + a few MiddlewareIngressRoute, Middleware, ServersTransport
Configured byannotations on the Ingressnative CRD fields
Sticky cookieannotation, name onlynative, incl. maxAge, path, sameSite
Body size, timeoutsnot availablemaxRequestBodyBytes, responseHeaderTimeout
CORS, rate limitingnot availableper component
Own middlewaresnot attachableingress.traefik.middlewares
Denied pathsrouted to the void serviceanswered with 403
Portable to another controlleryes, it stays an Ingressno, Traefik only

Rule of thumb: take the annotations variant if you want to keep the manifests portable and only need the basics. Take the native variant if you actually want to configure Traefik — body limits, timeouts, CORS, rate limiting — or if you want to attach middlewares of your own.

Both variants can be mixed per component: ingress.provider may be overridden on any single component, and ingress.enabled: false still takes a component out of the nplus routing completely, so you can route it yourself.

1. Kubernetes Ingress with Traefik annotations#

The classic way: the charts create a standard Ingress object per component and describe the behavior in Traefik annotations. Switch the backend — the provider stays ingress:

global:
  ingress:
    provider: ingress
    backend: traefik
    class: traefik

What the charts generate for a component:

  • traefik.ingress.kubernetes.io/service.serversscheme for the backend protocol
  • sticky cookie annotations, if ingress.cookie is set
  • a Middleware per component for those features that have no annotation: the appRoot redirect, an inputPath/rewriteTarget rewrite and the whitelist — bound to the Ingress via router.middlewares

Note that these middlewares are created per component: five components with an appRoot also mean five identical redirect middlewares. Anything Traefik can do beyond that (body limits, timeouts, CORS, rate limits) is not reachable from the values here — that is what the native variant is for.

See annotations.yaml.

2. Native Traefik CRDs#

Here the charts skip the Ingress object and write Traefik resources directly:

global:
  ingress:
    provider: traefik
    class: traefik

Every component renders its own IngressRoute — just like it renders its own Ingress in the other variants, so a single component chart still works on its own. Traefik merges all IngressRoutes of a namespace into one routing table anyway.

What exists once per instance are the cross cutting middlewares, rendered by the instance chart and referenced by the components by name:

global:
  ingress:
    appRoot: /nscale_web              # redirect middleware + a route for the host root
    whitelist: "10.0.0.0/8"           # ipAllowList middleware
    traefik:
      maxRequestBodyBytes: 52428800   # buffering middleware
      responseHeaderTimeout: 600s     # ServersTransport

Per component, a middleware is only created where the content really differs:

cmis:
  ingress:
    traefik:
      cors:
        enabled: true
        allowOrigins:
          - https://apps.example.com
      rateLimit:
        enabled: true
        average: 100
        burst: 200
      # attach middlewares you created yourself
      middlewares:
        - name: my-own-middleware

Sticky sessions are configured natively on the route service. The cookie name and sameSite come from the values you already use (ingress.cookie, ingress.sameSite), everything else is merged in from ingress.traefik.sticky:

web:
  ingress:
    cookie: XtConLoadBalancerSession
    traefik:
      sticky:
        maxAge: 86400
        path: /nscale_web

Denied paths (ingress.deny) become their own rules with a blocking middleware and are answered with 403. Traefik prefers the longer rule, so they always win over the component route — without depending on the void service.

If the generated routes do not cover a case, append raw routes; they are rendered verbatim into an IngressRoute of their own:

global:
  ingress:
    traefik:
      extraRoutes:
        - match: Host(`demo.example.com`) && PathPrefix(`/custom`)
          kind: Rule
          services:
            - name: my-backend
              port: 8080

See native.yaml.

TLS#

In both variants TLS stays where it always was: the certificate is the one of ingress.secret (by default <domain>-tls), created by your issuer or by the chart itself. See the certificates sample.

A component that overrides ingress.domain gets its own host rule and references its own secret — make sure a matching certificate exists in that case.

Files#

Download all files of this sample

annotations.yaml#

global:
  ingress:
    # keep the standard Kubernetes Ingress object ...
    provider: ingress
    # ... but let the chart write Traefik annotations instead of NGINX ones
    backend: traefik
    class: traefik
    # the host root is redirected to the web client by a generated middleware
    appRoot: /nscale_web
    # rendered as an ipAllowList middleware, not as an annotation
    whitelist: "10.0.0.0/8,192.168.0.0/16"

web:
  ingress:
    # rendered as traefik sticky cookie annotations on the service
    cookie: XtConLoadBalancerSession
    sameSite: none

nappl:
  ingress:
    enabled: true
    # denied paths are routed to the void service, which has no endpoints
    deny:
      - /nscalealinst1/webc/configuration
      - /nscalealinst1/webb/configuration

build.sh#

#!/bin/bash
#
# This sample script builds the example as described. It is also used to build the test environment in our lab,
# so it should be well tested.
#

# Make sure it fails immediately, if anything goes wrong
set -e

# -- ENVironment variables:
# CHARTS: The path to the source code
# DEST: The path to the build destination
# SAMPLE: The directory of the sample
# NAME: The name of the sample, used as the .Release.Name
# KUBE_CONTEXT: The name of the kube context, used to build this sample depending on where you run it against. You might have different Environments such as lab, dev, qa, prod, demo, local, ...

# Check, if we have the source code available
if [ ! -d "$CHARTS" ]; then
    echo "ERROR Building $SAMPLE example: The Charts Sources folder is not set. Please make sure to run this script with the full Source Code available"
    exit 1
fi
if [ ! -d "$DEST" ]; then
    echo "ERROR Building $SAMPLE example: DEST folder not found."
    exit 1
fi
if [ ! -d "$CHARTS/instance" ]; then
    echo "ERROR Building $SAMPLE example: Chart Sources in $CHARTS/instance not found. Are you running this script as a subscriber?"
    exit 1
fi

# Set the Variables
SAMPLE="traefik"
NAME="sample-$SAMPLE"

# Output what is happening
echo "Building $NAME"

# Create the manifests - one per variant
mkdir -p $DEST/instance
for VARIANT in annotations native; do
helm template --debug \
     --values $SAMPLES/application/empty.yaml \
     --values $SAMPLES/environment/$KUBE_CONTEXT.yaml \
     --values $SAMPLES/resources/$KUBE_CONTEXT.yaml \
     --values $SAMPLES/$SAMPLE/$VARIANT.yaml \
     $NAME-$VARIANT $CHARTS/instance > $DEST/instance/$SAMPLE-$VARIANT.yaml
done

native.yaml#

global:
  ingress:
    # native Traefik CRDs instead of a Kubernetes Ingress object
    provider: traefik
    class: traefik
    appRoot: /nscale_web
    traefik:
      entryPoints:
        - websecure
      # one shared buffering middleware for the whole instance (50 MiB)
      maxRequestBodyBytes: 52428800
      # one shared ServersTransport for the whole instance
      responseHeaderTimeout: 600s

web:
  ingress:
    # the sticky cookie is configured natively on the route service,
    # derived from ingress.cookie / ingress.sameSite
    cookie: XtConLoadBalancerSession
    traefik:
      sticky:
        maxAge: 86400

cmis:
  ingress:
    traefik:
      # dedicated CORS middleware, only for the cmis route
      cors:
        enabled: true
        allowOrigins:
          - https://apps.example.com

nappl:
  ingress:
    enabled: true
    # denied paths are answered with 403 by a blocking middleware
    deny:
      - /nscalealinst1/webc/configuration
      - /nscalealinst1/webb/configuration