Traefik Ingress
Introduction
This page describes how to use various features of Traefik as an Ingress Controller in your Kubernetes cluster. These are the most common examples we notice when helping our customers to set up Ingresses.
Pre-requisites
First determine whether you want to use Traefik for your Ingress, we offer several options depending on your needs: Explanation: Ingress.
The Traefik Ingress Controller(s) also need to be deployed in your cluster. Usually this is determined and set-up during customer onboarding, however you can verify if Traefik is enabled by checking your Cluster Definition files for the following snippets:
apiVersion: skyscrapers.eu/v1beta2
kind: EksCluster
metadata:
[...]
spec:
[...]
traefik:
public:
enabled: true
[...]
internal:
enabled: true
[...]To ensure Traefik is correctly deployed, get in touch with our support team. By default you should have at least the internal-only controller deployed, as this is used for dashboarding like Grafana etc.
General usage
To create a basic Internet-facing Ingress with Traefik, you need to create an Ingress resource with the appropriate ingressClassName. Below is an example of a simple Ingress that routes traffic for foo.example.com to a backend Service named foo in the default namespace:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
name: my-public-ingress
namespace: default
spec:
ingressClassName: traefik
rules:
- host: foo.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service: # you need to point this to your app's K8s Service
name: foo
port:
name: http
tls:
- secretName: foo-example-com-tls
hosts:
- foo.example.comThere are a couple of other things that are happening in the background to make this work:
- The
cert-manager.io/cluster-issuer: letsencrypt-prodannotation tells cert-manager to automatically issue a TLS certificate for this Ingress using Let’s Encrypt. The certificate will be requested for the specifiedhostsand stored in thefoo-example-com-tlsSecret. Further examples on how to use cert-manager with Traefik check our Cert-Manager How To Guide. - Through external-dns we will automatically create a DNS A record for
foo.example.compointing to the Traefik load balancer. This requires that the DNS zone forexample.comis hosted in Route53 in the same AWS account as the cluster.- If you don’t wish to auto-create a DNS record for this Ingress, you can add the annotation
external-dns.alpha.kubernetes.io/exclude: "true"to disable this for this specific Ingress, for example when using an external CDN like CloudFront or Cloudflare.
- If you don’t wish to auto-create a DNS record for this Ingress, you can add the annotation
- By default we will listen on both HTTP and HTTPS, where HTTP traffic is automatically redirected to HTTPS. You can disable this behaviour as described below.
Internal vs Internet-facing Ingress
The Skyscrapers platform supports both internal and Internet-facing Ingresses with Traefik. Internal-only Ingresses are only exposed within the AWS VPC, while Internet-facing Ingresses are exposed to the public Internet.
You can specify this by setting the appropriate IngressClassName when creating your Ingress resource:
traefikfor Internet-facing Ingressestraefik-internalfor internal-only Ingresses
Internal-only example:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
traefik.ingress.kubernetes.io/router.middlewares: default-ip-allowlist@kubernetescrd
name: my-private-ingress
namespace: default
spec:
ingressClassName: traefik-internal
rules: []Note
It’s important to remember that, by default, internal Ingresses will automatically have public DNS resolving configured like a normal Internet-facing Ingresses. This means anybody outside the VPC can still resolve the DNS name, but it will resolve to private IP addresses that are not reachable from the Internet. This is because we don’t currently support private hosted zones in Route53.
Similarly, if you add the cert-manager.io/cluster-issuer: letsencrypt-prod annotation for automatic TLS certificates, the domain name will be registered in the public Certificate Transparency logs, even though the Ingress is internal-only.
Using Traefik Middlewares
Traefik uses the concept of Middlewares to modify requests and responses. Middlewares are defined as separate Kubernetes Custom Resources (CRDs) and then referenced from your Ingress via annotations.
The general pattern is:
- Create a
MiddlewareCustom Resource in your namespace - Reference it from your Ingress using the annotation:
traefik.ingress.kubernetes.io/router.middlewares: <namespace>-<middleware-name>@kubernetescrd
You can chain multiple middlewares by comma-separating them:
annotations:
traefik.ingress.kubernetes.io/router.middlewares: default-auth@kubernetescrd,default-ratelimit@kubernetescrdTip
If you want to create shared middlewares that can be used across multiple namespaces, consider creating them in a common namespace (e.g., default, production) and referencing them in your Ingress annotations. Please only use application-specific namespaces, and not any of the platform namespaces like infrastructure or traefik; these are reserved for Skyscrapers-provided components and middlewares.
Basic authentication
Traefik’s BasicAuth middleware requires credentials in htpasswd format, stored in a Kubernetes Secret.
Generate htpasswd credentials:
# Generate a password hash (requires apache2-utils or httpd-tools)
htpasswd -nb admin t0p-Secret
# Output: admin:$apr1$xyz123$hashedpasswordhereExample:
---
# Secret containing htpasswd-formatted credentials
apiVersion: v1
kind: Secret
metadata:
name: basic-auth-secret
namespace: default
type: Opaque
stringData:
users: |
admin:$apr1$xyz123$hashedpasswordhere
---
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: basic-auth
namespace: default
spec:
basicAuth:
secret: basic-auth-secret
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
traefik.ingress.kubernetes.io/router.middlewares: default-basic-auth@kubernetescrd
name: foo
namespace: default
spec:
ingressClassName: traefik
rules: []For multiple users, add one per line in the users field:
stringData:
users: |
admin:$apr1$...
developer:$apr1$...
readonly:$apr1$...Note
This differs from ingress-nginx which uses kubernetes.io/basic-auth type secrets. When migrating, you’ll need to regenerate secrets in htpasswd format.
Buffering
The Buffering middleware controls how Traefik reads and buffers requests and responses. This is useful for handling large uploads or protecting backends from slow clients.
---
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: buffering
namespace: default
spec:
buffering:
maxRequestBodyBytes: 10485760 # 10MB - max request body size
memRequestBodyBytes: 2097152 # 2MB - threshold before buffering to disk
maxResponseBodyBytes: 10485760 # 10MB - max response body size
memResponseBodyBytes: 2097152 # 2MB - threshold before buffering to disk
retryExpression: "IsNetworkError() && Attempts() < 3"
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
traefik.ingress.kubernetes.io/router.middlewares: default-buffering@kubernetescrd
name: foo
namespace: default
spec:
ingressClassName: traefik
rules: []CORS
CORS (Cross-Origin Resource Sharing) is configured using the Headers middleware. When CORS headers are set, Traefik handles preflight (OPTIONS) requests automatically without forwarding them to the backend.
Basic CORS example:
---
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: cors
namespace: default
spec:
headers:
accessControlAllowMethods:
- "GET"
- "POST"
- "PUT"
- "DELETE"
- "OPTIONS"
accessControlAllowHeaders:
- "Content-Type"
- "Authorization"
accessControlAllowOriginList:
- "https://app.example.com"
- "https://admin.example.com"
accessControlMaxAge: 86400
addVaryHeader: true
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
traefik.ingress.kubernetes.io/router.middlewares: default-cors@kubernetescrd
name: api
namespace: default
spec:
ingressClassName: traefik
rules: []Allow all origins (use with caution):
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: cors-allow-all
namespace: default
spec:
headers:
accessControlAllowMethods:
- "GET"
- "POST"
- "PUT"
- "DELETE"
- "OPTIONS"
accessControlAllowHeaders:
- "*"
accessControlAllowOriginList:
- "*"
accessControlMaxAge: 86400
addVaryHeader: trueWith credentials support (cannot use * for origin):
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: cors-with-credentials
namespace: default
spec:
headers:
accessControlAllowMethods:
- "GET"
- "POST"
- "OPTIONS"
accessControlAllowHeaders:
- "Content-Type"
- "Authorization"
- "X-Requested-With"
accessControlAllowOriginList:
- "https://app.example.com"
accessControlAllowCredentials: true
accessControlExposeHeaders:
- "X-Custom-Header"
accessControlMaxAge: 86400
addVaryHeader: trueUsing regex for origin matching:
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: cors-regex
namespace: default
spec:
headers:
accessControlAllowMethods:
- "GET"
- "POST"
accessControlAllowOriginListRegex:
- "https://.*\\.example\\.com" # Match all subdomains
accessControlMaxAge: 86400
addVaryHeader: trueNote
By default we provide a shared CORS middleware in the traefik namespace named cors-permissive, which you can reference from your Ingresses as traefik-cors-permissive@kubernetescrd. This CORS middleware is based upon the old ingress-nginx defaults and meant as a drop-in replacement.
Custom TLS configuration
To configure custom TLS options (minimum version, cipher suites), create a TLSOption resource:
---
apiVersion: traefik.io/v1alpha1
kind: TLSOption
metadata:
name: strict-tls
namespace: default
spec:
minVersion: VersionTLS12
cipherSuites:
- TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
- TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
- TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
curvePreferences:
- CurveP521
- CurveP384
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
traefik.ingress.kubernetes.io/router.tls.options: default-strict-tls@kubernetescrd
name: foo
namespace: default
spec:
ingressClassName: traefik
rules: []
tls:
- secretName: foo-tls
hosts:
- foo.example.comFor TLS 1.3 only:
apiVersion: traefik.io/v1alpha1
kind: TLSOption
metadata:
name: tls13-only
namespace: default
spec:
minVersion: VersionTLS13TLS/SSL Passthrough
To enable TLS/SSL passthrough (for end-to-end TLS), you need to create an IngressRouteTCP resource instead of a normal Ingress as we’ll leave the TLS termination to the backend service instead of Traefik.
Example:
apiVersion: traefik.io/v1alpha1
kind: IngressRouteTCP
metadata:
name: foo
namespace: default
spec:
entryPoints:
- websecure
routes:
- match: HostSNI(`foo.example.com`)
services:
- name: foo
port: 8443
tls:
passthrough: true
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: foo
namespace: default
spec:
ingressClassName: traefik
rules:
- host: foo.example.comNote
The Ingress object is not strictly necessary here, but it allows external-dns to automatically create the DNS record for foo.example.com. At a later time we will try to automate this process for IngressRouteTCP resources as well.
Disable automatic HTTPS redirection
By default, our Traefik controller redirects HTTP traffic to HTTPS. To disable this for a specific Ingress, create an IngressRoute with a higher priority than our default redirection rule (100).
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: foo
namespace: default
spec:
entryPoints:
- web
routes:
- match: Host(`foo.example.com`)
kind: Rule
priority: 200
services:
- name: foo
port: 80
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: foo
namespace: default
spec:
ingressClassName: traefik
rules:
- host: foo.example.comNote
The Ingress object is not strictly necessary here, but it allows external-dns to automatically create the DNS record for foo.example.com. At a later time we will try to automate this process for IngressRoute resources as well.
IP allowlisting
Restrict access to specific IP ranges using the IPAllowList middleware:
---
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: ip-allowlist
namespace: default
spec:
ipAllowList:
sourceRange:
- "10.0.0.0/8"
- "172.16.0.0/12"
- "192.168.0.0/16"
- "203.0.113.0/24"
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
traefik.ingress.kubernetes.io/router.middlewares: default-ip-allowlist@kubernetescrd
name: foo
namespace: default
spec:
ingressClassName: traefik
rules: []Tip
You can create a shared IPAllowList middleware in a common namespace and reference it across multiple Ingresses. This makes it easier to maintain a central list of allowed IPs.
OAuth / OIDC integration
Traefik supports external authentication via the ForwardAuth middleware. This allows integration with OAuth2-Proxy, which we provide by default.
Example using our OAuth2-Proxy service for authentication:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
traefik.ingress.kubernetes.io/router.middlewares: infrastructure-oauth-errors@kubernetescrd,infrastructure-oauth-auth@kubernetescrd
name: protected-app
namespace: default
spec:
ingressClassName: traefik
rules: []Rate limiting
Traefik has native rate limiting support via the RateLimit middleware:
Basic rate limiting:
---
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: ratelimit
namespace: default
spec:
rateLimit:
average: 100 # Average requests per second
burst: 50 # Maximum burst size
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
traefik.ingress.kubernetes.io/router.middlewares: default-ratelimit@kubernetescrd
name: foo
namespace: default
spec:
ingressClassName: traefik
rules: []Rate limiting per client IP:
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: ratelimit-per-ip
namespace: default
spec:
rateLimit:
average: 10
period: 1m # Per minute instead of per second
burst: 20
sourceCriterion:
ipStrategy: {}Rate limiting by header (e.g., API key):
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: ratelimit-by-apikey
namespace: default
spec:
rateLimit:
average: 100
burst: 50
sourceCriterion:
requestHeaderName: X-API-KeyRedirects
Upstream documentation | RedirectRegex
Redirect to another domain
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: redirect-domain
namespace: default
spec:
redirectRegex:
regex: "^https://old-domain\\.com/(.*)"
replacement: "https://new-domain.com/${1}"
permanent: trueNon-www to www redirect
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: www-redirect
namespace: default
spec:
redirectRegex:
regex: "^https://example\\.com/(.*)"
replacement: "https://www.example.com/${1}"
permanent: trueRedirect with path modification
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: redirect-path
namespace: default
spec:
redirectRegex:
regex: "^https://example\\.com/old-path/(.*)"
replacement: "https://example.com/new-path/${1}"
permanent: trueSession affinity / stickiness
Session affinity in Traefik is configured on the Service resource, not on the Ingress. This is a fundamental difference from ingress-nginx.
Example using sticky session annotations on the Service:
---
apiVersion: v1
kind: Service
metadata:
annotations:
traefik.ingress.kubernetes.io/service.sticky.cookie: "true"
traefik.ingress.kubernetes.io/service.sticky.cookie.name: sticky-app-session
traefik.ingress.kubernetes.io/service.sticky.cookie.secure: "true"
name: sticky-app
namespace: default
spec:
ports:
- name: http
port: 80
protocol: TCP
targetPort: 80
selector:
app.kubernetes.io/name: sticky-app
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
name: sticky-app
namespace: default
spec:
ingressClassName: traefik
rules:
- host: foo.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: sticky-app
port:
name: http
tls:
- secretName: foo-example-com-tls
hosts:
- foo.example.comTimeouts
Timeouts in Traefik are configured at different levels depending on the type:
Response forwarding timeout (via ServersTransport)
For backend connection timeouts, create a ServersTransport:
---
apiVersion: traefik.io/v1alpha1
kind: ServersTransport
metadata:
name: custom-timeouts
namespace: default
spec:
serverName: "backend"
forwardingTimeouts:
dialTimeout: 30s # Time to establish connection
responseHeaderTimeout: 60s # Time to wait for response headers
idleConnTimeout: 90s # Keep-alive timeout
---
apiVersion: v1
kind: Service
metadata:
name: my-app
namespace: default
annotations:
traefik.ingress.kubernetes.io/service.serverstransport: default-custom-timeouts@kubernetescrd
spec:
ports:
- port: 80
selector:
app: my-appRequest timeout via middleware
For request-level timeouts, you can use a combination of retry and circuit breaker middlewares:
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: retry
namespace: default
spec:
retry:
attempts: 3
initialInterval: 100msNote
Traefik doesn’t have direct equivalents for all ingress-nginx proxy timeout annotations. The proxy-read-timeout and proxy-send-timeout are handled via ServersTransport, while request-level timeouts require middleware or service mesh integration.
URL rewriting
Upstream documentation | ReplacePathRegex
Strip path prefix
Remove a prefix from the URL path before forwarding to the backend:
---
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: strip-api-prefix
namespace: default
spec:
stripPrefix:
prefixes:
- "/api"
# forceSlash: false # Don't add a trailing slash
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
traefik.ingress.kubernetes.io/router.middlewares: default-strip-api-prefix@kubernetescrd
name: api
namespace: default
spec:
ingressClassName: traefik
rules:
- host: example.com
http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: api-service
port:
number: 80Request to /api/users → Backend receives /users
Strip prefix with regex
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: strip-version-prefix
namespace: default
spec:
stripPrefixRegex:
regex:
- "/v[0-9]+/"Request to /v1/users → Backend receives /users
Replace path
Replace the entire path:
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: replace-path
namespace: default
spec:
replacePath:
path: "/new-path"Replace path with regex
For more complex rewriting patterns:
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: rewrite-api
namespace: default
spec:
replacePathRegex:
regex: "^/api/v1/(.*)"
replacement: "/internal/$1"Request to /api/v1/users → Backend receives /internal/users
Add prefix
Add a prefix to all requests:
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: add-prefix
namespace: default
spec:
addPrefix:
prefix: "/backend"Request to /users → Backend receives /backend/users
Migration from ingress-nginx
When migrating from ingress-nginx to Traefik, use this annotation mapping table:
| ingress-nginx | Traefik |
|---|---|
nginx.ingress.kubernetes.io/ssl-redirect: "true" | Default behavior (automatic HTTPS redirect) |
nginx.ingress.kubernetes.io/force-ssl-redirect: "true" | Default behavior |
nginx.ingress.kubernetes.io/rewrite-target: /$1 | StripPrefix or ReplacePathRegex middleware |
nginx.ingress.kubernetes.io/whitelist-source-range | IPAllowList middleware |
nginx.ingress.kubernetes.io/auth-url & nginx.ingress.kubernetes.io/auth-signin | ForwardAuth + Errors middleware chain, use traefik.ingress.kubernetes.io/router.middlewares: infrastructure-oauth-errors@kubernetescrd,infrastructure-oauth-auth@kubernetescrd annotation |
nginx.ingress.kubernetes.io/proxy-body-size | Buffering middleware |
nginx.ingress.kubernetes.io/proxy-read-timeout | ServersTransport CRD |
nginx.ingress.kubernetes.io/enable-cors | Headers middleware with CORS options |
nginx.ingress.kubernetes.io/limit-rps | RateLimit middleware |
nginx.ingress.kubernetes.io/affinity: cookie | Use traefik.ingress.kubernetes.io/service.sticky.cookie* annotations on the Service |
nginx.ingress.kubernetes.io/auth-secret (basic auth) | BasicAuth middleware with htpasswd secret |
nginx.ingress.kubernetes.io/permanent-redirect | RedirectRegex middleware |
nginx.ingress.kubernetes.io/ssl-ciphers | TLSOption CRD |
Warning
Custom nginx snippets (server-snippet, configuration-snippet) have no direct equivalent in Traefik. These require refactoring to use appropriate middleware or moving the logic to the application layer.