필사 모드: The Complete Traefik Guide — IngressRoute, Middleware, and Dynamic Configuration
EnglishIntroduction
When you run a Kubernetes cluster, you inevitably need an ingress point that routes external traffic to internal services. The standard Ingress resource alone struggles to express requirements like path rewriting, authentication, and rate limiting, and because each controller uses its own annotation syntax, portability suffers.
Traefik is a cloud-native edge router that solves these limitations through the concepts of dynamic configuration and middleware. Written in Go as a single binary, it watches multiple providers at once (Kubernetes CRDs, the standard Ingress, Docker, Consul, and more) and reconfigures routing rules in real time. There is no need to reload a config file or restart the process.
In this article, we will work through Traefik's core concepts, then explore IngressRoute CRDs, middleware chaining, automatic TLS with Let's Encrypt, the dashboard, and Traefik's relationship to the Gateway API, which has become the standard in 2026. We close with Helm deployment and the pitfalls you are likely to encounter.
This article is for the following readers.
- Those evaluating Traefik as an alternative to ingress-nginx on Kubernetes
- Those who want to escape the annotation hell of standard Ingress
- Those who want to manage automatic TLS and middleware chaining declaratively
- Those weighing a migration path from Ingress to the Gateway API
Traefik Architecture at a Glance
Traefik's routing model consists of four core objects. Once you understand the flow from an incoming request to a backend pod, the rest of the configuration becomes far easier.
Internet / external clients
|
v
+-------------------------------------------------+
| EntryPoint |
| (listens on :80 web / :443 websecure) |
+-------------------------------------------------+
|
v
+-------------------------------------------------+
| Router |
| rule match: Host(`app.example.com`) && Path |
| on match, hand off to the Middleware chain |
+-------------------------------------------------+
|
v
+-------------------------------------------------+
| Middleware chain (in order) |
| auth -> redirect -> ratelimit -> stripprefix |
+-------------------------------------------------+
|
v
+-------------------------------------------------+
| Service |
| load-balancing target (k8s Service / pods) |
+-------------------------------------------------+
|
v
backend Pod
EntryPoint
An EntryPoint is the network entry point where Traefik receives traffic. It is usually defined in the static configuration, specifying a port and protocol. By convention, port 80 is called web and port 443 is called websecure.
traefik.yml (static configuration)
entryPoints:
web:
address: ":80"
http:
redirections:
entryPoint:
to: websecure
scheme: https
permanent: true
websecure:
address: ":443"
http:
tls: {}
metrics:
address: ":9100"
In the example above, every HTTP request arriving on web is permanently redirected to websecure (HTTPS). Applying a global redirect at the EntryPoint level is a very common pattern.
Router
A Router defines the rules used to match incoming requests. You can combine various matchers such as Host, Path, Header, and Method using logical operators.
dynamic configuration example
http:
routers:
my-app:
rule: "Host(`app.example.com`) && PathPrefix(`/api`)"
entryPoints:
- websecure
middlewares:
- strip-api-prefix
service: my-app-service
tls:
certResolver: letsencrypt
The rule is expressed as a backtick-wrapped value. Priority is automatically computed based on rule length by default, but you can also set it explicitly with the priority field.
Middleware
Middleware is the stage that transforms requests or responses between the Router and the Service. Dozens of built-in middlewares are provided for authentication, header injection, path rewriting, rate limiting, compression, and more, and you can connect several of them into a chain. The order of the chain directly affects behavior, so designing the order carefully matters.
Service
A Service points to the actual backend and defines options such as the load-balancing strategy (weighted round robin), health checks, and sticky sessions. On Kubernetes it usually maps to a cluster Service resource.
Provider
A Provider is the source from which Traefik reads its configuration. The key point is that Traefik watches multiple providers simultaneously. In a Kubernetes environment you mainly use two.
| Provider | Description | Resources used |
| --- | --- | --- |
| kubernetesCRD | Based on Traefik-specific CRDs | IngressRoute, Middleware, etc. |
| kubernetesIngress | Handles the standard Ingress resource | Ingress, IngressClass |
| kubernetesGateway | Handles the Gateway API standard | Gateway, HTTPRoute |
enable providers in the static configuration
providers:
kubernetesCRD:
allowCrossNamespace: false
kubernetesIngress:
ingressClass: traefik
kubernetesGateway: {}
Ingress vs. IngressRoute CRD
This is the point that confuses Traefik newcomers the most. Both approaches route traffic, but they differ greatly in expressiveness and portability.
Limitations of Standard Ingress
The standard Ingress is an official Kubernetes resource, so it is highly portable, but the spec itself only standardizes host- and path-based routing. Every other advanced feature (rewriting, authentication, rate limiting, and so on) depends on controller-specific annotations.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-app
annotations:
traefik.ingress.kubernetes.io/router.middlewares: default-auth@kubernetescrd
traefik.ingress.kubernetes.io/router.entrypoints: websecure
spec:
ingressClassName: traefik
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-app-service
port:
number: 80
As you can see, attaching middleware relies on annotation strings, and this syntax cannot be carried over to another controller as is. As of 2026 the standard Ingress API is frozen. In other words, no new features will be added, and the community's direction has shifted to the Gateway API.
IngressRoute CRD
IngressRoute is a CRD defined by Traefik that expresses routing rules, middleware, and TLS settings as explicit YAML fields instead of annotations.
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: my-app
namespace: default
spec:
entryPoints:
- websecure
routes:
- match: Host(`app.example.com`) && PathPrefix(`/api`)
kind: Rule
priority: 10
middlewares:
- name: api-auth
- name: strip-api-prefix
services:
- name: my-app-service
port: 80
tls:
certResolver: letsencrypt
The table below compares the two approaches.
| Aspect | Standard Ingress | IngressRoute CRD |
| --- | --- | --- |
| Portability | High (k8s standard) | Low (Traefik-only) |
| Middleware expression | Annotation strings | Native fields |
| Matching rules | Mostly host / path | Rich: Host, Header, Method |
| TCP / UDP routing | Not supported | IngressRouteTCP / UDP |
| Priority control | Limited | Explicit priority field |
| Future standard | Frozen | Tied to Traefik |
In short, if portability is the top priority, choose the standard Ingress or the Gateway API; if you want to use every Traefik feature, choose IngressRoute. That said, for a new project it is reasonable in 2026 to consider the Gateway API first, which we describe later.
Middleware Chaining in Practice
Middleware is Traefik's most powerful feature. You define each middleware as an independent CRD and reference them in order from an IngressRoute to build a chain. Let's look at four commonly used ones.
1. Basic Authentication (BasicAuth)
First create a Secret holding the credentials, then reference it from the middleware.
apiVersion: v1
kind: Secret
metadata:
name: dashboard-auth-secret
namespace: default
type: Opaque
stringData:
users: |
admin:$apr1$abcd1234$encryptedpasswordhash
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: api-auth
namespace: default
spec:
basicAuth:
secret: dashboard-auth-secret
removeHeader: true
The users value must be in the hash format generated by htpasswd. Setting removeHeader to true prevents the Authorization header from being forwarded to the backend after authentication.
2. Redirect (RedirectScheme / RedirectRegex)
You can force HTTP to HTTPS or send a specific path pattern elsewhere.
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: https-redirect
namespace: default
spec:
redirectScheme:
scheme: https
permanent: true
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: legacy-redirect
namespace: default
spec:
redirectRegex:
regex: "^https://old.example.com/(.*)"
replacement: "https://new.example.com/${1}"
permanent: true
3. Rate Limit
Limit the number of requests per second or minute to protect the backend. average is the allowed average rate and burst is the instantaneous allowance.
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: api-ratelimit
namespace: default
spec:
rateLimit:
average: 100
burst: 50
period: 1s
sourceCriterion:
ipStrategy:
depth: 1
sourceCriterion sets the basis for identifying the client. If you sit behind a proxy, configure ipStrategy.depth so the real client IP is extracted from the X-Forwarded-For header.
4. StripPrefix
When the backend expects the root path (/) but external clients access it under a specific prefix, strip the leading part of the path.
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: strip-api-prefix
namespace: default
spec:
stripPrefix:
prefixes:
- /api
forceSlash: false
When this middleware is applied, an external /api/users request is delivered to the backend as /users.
Building the Chain and Ordering
When you list multiple middlewares as an array in an IngressRoute, they are applied in that order. Getting the order wrong can produce unexpected behavior, so be careful.
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: secure-api
namespace: default
spec:
entryPoints:
- websecure
routes:
- match: Host(`api.example.com`)
kind: Rule
middlewares:
- name: https-redirect
- name: api-auth
- name: api-ratelimit
- name: strip-api-prefix
services:
- name: api-service
port: 8080
tls:
certResolver: letsencrypt
The chain above operates in the following order.
request
-> https-redirect (redirect to HTTPS if HTTP)
-> api-auth (verify authentication)
-> api-ratelimit (throttle request rate)
-> strip-api-prefix (remove path prefix)
-> api-service (forward to backend)
As a principle, place cheap middlewares that are likely to reject (redirect, authentication, rate limit) toward the front so unnecessary processing is cut off early. Put transformations such as path rewriting toward the back.
Automatic TLS — Let's Encrypt ACME
One of Traefik's appealing features is automatic certificate issuance and renewal via Let's Encrypt. It embeds the ACME protocol, so it works without a separate cert-manager.
ACME Challenge Types
ACME supports three challenges that prove domain ownership.
| Challenge | How it works | Wildcard certs |
| --- | --- | --- |
| HTTP-01 | Responds with a validation token on port 80 | Not supported |
| TLS-ALPN-01 | TLS handshake on port 443 | Not supported |
| DNS-01 | Validation via a DNS TXT record | Supported |
static configuration — HTTP-01 challenge
certificatesResolvers:
letsencrypt:
acme:
email: ops@example.com
storage: /data/acme.json
httpChallenge:
entryPoint: web
If you need a wildcard certificate, you must use the DNS-01 challenge. Inject the API token of your DNS provider (for example, Cloudflare or Route53) as an environment variable.
certificatesResolvers:
letsencrypt-dns:
acme:
email: ops@example.com
storage: /data/acme.json
dnsChallenge:
provider: cloudflare
resolvers:
- "1.1.1.1:53"
Because the acme.json file stores issued certificates and private keys, restrict its permissions to 600 and store it on a persistent volume (PVC). When the pod scales to several replicas, concurrent writes to the same file can collide, so in multi-replica environments it is safer to use cert-manager separately or to limit certificate issuance to a single instance.
Dashboard
Traefik currently provides a web dashboard that visually shows the state of routers, services, and middlewares. It is handy for quickly checking the live routing configuration.
enable the dashboard and API in the static configuration
api:
dashboard: true
insecure: false
Setting insecure to true exposes it without authentication, so you must never use that in production. Instead, expose the dashboard via an IngressRoute and protect it with a BasicAuth middleware.
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: traefik-dashboard
namespace: traefik
spec:
entryPoints:
- websecure
routes:
- match: Host(`traefik.example.com`)
kind: Rule
middlewares:
- name: dashboard-auth
services:
- name: api@internal
kind: TraefikService
tls:
certResolver: letsencrypt
Note that the service points to an internal TraefikService called api@internal. This is a special reference to Traefik's internal API handler.
Gateway API Support
As of 2026 the most important trend is the Gateway API. As mentioned, the standard Ingress is frozen, and the next-generation standard for Kubernetes networking has been settled as the Gateway API. The Gateway API provides role separation (infrastructure operators own the Gateway, app developers own the HTTPRoute) and rich routing expressiveness through standard CRDs.
Traefik is a conformant Gateway API implementation and processes the standard resources through the kubernetesGateway provider. In other words, you can obtain IngressRoute-level expressiveness as a standard without vendor lock-in.
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
name: traefik
spec:
controllerName: traefik.io/gateway-controller
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: traefik-gateway
namespace: traefik
spec:
gatewayClassName: traefik
listeners:
- name: web
protocol: HTTP
port: 80
- name: websecure
protocol: HTTPS
port: 443
tls:
mode: Terminate
certificateRefs:
- name: example-tls
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: my-app-route
namespace: default
spec:
parentRefs:
- name: traefik-gateway
namespace: traefik
hostnames:
- "app.example.com"
rules:
- matches:
- path:
type: PathPrefix
value: /api
backendRefs:
- name: my-app-service
port: 80
The table below summarizes where the three approaches stand.
| Approach | Standard? | Expressiveness | Recommended for |
| --- | --- | --- | --- |
| Standard Ingress | k8s standard (frozen) | Low | Simple routing, legacy |
| IngressRoute | Traefik-only | High | When you need Traefik features |
| Gateway API | k8s standard (evolving) | High | Recommended for new projects |
For now the Gateway API's middleware expressiveness may not be as rich as IngressRoute, so a transitional setup that mixes both approaches is common. In the long run, converging on the Gateway API is recommended.
Deployment in Practice — Helm values
In production it is common to deploy Traefik with the official Helm chart. The following is a values example frequently used in the field.
add the repository and install
helm repo add traefik https://traefik.github.io/charts
helm repo update
helm install traefik traefik/traefik \
--namespace traefik --create-namespace \
--values values.yaml
values.yaml
deployment:
replicas: 2
ingressClass:
enabled: true
isDefaultClass: true
providers:
kubernetesCRD:
enabled: true
allowCrossNamespace: false
kubernetesIngress:
enabled: true
kubernetesGateway:
enabled: true
ports:
web:
redirectTo:
port: websecure
websecure:
tls:
enabled: true
certificatesResolvers:
letsencrypt:
acme:
email: ops@example.com
storage: /data/acme.json
httpChallenge:
entryPoint: web
persistence:
enabled: true
size: 128Mi
path: /data
dashboard:
enabled: true
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 256Mi
logs:
general:
level: INFO
access:
enabled: true
After installation, check the state with the following commands.
kubectl get pods -n traefik
kubectl get svc -n traefik
kubectl logs -n traefik deploy/traefik --tail=100
Operations and Tuning
Access Logs and Metrics
In production, write access logs in JSON format and enable Prometheus metrics to secure observability.
metrics:
prometheus:
entryPoint: metrics
addEntryPointsLabels: true
addServicesLabels: true
accessLog:
format: json
filters:
statusCodes:
- "400-499"
- "500-599"
Using the statusCodes filter logs only 4xx and 5xx responses, reducing log volume.
Health Checks and Probes
Traefik's own readiness and liveness probes are configured through the ping EntryPoint.
ping:
entryPoint: web
Timeout Tuning
If you handle long-lived connections or large uploads, adjust the transport timeouts of the EntryPoint.
entryPoints:
websecure:
address: ":443"
transport:
respondingTimeouts:
readTimeout: 60s
writeTimeout: 60s
idleTimeout: 180s
Pitfalls and Troubleshooting
Here are the problems you will frequently run into in operations.
1. 404 or the Router Does Not Match
The most common cause is a missing EntryPoint specification. If you do not specify entryPoints on an IngressRoute, it may be exposed on every EntryPoint, or traffic may not arrive on the intended port. First check in the dashboard whether the router is registered and whether the rule is correct.
query router state via the API
kubectl port-forward -n traefik deploy/traefik 8080:8080
curl http://localhost:8080/api/http/routers
2. Middleware Not Found
To reference a middleware in another namespace, you must specify the namespace. In the annotation-based approach for an IngressRoute, you use the form name-namespace@kubernetescrd. Also, if allowCrossNamespace is false, cross-namespace references are blocked.
3. Certificate Is Not Issued
The HTTP-01 challenge requires port 80 to be reachable from the outside. Check your firewall or load balancer settings. If the acme.json file permissions are not 600, Traefik will reject it. Also, sharing the same acme.json across multiple replicas can tangle issuance.
4. Backend Breaks After StripPrefix
If the backend application references static resources by absolute path, links can break after the prefix is stripped. In that case configure a base path in the application, or handle the path more precisely with the replacePathRegex middleware.
5. The Real Client IP Is Not Visible
When you sit behind a cloud load balancer, only the LB's IP may appear in rate limits or logs. Configure forwardedHeaders.trustedIPs and adjust the rate limit middleware's ipStrategy.depth to match your environment.
entryPoints:
websecure:
address: ":443"
forwardedHeaders:
trustedIPs:
- "10.0.0.0/8"
- "172.16.0.0/12"
Closing
Traefik is a powerful tool that bundles dynamic configuration, middleware, and automatic TLS so you can operate Kubernetes ingress declaratively. The IngressRoute CRD frees you from the annotation hell of standard Ingress, but it comes at the cost of vendor lock-in.
As of 2026 the standard Ingress is frozen and the Gateway API has settled in as the next-generation standard. Because Traefik officially supports the Gateway API, for a new project it is realistic to consider the Gateway API first while still using IngressRoute where you need Traefik's middleware ecosystem. If you understand the core concepts accurately (EntryPoint, Router, Middleware, Service, Provider), you can operate confidently no matter which approach you choose.
References
- [Traefik Official Documentation](https://doc.traefik.io/traefik/)
- [Traefik Kubernetes IngressRoute Guide](https://doc.traefik.io/traefik/routing/providers/kubernetes-crd/)
- [Traefik Helm Charts](https://github.com/traefik/traefik-helm-chart)
- [Kubernetes Ingress Concepts](https://kubernetes.io/docs/concepts/services-networking/ingress/)
- [Gateway API Official Documentation](https://gateway-api.sigs.k8s.io/)
- [Let's Encrypt Documentation](https://letsencrypt.org/docs/)
- [cert-manager Documentation](https://cert-manager.io/docs/)
- [Helm Official Documentation](https://helm.sh/docs/)
- [ingress-nginx Documentation](https://kubernetes.github.io/ingress-nginx/)
현재 단락 (1/441)
When you run a Kubernetes cluster, you inevitably need an ingress point that routes external traffic...