diff --git a/CHANGELOG.md b/CHANGELOG.md
index cda4f26e5..27101e1f4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,26 @@
+## [v3.4.4](https://github.com/traefik/traefik/tree/v3.4.4) (2025-07-11)
+[All Commits](https://github.com/traefik/traefik/compare/v3.4.3...v3.4.4)
+
+**Bug fixes:**
+- **[k8s/gatewayapi]** Respect service.nativelb=false annotation when nativeLBByDefault is enabled ([#11847](https://github.com/traefik/traefik/pull/11847) by [sdelicata](https://github.com/sdelicata))
+- **[service]** Fix concurrent access to balancer status map in WRR and P2C strategies ([#11887](https://github.com/traefik/traefik/pull/11887) by [kevinpollet](https://github.com/kevinpollet))
+
+**Documentation:**
+- **[docker,k8s]** Add New Expose Guides ([#11760](https://github.com/traefik/traefik/pull/11760) by [sheddy-traefik](https://github.com/sheddy-traefik))
+- **[docker,k8s]** Add New Setup Guides ([#11741](https://github.com/traefik/traefik/pull/11741) by [sheddy-traefik](https://github.com/sheddy-traefik))
+- **[docker/swarm]** Fix label for overriding swarm network on container ([#11881](https://github.com/traefik/traefik/pull/11881) by [kevinpollet](https://github.com/kevinpollet))
+- **[logs,accesslogs]** Update Logs and Accesslogs Reference documentation with OTLP Options ([#11845](https://github.com/traefik/traefik/pull/11845) by [sheddy-traefik](https://github.com/sheddy-traefik))
+- Update what is Traefik page to include full Traefik Platform context ([#11885](https://github.com/traefik/traefik/pull/11885) by [tomatokoolaid](https://github.com/tomatokoolaid))
+
+**Misc:**
+- Merge branch v2.11 into v3.4 ([#11896](https://github.com/traefik/traefik/pull/11896) by [kevinpollet](https://github.com/kevinpollet))
+
+## [v2.11.27](https://github.com/traefik/traefik/tree/v2.11.27) (2025-07-11)
+[All Commits](https://github.com/traefik/traefik/compare/v2.11.26...v2.11.27)
+
+**Bug fixes:**
+- Bump github.com/go-viper/mapstructure/v2 to v2.3.0 ([#11880](https://github.com/traefik/traefik/pull/11880) by [kevinpollet](https://github.com/kevinpollet))
+
## [v3.5.0-rc1](https://github.com/traefik/traefik/tree/v3.5.0-rc1) (2025-06-26)
[All Commits](https://github.com/traefik/traefik/compare/v3.4.0-rc1...v3.5.0-rc1)
diff --git a/docs/content/assets/img/setup/route-in-dashboard.png b/docs/content/assets/img/setup/route-in-dashboard.png
new file mode 100644
index 000000000..99c8eaf4a
Binary files /dev/null and b/docs/content/assets/img/setup/route-in-dashboard.png differ
diff --git a/docs/content/assets/img/setup/traefik-dashboard-docker.png b/docs/content/assets/img/setup/traefik-dashboard-docker.png
new file mode 100644
index 000000000..862ba4994
Binary files /dev/null and b/docs/content/assets/img/setup/traefik-dashboard-docker.png differ
diff --git a/docs/content/assets/img/setup/traefik-dashboard-swarm.png b/docs/content/assets/img/setup/traefik-dashboard-swarm.png
new file mode 100644
index 000000000..f91e60782
Binary files /dev/null and b/docs/content/assets/img/setup/traefik-dashboard-swarm.png differ
diff --git a/docs/content/assets/img/setup/traefik-dashboard.png b/docs/content/assets/img/setup/traefik-dashboard.png
new file mode 100644
index 000000000..5d1738f45
Binary files /dev/null and b/docs/content/assets/img/setup/traefik-dashboard.png differ
diff --git a/docs/content/assets/img/setup/whoami-json-dump.png b/docs/content/assets/img/setup/whoami-json-dump.png
new file mode 100644
index 000000000..7fd1526f1
Binary files /dev/null and b/docs/content/assets/img/setup/whoami-json-dump.png differ
diff --git a/docs/content/expose/docker.md b/docs/content/expose/docker.md
new file mode 100644
index 000000000..045c64277
--- /dev/null
+++ b/docs/content/expose/docker.md
@@ -0,0 +1,462 @@
+# Exposing Services with Traefik on Docker
+
+This guide will help you expose your services securely through Traefik Proxy using Docker. We'll cover routing HTTP and HTTPS traffic, implementing TLS, adding middlewares, Let's Encrypt integration, and sticky sessions.
+
+## Prerequisites
+
+- Docker and Docker Compose installed
+- Basic understanding of Docker concepts
+- Traefik deployed using the Traefik Docker Setup guide
+
+## Expose Your First HTTP Service
+
+Let's expose a simple HTTP service using the [whoami](https://hub.docker.com/r/traefik/whoami) application. This will demonstrate basic routing to a backend service.
+
+First, create a `docker-compose.yml` file:
+
+```yaml
+services:
+ traefik:
+ image: "traefik:v3.4"
+ container_name: "traefik"
+ restart: unless-stopped
+ security_opt:
+ - no-new-privileges:true
+ networks:
+ - proxy
+ command:
+ - "--providers.docker=true"
+ - "--providers.docker.exposedbydefault=false"
+ - "--providers.docker.network=proxy"
+ - "--entryPoints.web.address=:80"
+ ports:
+ - "80:80"
+ - "8080:8080"
+ volumes:
+ - "/var/run/docker.sock:/var/run/docker.sock:ro"
+
+ whoami:
+ image: "traefik/whoami"
+ restart: unless-stopped
+ networks:
+ - proxy
+ labels:
+ - "traefik.enable=true"
+ - "traefik.http.routers.whoami.rule=Host(`whoami.docker.localhost`)"
+ - "traefik.http.routers.whoami.entrypoints=web"
+
+networks:
+ proxy:
+ name: proxy
+```
+
+Save this as `docker-compose.yml` and start the services:
+
+```bash
+docker compose up -d
+```
+
+### Verify Your Service
+
+Your service is now available at http://whoami.docker.localhost/. Test that it works:
+
+```bash
+curl -H "Host: whoami.docker.localhost" http://localhost/
+```
+
+You should see output similar to:
+
+```bash
+Hostname: whoami
+IP: 127.0.0.1
+IP: ::1
+IP: 172.18.0.3
+IP: fe80::215:5dff:fe00:c9e
+RemoteAddr: 172.18.0.2:55108
+GET / HTTP/1.1
+Host: whoami.docker.localhost
+User-Agent: curl/7.68.0
+Accept: */*
+Accept-Encoding: gzip
+X-Forwarded-For: 172.18.0.1
+X-Forwarded-Host: whoami.docker.localhost
+X-Forwarded-Port: 80
+X-Forwarded-Proto: http
+X-Forwarded-Server: 5789f594e7d5
+X-Real-Ip: 172.18.0.1
+```
+
+This confirms that Traefik is successfully routing requests to your whoami application.
+
+## Add Routing Rules
+
+Now we'll enhance our routing by directing traffic to different services based on [URL paths](../reference/routing-configuration/http/router/rules-and-priority.md#path-pathprefix-and-pathregexp). This is useful for API versioning, frontend/backend separation, or organizing microservices.
+
+Update your `docker-compose.yml` to add another service:
+
+```yaml
+# ...
+
+# New service
+ whoami-api:
+ image: "traefik/whoami"
+ networks:
+ - proxy
+ container_name: "whoami-api"
+ environment:
+ - WHOAMI_NAME=API Service
+ labels:
+ - "traefik.enable=true"
+ # Path-based routing
+ - "traefik.http.routers.whoami-api.rule=Host(`whoami.docker.localhost`) && PathPrefix(`/api`)"
+ - "traefik.http.routers.whoami-api.entrypoints=web"
+```
+
+Apply the changes:
+
+```bash
+docker compose up -d
+```
+
+### Test the Path-Based Routing
+
+Verify that different paths route to different services:
+
+```bash
+# Root path should go to the main whoami service
+curl -H "Host: whoami.docker.localhost" http://localhost/
+
+# /api path should go to the whoami-api service
+curl -H "Host: whoami.docker.localhost" http://localhost/api
+```
+
+For the `/api` requests, you should see the response showing "API Service" in the environment variables section, confirming that your path-based routing is working correctly.
+
+## Enable TLS
+
+Let's secure our service with HTTPS by adding TLS. We'll start with a self-signed certificate for local development.
+
+### Create a Self-Signed Certificate
+
+Generate a self-signed certificate:
+
+```bash
+mkdir -p certs
+openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
+ -keyout certs/local.key -out certs/local.crt \
+ -subj "/CN=*.docker.localhost"
+```
+
+Create a directory for dynamic configuration and add a TLS configuration file:
+
+```bash
+mkdir -p dynamic
+cat > dynamic/tls.yml << EOF
+tls:
+ certificates:
+ - certFile: /certs/local.crt
+ keyFile: /certs/local.key
+EOF
+```
+
+Update your `docker-compose.yml` file with the following changes:
+
+```yaml
+services:
+ traefik:
+ image: "traefik:v3.4"
+ container_name: "traefik"
+ restart: unless-stopped
+ security_opt:
+ - no-new-privileges:true
+ networks:
+ - proxy
+ command:
+ - "--api.insecure=false"
+ - "--api.dashboard=true"
+ - "--providers.docker=true"
+ - "--providers.docker.exposedbydefault=false"
+ - "--providers.docker.network=proxy"
+ - "--providers.file.directory=/etc/traefik/dynamic"
+ - "--entryPoints.web.address=:80"
+ - "--entryPoints.websecure.address=:443"
+ - "--entryPoints.websecure.http.tls=true"
+ ports:
+ - "80:80"
+ - "443:443"
+ - "8080:8080"
+ volumes:
+ - "/var/run/docker.sock:/var/run/docker.sock:ro"
+ # Add the following volumes
+ - "./certs:/certs:ro"
+ - "./dynamic:/etc/traefik/dynamic:ro"
+ labels:
+ - "traefik.enable=true"
+ - "traefik.http.routers.dashboard.rule=Host(`dashboard.docker.localhost`)"
+ - "traefik.http.routers.dashboard.entrypoints=websecure"
+ - "traefik.http.routers.dashboard.service=api@internal"
+ # Add the following label
+ - "traefik.http.routers.dashboard.tls=true"
+
+ whoami:
+ image: "traefik/whoami"
+ restart: unless-stopped
+ networks:
+ - proxy
+ labels:
+ - "traefik.enable=true"
+ - "traefik.http.routers.whoami.rule=Host(`whoami.docker.localhost`)"
+ - "traefik.http.routers.whoami.entrypoints=websecure"
+ # Add the following label
+ - "traefik.http.routers.whoami.tls=true"
+
+ whoami-api:
+ image: "traefik/whoami"
+ container_name: "whoami-api"
+ restart: unless-stopped
+ networks:
+ - proxy
+ environment:
+ - WHOAMI_NAME=API Service
+ labels:
+ - "traefik.enable=true"
+ - "traefik.http.routers.whoami-api.rule=Host(`whoami.docker.localhost`) && PathPrefix(`/api`)"
+ - "traefik.http.routers.whoami-api.entrypoints=websecure"
+ # Add the following label
+ - "traefik.http.routers.whoami-api.tls=true"
+
+networks:
+ proxy:
+ name: proxy
+```
+
+Apply the changes:
+
+```bash
+docker compose up -d
+```
+
+Your browser can access https://whoami.docker.localhost/ for the service. You'll need to accept the security warning for the self-signed certificate.
+
+## Add Middlewares
+
+Middlewares allow you to modify requests or responses as they pass through Traefik. Let's add two useful middlewares: [Headers](../reference/routing-configuration/http/middlewares/headers.md) for security and [IP allowlisting](../reference/routing-configuration/http/middlewares/ipallowlist.md) for access control.
+
+Add the following labels to your whoami service in `docker-compose.yml`:
+
+```yaml
+labels:
+
+ # Secure Headers Middleware
+ - "traefik.http.middlewares.secure-headers.headers.frameDeny=true"
+ - "traefik.http.middlewares.secure-headers.headers.sslRedirect=true"
+ - "traefik.http.middlewares.secure-headers.headers.browserXssFilter=true"
+ - "traefik.http.middlewares.secure-headers.headers.contentTypeNosniff=true"
+ - "traefik.http.middlewares.secure-headers.headers.stsIncludeSubdomains=true"
+ - "traefik.http.middlewares.secure-headers.headers.stsPreload=true"
+ - "traefik.http.middlewares.secure-headers.headers.stsSeconds=31536000"
+
+ # IP Allowlist Middleware
+ - "traefik.http.middlewares.ip-allowlist.ipallowlist.sourceRange=127.0.0.1/32,192.168.0.0/16,10.0.0.0/8"
+```
+
+Add the same middleware to your whoami-api service:
+
+```yaml
+labels:
+ - "traefik.http.routers.whoami-api.middlewares=secure-headers,ip-allowlist"
+```
+
+Apply the changes:
+
+```bash
+docker compose up -d
+```
+
+### Test the Middlewares
+
+Now let's verify that our middlewares are working correctly:
+
+Test the Secure Headers middleware:
+
+```bash
+curl -k -I -H "Host: whoami.docker.localhost" https://localhost/
+```
+
+In the response headers, you should see security headers set by the middleware:
+
+- `X-Frame-Options: DENY`
+- `X-Content-Type-Options: nosniff`
+- `X-XSS-Protection: 1; mode=block`
+- `Strict-Transport-Security` with the appropriate settings
+
+Test the IP Allowlist middleware:
+
+If your request comes from an IP that's in the allow list (e.g., 127.0.0.1), it should succeed:
+
+```bash
+curl -k -I -H "Host: whoami.docker.localhost" https://localhost/
+```
+
+If you try to access from an IP not in the allow list, the request will be rejected with a `403` Forbidden response. To simulate this in a local environment, you can modify the middleware configuration temporarily to exclude your IP address, then test again.
+
+## Generate Certificates with Let's Encrypt
+
+Let's Encrypt provides free, automated TLS certificates. Let's configure Traefik to automatically obtain and renew certificates for our services.
+
+Instead of using self-signed certificates, update your existing `docker-compose.yml` file with the following changes:
+
+Add the Let's Encrypt certificate resolver to the Traefik service command section:
+
+```yaml
+command:
+ - "--api.insecure=false"
+ - "--api.dashboard=true"
+ - "--providers.docker=true"
+ - "--providers.docker.exposedbydefault=false"
+ - "--providers.docker.network=proxy"
+ - "--entryPoints.web.address=:80"
+ - "--entryPoints.websecure.address=:443"
+ - "--entryPoints.websecure.http.tls=true"
+ - "--entryPoints.web.http.redirections.entryPoint.to=websecure"
+ - "--entryPoints.web.http.redirections.entryPoint.scheme=https"
+ # Let's Encrypt configuration
+ - "--certificatesresolvers.le.acme.email=your-email@example.com" # replace with your actual email
+ - "--certificatesresolvers.le.acme.storage=/letsencrypt/acme.json"
+ - "--certificatesresolvers.le.acme.httpchallenge.entrypoint=web"
+```
+
+Add a volume for Let's Encrypt certificates:
+
+```yaml
+volumes:
+ # ...Existing volumes...
+ - "./letsencrypt:/letsencrypt"
+```
+
+Update your service labels to use the certificate resolver:
+
+```yaml
+labels:
+ - "traefik.http.routers.whoami.tls.certresolver=le"
+```
+
+Do the same for any other services you want to secure:
+
+```yaml
+labels:
+ - "traefik.http.routers.whoami-api.tls.certresolver=le"
+```
+
+Create a directory for storing Let's Encrypt certificates:
+
+```bash
+mkdir -p letsencrypt
+```
+
+Apply the changes:
+
+```bash
+docker compose up -d
+```
+
+!!! important "Public DNS Required"
+ Let's Encrypt may require a publicly accessible domain to validate domain ownership. For testing with local domains like `whoami.docker.localhost`, the certificate will remain self-signed. In production, replace it with a real domain that has a publicly accessible DNS record pointing to your Traefik instance.
+
+Once the certificate is issued, you can verify it:
+
+```bash
+# Verify the certificate chain
+curl -v https://whoami.docker.localhost/ 2>&1 | grep -i "server certificate"
+```
+
+You should see that your certificate is issued by Let's Encrypt.
+
+## Configure Sticky Sessions
+
+Sticky sessions ensure that a user's requests always go to the same backend server, which is essential for applications that maintain session state. Let's implement sticky sessions for our whoami service.
+
+### First, Add Sticky Session Labels
+
+Add the following labels to your whoami service in the `docker-compose.yml` file:
+
+```yaml
+labels:
+ - "traefik.http.services.whoami.loadbalancer.sticky.cookie=true"
+ - "traefik.http.services.whoami.loadbalancer.sticky.cookie.name=sticky_cookie"
+ - "traefik.http.services.whoami.loadbalancer.sticky.cookie.secure=true"
+ - "traefik.http.services.whoami.loadbalancer.sticky.cookie.httpOnly=true"
+```
+
+Apply the changes:
+
+```bash
+docker compose up -d
+```
+
+### Then, Scale Up the Service
+
+To demonstrate sticky sessions with Docker, use Docker Compose's scale feature:
+
+```bash
+docker compose up -d --scale whoami=3
+```
+
+This creates multiple instances of the whoami service.
+
+!!! important "Scaling After Configuration Changes"
+ If you run `docker compose up -d` after scaling, it will reset the number of whoami instances back to 1. Always scale after applying configuration changes and starting the services.
+
+### Test Sticky Sessions
+
+You can test the sticky sessions by making multiple requests and observing that they all go to the same backend container:
+
+```bash
+# First request - save cookies to a file
+curl -k -c cookies.txt -H "Host: whoami.docker.localhost" https://localhost/
+
+# Subsequent requests - use the cookies
+curl -k -b cookies.txt -H "Host: whoami.docker.localhost" https://localhost/
+curl -k -b cookies.txt -H "Host: whoami.docker.localhost" https://localhost/
+```
+
+Pay attention to the `Hostname` field in each response - it should remain the same across all requests when using the cookie file, confirming that sticky sessions are working.
+
+For comparison, try making requests without the cookie:
+
+```bash
+# Requests without cookies should be load-balanced across different containers
+curl -k -H "Host: whoami.docker.localhost" https://localhost/
+curl -k -H "Host: whoami.docker.localhost" https://localhost/
+```
+
+You should see different `Hostname` values in these responses, as each request is load-balanced to a different container.
+
+!!! important "Browser Testing"
+ When testing in browsers, you need to use the same browser session to maintain the cookie. The cookie is set with `httpOnly` and `secure` flags for security, so it will only be sent over HTTPS connections and won't be accessible via JavaScript.
+
+For more advanced configuration options, see the [reference documentation](../reference/routing-configuration/http/load-balancing/service.md).
+
+## Conclusion
+
+In this guide, you've learned how to:
+
+- Expose HTTP services through Traefik in Docker
+- Set up path-based routing to direct traffic to different backend services
+- Secure your services with TLS using self-signed certificates
+- Add security with middlewares like secure headers and IP allow listing
+- Automate certificate management with Let's Encrypt
+- Implement sticky sessions for stateful applications
+
+These fundamental capabilities provide a solid foundation for exposing any application through Traefik Proxy in Docker. Each of these can be further customized to meet your specific requirements.
+
+### Next Steps
+
+Now that you understand the basics of exposing services with Traefik Proxy, you might want to explore:
+
+- [Advanced routing options](../reference/routing-configuration/http/router/rules-and-priority.md) like query parameter matching, header-based routing, and more
+- [Additional middlewares](../reference/routing-configuration/http/middlewares/overview.md) for authentication, rate limiting, and request modifications
+- [Observability features](../reference/install-configuration/observability/metrics.md) for monitoring and debugging your Traefik deployment
+- [TCP services](../reference/routing-configuration/tcp/service.md) for exposing TCP services
+- [UDP services](../reference/routing-configuration/udp/service.md) for exposing UDP services
+- [Docker provider documentation](../reference/install-configuration/providers/docker.md) for more details about the Docker integration
diff --git a/docs/content/expose/kubernetes.md b/docs/content/expose/kubernetes.md
new file mode 100644
index 000000000..6b5bca03c
--- /dev/null
+++ b/docs/content/expose/kubernetes.md
@@ -0,0 +1,1012 @@
+# Exposing Services with Traefik on Kubernetes
+
+This guide will help you expose your services securely through Traefik Proxy on Kubernetes. We'll cover routing HTTP and HTTPS traffic, implementing TLS, adding security middleware, and configuring sticky sessions. For routing, this guide gives you two options:
+
+- [Gateway API](../reference/routing-configuration/kubernetes/gateway-api.md)
+- [IngressRoute](../reference/routing-configuration/kubernetes/crd/http/ingressroute.md)
+
+Feel free to choose the one that fits your needs best.
+
+## Prerequisites
+
+- A Kubernetes cluster with Traefik Proxy installed
+- `kubectl` configured to interact with your cluster
+- Traefik deployed using the Traefik Kubernetes Setup guide
+
+## Expose Your First HTTP Service
+
+Let's expose a simple HTTP service using the [whoami](https://github.com/traefik/whoami) application. This will demonstrate basic routing to a backend service.
+
+First, create the deployment and service:
+
+```yaml
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: whoami
+ namespace: default
+spec:
+ replicas: 2
+ selector:
+ matchLabels:
+ app: whoami
+ template:
+ metadata:
+ labels:
+ app: whoami
+ spec:
+ containers:
+ - name: whoami
+ image: traefik/whoami
+ ports:
+ - containerPort: 80
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: whoami
+ namespace: default
+spec:
+ selector:
+ app: whoami
+ ports:
+ - port: 80
+```
+
+Save this as `whoami.yaml` and apply it:
+
+```bash
+kubectl apply -f whoami.yaml
+```
+
+Now, let's create routes using either Gateway API or IngressRoute.
+
+### Using Gateway API
+
+```yaml
+apiVersion: gateway.networking.k8s.io/v1
+kind: HTTPRoute
+metadata:
+ name: whoami
+ namespace: default
+spec:
+ parentRefs:
+ - name: traefik-gateway # This Gateway is automatically created by Traefik
+ hostnames:
+ - "whoami.docker.localhost"
+ rules:
+ - matches:
+ - path:
+ type: PathPrefix
+ value: /
+ backendRefs:
+ - name: whoami
+ port: 80
+```
+
+Save this as `whoami-route.yaml` and apply it:
+
+```bash
+kubectl apply -f whoami-route.yaml
+```
+
+### Using IngressRoute
+
+```yaml
+apiVersion: traefik.io/v1alpha1
+kind: IngressRoute
+metadata:
+ name: whoami
+ namespace: default
+spec:
+ entryPoints:
+ - web
+ routes:
+ - match: Host(`whoami.docker.localhost`)
+ kind: Rule
+ services:
+ - name: whoami
+ port: 80
+```
+
+Save this as `whoami-ingressroute.yaml` and apply it:
+
+```bash
+kubectl apply -f whoami-ingressroute.yaml
+```
+
+### Verify Your Service
+
+Your service is now available at http://whoami.docker.localhost/. Test that it works:
+
+```bash
+curl -H "Host: whoami.docker.localhost" http://localhost/
+```
+
+!!! info
+ Make sure to remove the `ports.web.redirections` block from the `values.yaml` file if you followed the Kubernetes Setup Guide to install Traefik otherwise you will be redirected to the HTTPS entrypoint:
+
+ ```yaml
+ redirections:
+ entryPoint:
+ to: websecure
+ ```
+
+You should see output similar to:
+
+```bash
+Hostname: whoami-6d5d964cb-8pv4k
+IP: 127.0.0.1
+IP: ::1
+IP: 10.42.0.18
+IP: fe80::d4c0:3bff:fe20:b0a3
+RemoteAddr: 10.42.0.17:39872
+GET / HTTP/1.1
+Host: whoami.docker.localhost
+User-Agent: curl/7.68.0
+Accept: */*
+Accept-Encoding: gzip
+X-Forwarded-For: 10.42.0.1
+X-Forwarded-Host: whoami.docker.localhost
+X-Forwarded-Port: 80
+X-Forwarded-Proto: http
+X-Forwarded-Server: traefik-76cbd5b89c-rx5xn
+X-Real-Ip: 10.42.0.1
+```
+
+This confirms that Traefik is successfully routing requests to your whoami application.
+
+## Add Routing Rules
+
+Now we'll enhance our routing by directing traffic to different services based on URL paths. This is useful for API versioning, frontend/backend separation, or organizing microservices.
+
+First, deploy a second service to represent an API:
+
+```yaml
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: whoami-api
+ namespace: default
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: whoami-api
+ template:
+ metadata:
+ labels:
+ app: whoami-api
+ spec:
+ containers:
+ - name: whoami
+ image: traefik/whoami
+ env:
+ - name: WHOAMI_NAME
+ value: "API Service"
+ ports:
+ - containerPort: 80
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: whoami-api
+ namespace: default
+spec:
+ selector:
+ app: whoami-api
+ ports:
+ - port: 80
+```
+
+Save this as `whoami-api.yaml` and apply it:
+
+```bash
+kubectl apply -f whoami-api.yaml
+```
+
+Now set up path-based routing:
+
+### Gateway API with Path Rules
+
+Update your existing `HTTPRoute` to include path-based routing:
+
+```yaml
+apiVersion: gateway.networking.k8s.io/v1
+kind: HTTPRoute
+metadata:
+ name: whoami
+ namespace: default
+spec:
+ parentRefs:
+ - name: traefik-gateway
+ hostnames:
+ - "whoami.docker.localhost"
+ rules:
+ - matches:
+ - path:
+ type: PathPrefix
+ value: /api
+ backendRefs:
+ - name: whoami-api
+ port: 80
+ - matches:
+ - path:
+ type: PathPrefix
+ value: /
+ backendRefs:
+ - name: whoami
+ port: 80
+```
+
+Update the file `whoami-route.yaml` and apply it:
+
+```bash
+kubectl apply -f whoami-route.yaml
+```
+
+### IngressRoute with Path Rules
+
+Update your existing IngressRoute to include path-based routing:
+
+```yaml
+apiVersion: traefik.io/v1alpha1
+kind: IngressRoute
+metadata:
+ name: whoami
+ namespace: default
+spec:
+ entryPoints:
+ - web
+ routes:
+ - match: Host(`whoami.docker.localhost`) && Path(`/api`)
+ kind: Rule
+ services:
+ - name: whoami-api
+ port: 80
+ - match: Host(`whoami.docker.localhost`)
+ kind: Rule
+ services:
+ - name: whoami
+ port: 80
+```
+
+Save this as `whoami-ingressroute.yaml` and apply it:
+
+```bash
+kubectl apply -f whoami-ingressroute.yaml
+```
+
+### Test the Path-Based Routing
+
+Verify that different paths route to different services:
+
+```bash
+# Root path should go to the main whoami service
+curl -H "Host: whoami.docker.localhost" http://localhost/
+
+# /api path should go to the whoami-api service
+curl -H "Host: whoami.docker.localhost" http://localhost/api
+```
+
+For the `/api` requests, you should see the response showing "API Service" in the environment variables section, confirming that your path-based routing is working correctly:
+
+```bash
+{"hostname":"whoami-api-67d97b4868-dvvll","ip":["127.0.0.1","::1","10.42.0.9","fe80::10aa:37ff:fe74:31f2"],"headers":{"Accept":["*/*"],"Accept-Encoding":["gzip"],"User-Agent":["curl/8.7.1"],"X-Forwarded-For":["10.42.0.1"],"X-Forwarded-Host":["whoami.docker.localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["traefik-669c479df8-vkj22"],"X-Real-Ip":["10.42.0.1"]},"url":"/api","host":"whoami.docker.localhost","method":"GET","name":"API Service","remoteAddr":"10.42.0.13:36592"}
+```
+
+## Enable TLS
+
+Let's secure our service with HTTPS by adding TLS. We'll start with a self-signed certificate for local development.
+
+### Create a Self-Signed Certificate
+
+Generate a self-signed certificate:
+
+```bash
+openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
+ -keyout tls.key -out tls.crt \
+ -subj "/CN=whoami.docker.localhost"
+```
+
+Create a TLS secret in Kubernetes:
+
+```bash
+kubectl create secret tls whoami-tls --cert=tls.crt --key=tls.key
+```
+
+!!! important "Prerequisite for Gateway API with TLS"
+ Before using the Gateway API with TLS, you must define the `websecure` listener in your Traefik installation. This is typically done in your Helm values.
+
+ Example configuration in `values.yaml`:
+ ```yaml
+ gateway:
+ listeners:
+ web:
+ port: 80
+ protocol: HTTP
+ namespacePolicy: All
+ websecure:
+ port: 443
+ protocol: HTTPS
+ namespacePolicy: All
+ mode: Terminate
+ certificateRefs:
+ - kind: Secret
+ name: local-selfsigned-tls
+ group: ""
+ ```
+
+ See the Traefik Kubernetes Setup Guide for complete installation details.
+
+### Gateway API with TLS
+
+Update your existing `HTTPRoute` to use the secured gateway listener:
+
+```yaml
+apiVersion: gateway.networking.k8s.io/v1
+kind: HTTPRoute
+metadata:
+ name: whoami
+ namespace: default
+spec:
+ parentRefs:
+ - name: traefik-gateway
+ sectionName: websecure # The HTTPS listener
+ hostnames:
+ - "whoami.docker.localhost"
+ rules:
+ - matches:
+ - path:
+ type: PathPrefix
+ value: /api
+ backendRefs:
+ - name: whoami-api
+ port: 80
+ - matches:
+ - path:
+ type: PathPrefix
+ value: /
+ backendRefs:
+ - name: whoami
+ port: 80
+```
+
+Update the file `whoami-route.yaml` and apply it:
+
+```bash
+kubectl apply -f whoami-route.yaml
+```
+
+### IngressRoute with TLS
+
+Update your existing IngressRoute to use TLS:
+
+```yaml
+apiVersion: traefik.io/v1alpha1
+kind: IngressRoute
+metadata:
+ name: whoami
+ namespace: default
+spec:
+ entryPoints:
+ - websecure # Changed from 'web' to 'websecure'
+ routes:
+ - match: Host(`whoami.docker.localhost`) && Path(`/api`)
+ kind: Rule
+ services:
+ - name: whoami-api
+ port: 80
+ - match: Host(`whoami.docker.localhost`)
+ kind: Rule
+ services:
+ - name: whoami
+ port: 80
+ tls:
+ secretName: whoami-tls # Added TLS configuration
+```
+
+Update the file `whoami-ingressroute.yaml` and apply it:
+
+```bash
+kubectl apply -f whoami-ingressroute.yaml
+```
+
+### Verify HTTPS Access
+
+Now you can access your service securely. Since we're using a self-signed certificate, you'll need to skip certificate verification:
+
+```bash
+curl -k -H "Host: whoami.docker.localhost" https://localhost/
+```
+
+Your browser can also access https://whoami.docker.localhost/ (you'll need to accept the security warning for the self-signed certificate).
+
+## Add Middlewares
+
+Middlewares allow you to modify requests or responses as they pass through Traefik. Let's add two useful middlewares: [Headers](../reference/routing-configuration/http/middlewares/headers.md) for security and [IP allowlisting](../reference/routing-configuration/http/middlewares/ipallowlist.md) for access control.
+
+### Create Middlewares
+
+```yaml
+apiVersion: traefik.io/v1alpha1
+kind: Middleware
+metadata:
+ name: secure-headers
+ namespace: default
+spec:
+ headers:
+ frameDeny: true
+ sslRedirect: true
+ browserXssFilter: true
+ contentTypeNosniff: true
+ stsIncludeSubdomains: true
+ stsPreload: true
+ stsSeconds: 31536000
+---
+apiVersion: traefik.io/v1alpha1
+kind: Middleware
+metadata:
+ name: ip-allowlist
+ namespace: default
+spec:
+ ipAllowList:
+ sourceRange:
+ - 127.0.0.1/32
+ - 10.0.0.0/8 # Typical cluster network range
+ - 192.168.0.0/16 # Common local network range
+```
+
+Save this as `middlewares.yaml` and apply it:
+
+```bash
+kubectl apply -f middlewares.yaml
+```
+
+### Apply Middlewares with Gateway API
+
+In Gateway API, you can apply middlewares using the `ExtensionRef` filter type. This is the preferred and standard way to use Traefik middlewares with Gateway API, as it integrates directly with the HTTPRoute specification.
+
+First, make sure you have the same middlewares defined:
+
+```yaml
+apiVersion: traefik.io/v1alpha1
+kind: Middleware
+metadata:
+ name: secure-headers
+ namespace: default
+spec:
+ headers:
+ frameDeny: true
+ sslRedirect: true
+ browserXssFilter: true
+ contentTypeNosniff: true
+ stsIncludeSubdomains: true
+ stsPreload: true
+ stsSeconds: 31536000
+---
+apiVersion: traefik.io/v1alpha1
+kind: Middleware
+metadata:
+ name: ip-allowlist
+ namespace: default
+spec:
+ ipAllowList:
+ sourceRange:
+ - 127.0.0.1/32
+ - 10.0.0.0/8 # Typical cluster network range
+ - 192.168.0.0/16 # Common local network range
+```
+
+Now, update your `HTTPRoute` to reference these middlewares using the `ExtensionRef` filter:
+
+```yaml
+apiVersion: gateway.networking.k8s.io/v1
+kind: HTTPRoute
+metadata:
+ name: whoami
+ namespace: default
+spec:
+ parentRefs:
+ - name: traefik-gateway
+ sectionName: websecure
+ hostnames:
+ - "whoami.docker.localhost"
+ rules:
+ - matches:
+ - path:
+ type: PathPrefix
+ value: /api
+ filters:
+ - type: ExtensionRef
+ extensionRef: # Headers Middleware Definition
+ group: traefik.io
+ kind: Middleware
+ name: secure-headers
+ - type: ExtensionRef
+ extensionRef: # IP AllowList Middleware Definition
+ group: traefik.io
+ kind: Middleware
+ name: ip-allowlist
+ backendRefs:
+ - name: whoami-api
+ port: 80
+ - matches:
+ - path:
+ type: PathPrefix
+ value: /
+ filters:
+ - type: ExtensionRef
+ extensionRef: # Headers Middleware Definition
+ group: traefik.io
+ kind: Middleware
+ name: secure-headers
+ - type: ExtensionRef
+ extensionRef: # IP AllowList Middleware Definition
+ group: traefik.io
+ kind: Middleware
+ name: ip-allowlist
+ backendRefs:
+ - name: whoami
+ port: 80
+```
+
+Update the file `whoami-route.yaml` and apply it:
+
+```bash
+kubectl apply -f whoami-route.yaml
+```
+
+This approach uses the Gateway API's native filter mechanism rather than annotations. The `ExtensionRef` filter type allows you to reference Traefik middlewares directly within the HTTPRoute specification, which is more consistent with the Gateway API design principles.
+
+### Apply Middlewares with IngressRoute
+
+Update your existing IngressRoute to include middlewares:
+
+```yaml
+apiVersion: traefik.io/v1alpha1
+kind: IngressRoute
+metadata:
+ name: whoami
+ namespace: default
+spec:
+ entryPoints:
+ - websecure
+ routes:
+ - match: Host(`whoami.docker.localhost`) && Path(`/api`)
+ kind: Rule
+ middlewares: # Middleware Definition
+ - name: secure-headers
+ - name: ip-allowlist
+ services:
+ - name: whoami-api
+ port: 80
+ - match: Host(`whoami.docker.localhost`)
+ kind: Rule
+ middlewares: # Middleware Definition
+ - name: secure-headers
+ - name: ip-allowlist
+ services:
+ - name: whoami
+ port: 80
+ tls:
+ certResolver: le
+```
+
+Update the file `whoami-ingressroute.yaml` and apply it:
+
+```bash
+kubectl apply -f whoami-ingressroute.yaml
+```
+
+### Verify Middleware Effects
+
+Check that the security headers are being applied:
+
+```bash
+curl -k -I -H "Host: whoami.docker.localhost" https://localhost/
+```
+
+You should see security headers in the response, such as:
+
+```bash
+HTTP/2 200
+x-content-type-options: nosniff
+x-frame-options: DENY
+x-xss-protection: 1; mode=block
+strict-transport-security: max-age=31536000; includeSubDomains; preload
+content-type: text/plain; charset=utf-8
+content-length: 403
+```
+
+To test the IP allowlist, you can modify the `sourceRange` in the middleware to exclude your IP and verify that access is blocked.
+
+## Generate Certificates with Let's Encrypt
+
+!!! info
+ Traefik's built-in Let's Encrypt integration works with IngressRoute but does not automatically issue certificates for Gateway API listeners. For Gateway API, you should use cert-manager or another certificate controller.
+
+### Using IngressRoute with Let's Encrypt
+
+Configure a certificate resolver in your Traefik values.yaml:
+
+```yaml
+additionalArguments:
+ - "--certificatesresolvers.le.acme.email=your-email@example.com" #replace with your email
+ - "--certificatesresolvers.le.acme.storage=/data/acme.json"
+ - "--certificatesresolvers.le.acme.httpchallenge.entrypoint=web"
+```
+
+!!! important "Public DNS Required"
+ Let's Encrypt may require a publicly accessible domain to validate domain ownership. For testing with local domains like `whoami.docker.localhost`, the certificate will remain self-signed. In production, replace it with a real domain that has a publicly accessible DNS record pointing to your Traefik instance.
+
+Update your Traefik installation with this configuration:
+
+```bash
+helm upgrade traefik traefik/traefik -n traefik --reuse-values -f values.yaml
+```
+
+Update your IngressRoute with the Let's Encrypt certificate:
+
+```yaml
+apiVersion: traefik.io/v1alpha1
+kind: IngressRoute
+metadata:
+ name: whoami
+ namespace: default
+spec:
+ entryPoints:
+ - websecure
+ routes:
+ - match: Host(`whoami.docker.localhost`) && Path(`/api`)
+ kind: Rule
+ middlewares:
+ - name: secure-headers
+ - name: ip-allowlist
+ services:
+ - name: whoami-api
+ port: 80
+ - match: Host(`whoami.docker.localhost`)
+ kind: Rule
+ middlewares:
+ - name: secure-headers
+ - name: ip-allowlist
+ services:
+ - name: whoami
+ port: 80
+ tls:
+ certResolver: le
+```
+
+Apply it:
+
+```bash
+kubectl apply -f whoami-ingressroute.yaml
+```
+
+### Using Gateway API with cert-manager
+
+For Gateway API, install cert-manager:
+
+```bash
+kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.10.0/cert-manager.yaml
+```
+
+Create an Issuer & Certificate:
+
+```yaml
+apiVersion: cert-manager.io/v1
+kind: Issuer
+metadata:
+ name: letsencrypt
+spec:
+ acme:
+ email: your-email@example.com # replace with your email
+ server: https://acme-v02-staging.api.letsencrypt.org/directory # Replace with the production server in production
+ privateKeySecretRef:
+ name: letsencrypt-account-key
+ solvers:
+ - http01:
+ gatewayHTTPRoute:
+ parentRefs:
+ - name: traefik
+ namespace: default
+ kind: Gateway
+---
+apiVersion: cert-manager.io/v1
+kind: Certificate
+metadata:
+ name: whoami
+ namespace: default
+spec:
+ secretName: whoami-tls-le # Name of secret where the generated certificate will be stored.
+ dnsNames:
+ - "whoami.docker.localhost" # Replace a real domain
+ issuerRef:
+ name: letsencrypt
+ kind: Issuer
+```
+
+!!! important "Public DNS Required"
+ Let's Encrypt requires a publicly accessible domain to verify ownership. When using a local domain like `whoami.docker.localhost`, cert-manager will attempt the challenge but it will fail, and the certificate will remain self-signed. For production use, replace the domain with one that has a public DNS record pointing to your cluster's ingress point.
+
+Save the YAML file and apply:
+
+```bash
+kubectl apply -f letsencrypt-issuer-andwhoami-certificate.yaml
+```
+
+Now, update your Gateway to use the generated certificate:
+
+```yaml
+apiVersion: gateway.networking.k8s.io/v1
+kind: Gateway
+metadata:
+ name: traefik-gateway
+ namespace: default
+spec:
+ gatewayClassName: traefik
+ listeners:
+ - name: web
+ port: 80
+ protocol: HTTP
+ allowedRoutes:
+ namespaces:
+ from: All
+ - name: websecure
+ port: 443
+ protocol: HTTPS
+ allowedRoutes:
+ namespaces:
+ from: All
+ tls:
+ certificateRefs:
+ - name: whoami-tls-le # References the secret created by cert-manager
+```
+
+Apply the updated Gateway:
+
+```bash
+kubectl apply -f gateway.yaml
+```
+
+Your existing `HTTPRoute` will now use this certificate when connecting to the secured gateway listener.
+
+### Verify the Let's Encrypt Certificate
+
+Once the certificate is issued, you can verify it:
+
+```bash
+# Check certificate status
+kubectl get certificate -n default
+
+# Verify the certificate chain
+curl -v https://whoami.docker.localhost/ 2>&1 | grep -i "server certificate"
+```
+
+You should see that your certificate is issued by Let's Encrypt.
+
+## Configure Sticky Sessions
+
+Sticky sessions ensure that a user's requests always go to the same backend server, which is essential for applications that maintain session state. Let's implement sticky sessions for our whoami service.
+
+### First, Scale Up the Deployment
+
+To demonstrate sticky sessions, first scale up the deployment to 3 replicas:
+
+```bash
+kubectl scale deployment whoami --replicas=3
+```
+
+### Using Gateway API with TraefikService
+
+First, create the `TraefikService` for sticky sessions:
+
+```yaml
+apiVersion: traefik.io/v1alpha1
+kind: TraefikService
+metadata:
+ name: whoami-sticky
+ namespace: default
+spec:
+ weighted:
+ services:
+ - name: whoami
+ port: 80
+ weight: 1
+ sticky:
+ cookie:
+ name: sticky_cookie
+ secure: true
+ httpOnly: true
+```
+
+Save this as `whoami-sticky-service.yaml` and apply it:
+
+```bash
+kubectl apply -f whoami-sticky-service.yaml
+```
+
+Now update your `HTTPRoute` with an annotation referencing the `TraefikService`:
+
+```yaml
+apiVersion: gateway.networking.k8s.io/v1
+kind: HTTPRoute
+metadata:
+ name: whoami
+ namespace: default
+spec:
+ parentRefs:
+ - name: traefik-gateway
+ sectionName: websecure
+ hostnames:
+ - "whoami.docker.localhost"
+ rules:
+ - matches:
+ - path:
+ type: PathPrefix
+ value: /api
+ filters:
+ - type: ExtensionRef
+ extensionRef: # Headers Middleware Definition
+ group: traefik.io
+ kind: Middleware
+ name: secure-headers
+ - type: ExtensionRef
+ extensionRef: # IP AllowList Middleware Definition
+ group: traefik.io
+ kind: Middleware
+ name: ip-allowlist
+ backendRefs:
+ - name: whoami-api
+ port: 80
+ - matches:
+ - path:
+ type: PathPrefix
+ value: /
+ backendRefs:
+ - group: traefik.io # <── tell Gateway this is a TraefikService
+ kind: TraefikService
+ name: whoami-sticky
+ filters:
+ - type: ExtensionRef
+ extensionRef: # Headers Middleware Definition
+ group: traefik.io
+ kind: Middleware
+ name: secure-headers
+ - type: ExtensionRef
+ extensionRef: # IP AllowList Middleware Definition
+ group: traefik.io
+ kind: Middleware
+ name: ip-allowlist
+ backendRefs:
+ - name: whoami
+ port: 80
+```
+
+Update the file `whoami-route.yaml` and apply it:
+
+```bash
+kubectl apply -f whoami-route.yaml
+```
+
+### Using IngressRoute with TraefikService
+
+First, create the `TraefikService` for sticky sessions:
+
+```yaml
+apiVersion: traefik.io/v1alpha1
+kind: TraefikService
+metadata:
+ name: whoami-sticky
+ namespace: default
+spec:
+ weighted:
+ services:
+ - name: whoami
+ port: 80
+ sticky:
+ cookie:
+ name: sticky_cookie
+ secure: true
+ httpOnly: true
+```
+
+Save this as `whoami-sticky-service.yaml` and apply it:
+
+```bash
+kubectl apply -f whoami-sticky-service.yaml
+```
+
+Now update your IngressRoute to use this `TraefikService`:
+
+```yaml
+apiVersion: traefik.io/v1alpha1
+kind: IngressRoute
+metadata:
+ name: whoami
+ namespace: default
+spec:
+ entryPoints:
+ - websecure
+ routes:
+ - match: Host(`whoami.docker.localhost`) && Path(`/api`)
+ kind: Rule
+ middlewares: # Middleware Definition
+ - name: secure-headers
+ - name: ip-allowlist
+ services:
+ - name: whoami-api
+ port: 80
+ - match: Host(`whoami.docker.localhost`)
+ kind: Rule
+ middlewares: # Middleware Definition
+ - name: secure-headers
+ - name: ip-allowlist
+ services:
+ - name: whoami-sticky # Changed from whoami to whoami-sticky
+ kind: TraefikService # Added kind: TraefikService
+ tls:
+ certResolver: le
+```
+
+Update the file `whoami-ingressroute.yaml` and apply it:
+
+```bash
+kubectl apply -f whoami-ingressroute.yaml
+```
+
+### Test Sticky Sessions
+
+You can test the sticky sessions by making multiple requests and observing that they all go to the same backend pod:
+
+```bash
+# First request - save cookies to a file
+curl -k -c cookies.txt -H "Host: whoami.docker.localhost" https://localhost/
+
+# Subsequent requests - use the cookies
+curl -k -b cookies.txt -H "Host: whoami.docker.localhost" https://localhost/
+curl -k -b cookies.txt -H "Host: whoami.docker.localhost" https://localhost/
+```
+
+Pay attention to the `Hostname` field in each response - it should remain the same across all requests when using the cookie file, confirming that sticky sessions are working.
+
+For comparison, try making requests without the cookie:
+
+```bash
+# Requests without cookies should be load-balanced across different pods
+curl -k -H "Host: whoami.docker.localhost" https://localhost/
+curl -k -H "Host: whoami.docker.localhost" https://localhost/
+```
+
+You should see different `Hostname` values in these responses, as each request is load-balanced to a different pod.
+
+!!! important "Browser Testing"
+ When testing in browsers, you need to use the same browser session to maintain the cookie. The cookie is set with `httpOnly` and `secure` flags for security, so it will only be sent over HTTPS connections and won't be accessible via JavaScript.
+
+For more advanced configuration options, see the [reference documentation](../reference/routing-configuration/http/load-balancing/service.md).
+
+## Conclusion
+
+In this guide, you've learned how to:
+
+- Expose HTTP services through Traefik in Kubernetes using both Gateway API and IngressRoute
+- Set up path-based routing to direct traffic to different backend services
+- Secure your services with TLS using self-signed certificates
+- Add security with middlewares like secure headers and IP allow listing
+- Automate certificate management with Let's Encrypt
+- Implement sticky sessions for stateful applications
+
+These fundamental capabilities provide a solid foundation for exposing any application through Traefik Proxy in Kubernetes. Each of these can be further customized to meet your specific requirements.
+
+### Next Steps
+
+Now that you understand the basics of exposing services with Traefik Proxy, you might want to explore:
+
+- [Advanced routing options](../reference/routing-configuration/http/router/rules-and-priority.md) like query parameter matching, header-based routing, and more
+- [Additional middlewares](../reference/routing-configuration/http/middlewares/overview.md) for authentication, rate limiting, and request modifications
+- [Observability features](../reference/install-configuration/observability/metrics.md) for monitoring and debugging your Traefik deployment
+- [TCP services](../reference/routing-configuration/tcp/service.md) for exposing TCP services
+- [UDP services](../reference/routing-configuration/udp/service.md) for exposing UDP services
+- [Kubernetes Provider documentation](../reference/install-configuration/providers/kubernetes/kubernetes-crd.md) for more details about the Kubernetes integration.
+- [Gateway API provider documentation](../reference/install-configuration/providers/kubernetes/kubernetes-gateway.md) for more details about the Gateway API integration.
diff --git a/docs/content/expose/overview.md b/docs/content/expose/overview.md
new file mode 100644
index 000000000..641203a65
--- /dev/null
+++ b/docs/content/expose/overview.md
@@ -0,0 +1,22 @@
+# Exposing Services with Traefik Proxy
+
+This section guides you through exposing services securely with Traefik Proxy. You'll learn how to route HTTP and HTTPS traffic to your services, add security features, and implement advanced load balancing.
+
+## What You'll Accomplish
+
+Following these guides, you'll learn how to:
+
+- Route HTTP traffic to your services with [Gateway API](../reference/routing-configuration/kubernetes/gateway-api.md) and [IngressRoute](../reference/routing-configuration/kubernetes/crd/http/ingressroute.md)
+- Configure routing rules to direct requests
+- Enable HTTPS with TLS
+- Add security middlewares
+- Generate certificates automatically with Let's Encrypt
+- Implement sticky sessions for session persistence
+
+## Platform-Specific Guides
+
+For detailed steps tailored to your environment, follow the guide for your platform:
+
+- [Kubernetes](./kubernetes.md)
+- [Docker](./docker.md)
+- [Docker Swarm](./swarm.md)
diff --git a/docs/content/expose/swarm.md b/docs/content/expose/swarm.md
new file mode 100644
index 000000000..2dac7b060
--- /dev/null
+++ b/docs/content/expose/swarm.md
@@ -0,0 +1,401 @@
+# Exposing Services with Traefik on Docker Swarm
+
+This guide will help you expose your services securely through Traefik Proxy using Docker Swarm. We'll cover routing HTTP and HTTPS traffic, implementing TLS, adding middlewares, Let's Encrypt integration, and sticky sessions.
+
+## Prerequisites
+
+- Docker Swarm cluster initialized
+- Basic understanding of Docker Swarm concepts
+- Traefik deployed using the Traefik Docker Swarm Setup guide
+
+## Expose Your First HTTP Service
+
+Let's expose a simple HTTP service using the [whoami](https://hub.docker.com/r/traefik/whoami) application. This will demonstrate basic routing to a backend service.
+
+First, update your existing `docker-compose.yml` file if you haven't already:
+
+```yaml
+services:
+ whoami:
+ image: traefik/whoami
+ networks:
+ - traefik_proxy
+ deploy:
+ replicas: 3
+ labels:
+ - "traefik.enable=true"
+ - "traefik.http.routers.whoami.rule=Host(`whoami.swarm.localhost`)"
+ - "traefik.http.routers.whoami.entrypoints=web,websecure"
+```
+
+Save this as `docker-compose.yml` and deploy the stack:
+
+```bash
+docker stack deploy -c docker-compose.yml traefik
+```
+
+### Verify Your Service
+
+Your service is now available at http://whoami.swarm.localhost/. Test that it works:
+
+```bash
+curl -H "Host: whoami.swarm.localhost" http://localhost/
+```
+
+You should see output similar to:
+
+```bash
+Hostname: whoami.1.7c8f7tr56q3p949rscxrkp80e
+IP: 127.0.0.1
+IP: ::1
+IP: 10.0.1.8
+IP: fe80::215:5dff:fe00:c9e
+RemoteAddr: 10.0.1.2:45098
+GET / HTTP/1.1
+Host: whoami.swarm.localhost
+User-Agent: curl/7.68.0
+Accept: */*
+Accept-Encoding: gzip
+X-Forwarded-For: 10.0.1.1
+X-Forwarded-Host: whoami.swarm.localhost
+X-Forwarded-Port: 80
+X-Forwarded-Proto: http
+X-Forwarded-Server: 5789f594e7d5
+X-Real-Ip: 10.0.1.1
+```
+
+This confirms that Traefik is successfully routing requests to your whoami application.
+
+## Add Routing Rules
+
+Now we'll enhance our routing by directing traffic to different services based on [URL paths](../reference/routing-configuration/http/router/rules-and-priority.md#path-pathprefix-and-pathregexp). This is useful for API versioning, frontend/backend separation, or organizing microservices.
+
+Update your `docker-compose.yml` to add another service:
+
+```yaml
+# ...
+
+# New service
+ whoami-api:
+ image: traefik/whoami
+ networks:
+ - traefik_proxy
+ environment:
+ - WHOAMI_NAME=API Service
+ deploy:
+ replicas: 2
+ labels:
+ - "traefik.enable=true"
+ # Path-based routing
+ - "traefik.http.routers.whoami-api.rule=Host(`whoami.swarm.localhost`) && PathPrefix(`/api`)"
+ - "traefik.http.routers.whoami-api.entrypoints=web,websecure"
+ - "traefik.http.routers.whoami-api.service=whoami-api-svc"
+ - "traefik.http.services.whoami-api-svc.loadbalancer.server.port=80"
+
+# ...
+```
+
+Apply the changes:
+
+```bash
+docker stack deploy -c docker-compose.yml traefik
+```
+
+### Test the Path-Based Routing
+
+Verify that different paths route to different services:
+
+```bash
+# Root path should go to the main whoami service
+curl -H "Host: whoami.swarm.localhost" http://localhost/
+
+# /api path should go to the whoami-api service
+curl -H "Host: whoami.swarm.localhost" http://localhost/api
+```
+
+For the `/api` requests, you should see the response showing "API Service" in the environment variables section, confirming that your path-based routing is working correctly.
+
+## Enable TLS
+
+Let's secure our service with HTTPS by adding TLS. We'll start with a self-signed certificate for local development.
+
+### Create a Self-Signed Certificate
+
+Generate a self-signed certificate and dynamic config file to tell Traefik where the cert lives:
+
+```bash
+mkdir -p certs
+
+# key + cert (valid for one year)
+openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
+ -keyout certs/local.key -out certs/local.crt \
+ -subj "/CN=*.swarm.localhost"
+
+# dynamic config that tells Traefik where the cert lives
+cat > certs/tls.yml <<'EOF'
+tls:
+ certificates:
+ - certFile: /certificates/local.crt
+ keyFile: /certificates/local.key
+EOF
+```
+
+Create a Docker config for the certificate files:
+
+```bash
+docker config create swarm-cert.crt certs/local.crt
+docker config create swarm-cert.key certs/local.key
+docker config create swarm-tls.yml certs/tls.yml
+```
+
+Update your `docker-compose.yml` file with the following changes:
+
+```yaml
+# Add to the Traefik command section:
+command:
+ # ... existing commands ...
+ - "--entryPoints.websecure.address=:443"
+ - "--entryPoints.websecure.http.tls=true"
+ - "--providers.file.directory=/etc/traefik/dynamic"
+```
+
+```yaml
+# Add to the root of your docker-compose.yml file:
+configs:
+ swarm-cert.crt:
+ file: ./certs/local.crt
+ swarm-cert.key:
+ file: ./certs/local.key
+ swarm-tls.yml:
+ file: ./certs/tls.yml
+```
+
+Deploy the stack:
+
+```bash
+docker stack deploy -c docker-compose.yml traefik
+```
+
+Your browser can access https://whoami.swarm.localhost/ for the service. You'll need to accept the security warning for the self-signed certificate.
+
+## Add Middlewares
+
+Middlewares allow you to modify requests or responses as they pass through Traefik. Let's add two useful middlewares: [Headers](../reference/routing-configuration/http/middlewares/headers.md) for security and [IP allowlisting](../reference/routing-configuration/http/middlewares/ipallowlist.md) for access control.
+
+Add the following labels to your whoami service deployment section in `docker-compose.yml`:
+
+```yaml
+deploy:
+ # ... existing configuration ...
+ labels:
+ # ... existing labels ...
+
+ # Secure Headers Middleware
+ - "traefik.http.middlewares.secure-headers.headers.frameDeny=true"
+ - "traefik.http.middlewares.secure-headers.headers.sslRedirect=true"
+ - "traefik.http.middlewares.secure-headers.headers.browserXssFilter=true"
+ - "traefik.http.middlewares.secure-headers.headers.contentTypeNosniff=true"
+ - "traefik.http.middlewares.secure-headers.headers.stsIncludeSubdomains=true"
+ - "traefik.http.middlewares.secure-headers.headers.stsPreload=true"
+ - "traefik.http.middlewares.secure-headers.headers.stsSeconds=31536000"
+
+ # IP Allowlist Middleware
+ - "traefik.http.middlewares.ip-allowlist.ipallowlist.sourceRange=127.0.0.1/32,192.168.0.0/16,10.0.0.0/8"
+
+ # Apply the middlewares
+ - "traefik.http.routers.whoami.middlewares=secure-headers,ip-allowlist"
+```
+
+Add the same middleware to your whoami-api service:
+
+```yaml
+deploy:
+ # ... existing configuration ...
+ labels:
+ # ... existing labels ...
+ - "traefik.http.routers.whoami-api.middlewares=secure-headers,ip-allowlist"
+```
+
+Apply the changes:
+
+```bash
+docker stack deploy -c docker-compose.yml traefik
+```
+
+### Test the Middlewares
+
+Now let's verify that our middlewares are working correctly:
+
+Test the Secure Headers middleware:
+
+```bash
+curl -k -I -H "Host: whoami.swarm.localhost" https://localhost/
+```
+
+In the response headers, you should see security headers set by the middleware:
+
+- `X-Frame-Options: DENY`
+- `X-Content-Type-Options: nosniff`
+- `X-XSS-Protection: 1; mode=block`
+- `Strict-Transport-Security` with the appropriate settings
+
+Test the IP Allowlist middleware:
+
+If your request comes from an IP that's in the allow list (e.g., 127.0.0.1), it should succeed:
+
+```bash
+curl -k -I -H "Host: whoami.swarm.localhost" https://localhost/
+```
+
+If you try to access from an IP not in the allow list, the request will be rejected with a `403` Forbidden response. To simulate this in a local environment, you can modify the middleware configuration temporarily to exclude your IP address, then test again.
+
+## Generate Certificates with Let's Encrypt
+
+Let's Encrypt provides free, automated TLS certificates. Let's configure Traefik to automatically obtain and renew certificates for our services.
+
+Instead of using self-signed certificates, update your existing `docker-compose.yml` file with the following changes:
+
+Add the Let's Encrypt certificate resolver to the Traefik service command section:
+
+```yaml
+command:
+ # ... existing commands ...
+ # Let's Encrypt configuration
+ - "--certificatesresolvers.le.acme.email=your-email@example.com" # replace with your actual email
+ - "--certificatesresolvers.le.acme.storage=/letsencrypt/acme.json"
+ - "--certificatesresolvers.le.acme.httpchallenge.entrypoint=web"
+```
+
+Add a volume for Let's Encrypt certificates:
+
+```yaml
+volumes:
+ # ...Existing volumes...
+ - letsencrypt:/letsencrypt
+```
+
+Update your service labels to use the certificate resolver:
+
+```yaml
+labels:
+ # ... existing labels ...
+ - "traefik.http.routers.whoami.tls.certresolver=le"
+```
+
+Do the same for any other services you want to secure:
+
+```yaml
+labels:
+ # ... existing labels ...
+ - "traefik.http.routers.whoami-api.tls.certresolver=le"
+```
+
+Create a named volume for storing Let's Encrypt certificates by adding to the volumes section:
+
+```yaml
+volumes:
+ # ... existing volumes ...
+ letsencrypt:
+ driver: local
+```
+
+Apply the changes:
+
+```bash
+docker stack deploy -c docker-compose.yml traefik
+```
+
+!!! important "Public DNS Required"
+ Let's Encrypt may require a publicly accessible domain to validate domain ownership. For testing with local domains like `whoami.swarm.localhost`, the certificate will remain self-signed. In production, replace it with a real domain that has a publicly accessible DNS record pointing to your Traefik instance.
+
+Once the certificate is issued, you can verify it:
+
+```bash
+# Verify the certificate chain
+curl -v https://whoami.swarm.localhost/ 2>&1 | grep -i "server certificate"
+```
+
+You should see that your certificate is issued by Let's Encrypt.
+
+## Configure Sticky Sessions
+
+Sticky sessions ensure that a user's requests always go to the same backend server, which is essential for applications that maintain session state. Let's implement sticky sessions for our whoami service.
+
+Docker Swarm already has multiple replicas running; we'll now add sticky session configuration. Update your whoami service in the `docker-compose.yml` file:
+
+### Add Sticky Session Configuration
+
+Add the following labels to your whoami service in the `docker-compose.yml` file:
+
+```yaml
+deploy:
+ # ... existing configuration ...
+ labels:
+ # ... existing labels ...
+
+ # Sticky Sessions Configuration
+ - "traefik.http.services.whoami.loadbalancer.sticky.cookie=true"
+ - "traefik.http.services.whoami.loadbalancer.sticky.cookie.name=sticky_cookie"
+ - "traefik.http.services.whoami.loadbalancer.sticky.cookie.secure=true"
+ - "traefik.http.services.whoami.loadbalancer.sticky.cookie.httpOnly=true"
+```
+
+Apply the changes:
+
+```bash
+docker stack deploy -c docker-compose.yml traefik
+```
+
+### Test Sticky Sessions
+
+You can test the sticky sessions by making multiple requests and observing that they all go to the same backend container:
+
+```bash
+# First request - save cookies to a file
+curl -k -c cookies.txt -H "Host: whoami.swarm.localhost" https://localhost/
+
+# Subsequent requests - use the cookies
+curl -k -b cookies.txt -H "Host: whoami.swarm.localhost" https://localhost/
+curl -k -b cookies.txt -H "Host: whoami.swarm.localhost" https://localhost/
+```
+
+Pay attention to the `Hostname` field in each response - it should remain the same across all requests when using the cookie file, confirming that sticky sessions are working.
+
+For comparison, try making requests without the cookie:
+
+```bash
+# Requests without cookies should be load-balanced across different containers
+curl -k -H "Host: whoami.swarm.localhost" https://localhost/
+curl -k -H "Host: whoami.swarm.localhost" https://localhost/
+```
+
+You should see different `Hostname` values in these responses, as each request is load-balanced to a different container.
+
+!!! important "Browser Testing"
+ When testing in browsers, you need to use the same browser session to maintain the cookie. The cookie is set with `httpOnly` and `secure` flags for security, so it will only be sent over HTTPS connections and won't be accessible via JavaScript.
+
+For more advanced configuration options, see the [reference documentation](../reference/routing-configuration/http/load-balancing/service.md).
+
+## Conclusion
+
+In this guide, you've learned how to:
+
+- Expose HTTP services through Traefik in Docker Swarm
+- Set up path-based routing to direct traffic to different backend services
+- Secure your services with TLS using self-signed certificates
+- Add security with middlewares like secure headers and IP allow listing
+- Automate certificate management with Let's Encrypt
+- Implement sticky sessions for stateful applications
+
+These fundamental capabilities provide a solid foundation for exposing any application through Traefik Proxy in Docker Swarm. Each of these can be further customized to meet your specific requirements.
+
+### Next Steps
+
+Now that you understand the basics of exposing services with Traefik Proxy, you might want to explore:
+
+- [Advanced routing options](../reference/routing-configuration/http/router/rules-and-priority.md) like query parameter matching, header-based routing, and more
+- [Additional middlewares](../reference/routing-configuration/http/middlewares/overview.md) for authentication, rate limiting, and request modifications
+- [Observability features](../reference/install-configuration/observability/metrics.md) for monitoring and debugging your Traefik deployment
+- [TCP services](../reference/routing-configuration/tcp/service.md) for exposing TCP services
+- [UDP services](../reference/routing-configuration/udp/service.md) for exposing UDP services
+- [Docker provider documentation](../reference/install-configuration/providers/docker.md) for more details about the Docker integration
diff --git a/docs/content/index.md b/docs/content/index.md
index bc5dd9fb7..738746c37 100644
--- a/docs/content/index.md
+++ b/docs/content/index.md
@@ -7,18 +7,15 @@ description: "Traefik Proxy, an open-source Edge Router, auto-discovers configur

-Traefik is an [open-source](https://github.com/traefik/traefik) *Application Proxy* that makes publishing your services a fun and easy experience.
-It receives requests on behalf of your system, identifies which components are responsible for handling them, and routes them securely.
+Traefik is an [open-source](https://github.com/traefik/traefik) Application Proxy and the core of the Traefik Hub Runtime Platform.
-What sets Traefik apart, besides its many features, is that it automatically discovers the right configuration for your services.
-The magic happens when Traefik inspects your infrastructure, where it finds relevant information and discovers which service serves which request.
+If you start with Traefik for service discovery and routing, you can seamlessly add [API management](https://traefik.io/solutions/api-management/), [API gateway](https://traefik.io/solutions/api-gateway/), [AI gateway](https://traefik.io/solutions/ai-gateway/), and [API mocking](https://traefik.io/solutions/api-mocking/) capabilities as needed.
-Traefik is natively compliant with every major cluster technology, such as Kubernetes, Docker Swarm, AWS, and [the list goes on](./reference/install-configuration/providers/overview.md); and can handle many at the same time. (It even works for legacy software running on bare metal.)
-
-With Traefik, there is no need to maintain and synchronize a separate configuration file: everything happens automatically, in real time (no restarts, no connection interruptions).
-With Traefik, you spend time developing and deploying new features to your system, not on configuring and maintaining its working state.
+With 3.3 billion downloads and over 55k stars on GitHub, Traefik is used globally across hybrid cloud, multi-cloud, on prem, and bare metal environments running Kuberentes, Docker Swarm, AWS, [the list goes on](https://doc.traefik.io/traefik/reference/install-configuration/providers/overview/).
-And if your needs change, you can add API gateway and API management capabilities seamlessly to your existing Traefik deployments. It takes less than a minute, there’s no rip-and-replace, and all your configurations are preserved. See this in action in [our API gateway demo video](https://info.traefik.io/watch-traefik-api-gw-demo?cta=docs).
+Here’s how it works—Traefik receives requests on behalf of your system, identifies which components are responsible for handling them, and routes them securely. It automatically discovers the right configuration for your services by inspecting your infrastructure to identify relevant information and which service serves which request.
+
+Because everything happens automatically, in real time (no restarts, no connection interruptions), you can focus on developing and deploying new features to your system, instead of configuring and maintaining its working state.
!!! quote "From the Traefik Maintainer Team"
When developing Traefik, our main goal is to make it easy to use, and we're sure you'll enjoy it.
@@ -53,6 +50,6 @@ Use the sidebar to navigate to the section that is most appropriate for your nee
Have a question? Join our [Community Forum](https://community.traefik.io "Link to Traefik Community Forum") to discuss, learn, and connect with the Traefik community.
- Using Traefik OSS in Production? Consider our enterprise-grade [API Gateway](https://info.traefik.io/watch-traefik-api-gw-demo?cta=doc) or our [24/7/365 OSS Support](https://info.traefik.io/request-commercial-support?cta=doc).
+ Using Traefik OSS in production? Consider upgrading to our API gateway ([watch demo video](https://info.traefik.io/watch-traefik-api-gw-demo)) for better security, control, and 24/7 support.
- Explore our API Gateway upgrade via [this short demo video](https://info.traefik.io/watch-traefik-api-gw-demo?cta=doc).
+ Just need support? Explore our [24/7/365 support for Traefik OSS](https://info.traefik.io/request-commercial-support?cta=doc).
diff --git a/docs/content/providers/swarm.md b/docs/content/providers/swarm.md
index d1db245df..1a9388174 100644
--- a/docs/content/providers/swarm.md
+++ b/docs/content/providers/swarm.md
@@ -450,10 +450,7 @@ _Optional, Default=""_
Defines a default docker network to use for connections to all containers.
-This option can be overridden on a per-container basis with the `traefik.docker.network` [routing label](../routing/providers/swarm.md#traefikdockernetwork).
-
-!!! warning
- The Docker Swarm provider still uses the same per-container mechanism as the Docker provider, so therefore the label still uses the `docker` keyword intentionally.
+This option can be overridden on a per-container basis with the `traefik.swarm.network` [routing label](../routing/providers/swarm.md#traefikswarmnetwork).
```yaml tab="File (YAML)"
providers:
diff --git a/docs/content/reference/install-configuration/observability/logs-and-accesslogs.md b/docs/content/reference/install-configuration/observability/logs-and-accesslogs.md
index 4559db3ea..d67feb027 100644
--- a/docs/content/reference/install-configuration/observability/logs-and-accesslogs.md
+++ b/docs/content/reference/install-configuration/observability/logs-and-accesslogs.md
@@ -44,8 +44,82 @@ The section below describe how to configure Traefik logs using the static config
| `log.maxBackups` | Maximum number of old log files to retain.
The default is to retain all old log files. | 0 | No |
| `log.compress` | Compress log files in gzip after rotation. | false | No |
+### OpenTelemetry
+
+Traefik supports OpenTelemetry for logging. To enable OpenTelemetry, you need to set the following in the static configuration:
+
+```yaml tab="File (YAML)"
+experimental:
+ otlpLogs: true
+```
+
+```toml tab="File (TOML)"
+[experimental]
+ otlpLogs = true
+```
+
+```sh tab="CLI"
+--experimental.otlpLogs=true
+```
+
+!!! warning
+ This is an experimental feature.
+
+#### Configuration Example
+
+```yaml tab="File (YAML)"
+experimental:
+ otlpLogs: true
+
+log:
+ otlp:
+ http:
+ endpoint: https://collector:4318/v1/logs
+ headers:
+ Authorization: Bearer auth_asKXRhIMplM7El1JENjrotGouS1LYRdL
+```
+
+```toml tab="File (TOML)"
+[experimental]
+ otlpLogs = true
+
+[log.otlp]
+ http.endpoint = "https://collector:4318/v1/logs"
+ http.headers.Authorization = "Bearer auth_asKXRhIMplM7El1JENjrotGouS1LYRdL"
+```
+
+```sh tab="CLI"
+--experimental.otlpLogs=true
+--log.otlp.http.endpoint=https://collector:4318/v1/logs
+--log.otlp.http.headers.Authorization=Bearer auth_asKXRhIMplM7El1JENjrotGouS1LYRdL
+```
+
+#### Configuration Options
+
+| Field | Description | Default | Required |
+|:-----------|:-----------------------------------------------------------------------------|:--------|:---------|
+| `log.otlp.http` | This instructs the exporter to send logs to the OpenTelemetry Collector using HTTP.| | No |
+| `log.otlp.http.endpoint` | The endpoint of the OpenTelemetry Collector. (format=`://:`) | `https://localhost:4318/v1/logs` | No |
+| `log.otlp.http.headers` | Additional headers sent with logs by the exporter to the OpenTelemetry Collector. | [ ] | No |
+| `log.otlp.http.tls` | Defines the Client TLS configuration used by the exporter to send logs to the OpenTelemetry Collector. | | No |
+| `log.otlp.http.tls.ca` | The path to the certificate authority used for the secure connection to the OpenTelemetry Collector, it defaults to the system bundle. | | No |
+| `log.otlp.http.tls.cert` | The path to the certificate to use for the OpenTelemetry Collector. | | No |
+| `log.otlp.http.tls.key` | The path to the key to use for the OpenTelemetry Collector. | | No |
+| `log.otlp.http.tls.insecureSkipVerify` | Instructs the OpenTelemetry Collector to accept any certificate presented by the server regardless of the hostname in the certificate. | false | No |
+| `log.otlp.grpc` | This instructs the exporter to send logs to the OpenTelemetry Collector using gRPC.| | No |
+| `log.otlp.grpc.endpoint` | The endpoint of the OpenTelemetry Collector. (format=`:`) | `localhost:4317` | No |
+| `log.otlp.grpc.headers` | Additional headers sent with logs by the exporter to the OpenTelemetry Collector. | [ ] | No |
+| `log.otlp.grpc.insecure` | Instructs the exporter to send logs to the OpenTelemetry Collector using an insecure protocol. | false | No |
+| `log.otlp.grpc.tls` | Defines the Client TLS configuration used by the exporter to send logs to the OpenTelemetry Collector. | | No |
+| `log.otlp.grpc.tls.ca` | The path to the certificate authority used for the secure connection to the OpenTelemetry Collector, it defaults to the system bundle. | | No |
+| `log.otlp.grpc.tls.cert` | The path to the certificate to use for the OpenTelemetry Collector. | | No |
+| `log.otlp.grpc.tls.key` | The path to the key to use for the OpenTelemetry Collector. | | No |
+| `log.otlp.grpc.tls.insecureSkipVerify` | Instructs the OpenTelemetry Collector to accept any certificate presented by the server regardless of the hostname in the certificate. | false | No |
+
## AccessLogs
+Access logs concern everything that happens to the requests handled by Traefik.
+
### Configuration Example
```yaml tab="File (YAML)"
@@ -111,6 +185,7 @@ accessLog:
--accesslog.fields.headers.names.Authorization=drop
```
+
### Configuration Options
The section below describes how to configure Traefik access logs using the static configuration.
@@ -121,15 +196,87 @@ The section below describes how to configure Traefik access logs using the stati
| `accesslog.format` | By default, logs are written using the Common Log Format (CLF).
To write logs in JSON, use `json` in the `format` option.
If the given format is unsupported, the default (CLF) is used instead.
More information about CLF fields [here](#clf-format-fields). | "common" | No |
| `accesslog.bufferingSize` | To write the logs in an asynchronous fashion, specify a `bufferingSize` option.
This option represents the number of log lines Traefik will keep in memory before writing them to the selected output.
In some cases, this option can greatly help performances.| 0 | No |
| `accesslog.addInternals` | Enables access logs for internal resources (e.g.: `ping@internal`). | false | No |
-| `accesslog.filters.statusCodes` | Limit the access logs to requests with a status codes in the specified range. | false | No |
+| `accesslog.filters.statusCodes` | Limit the access logs to requests with a status codes in the specified range. | [ ] | No |
| `accesslog.filters.retryAttempts` | Keep the access logs when at least one retry has happened. | false | No |
| `accesslog.filters.minDuration` | Keep access logs when requests take longer than the specified duration (provided in seconds or as a valid duration format, see [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration)). | 0 | No |
| `accesslog.fields.defaultMode` | Mode to apply by default to the access logs fields (`keep`, `redact` or `drop`). | keep | No |
-| `accesslog.fields.names` | Set the fields list to display in the access logs (format `name:mode`).
Available fields list [here](#available-fields). | - | No |
+| `accesslog.fields.names` | Set the fields list to display in the access logs (format `name:mode`).
Available fields list [here](#available-fields). | [ ] | No |
| `accesslog.headers.defaultMode` | Mode to apply by default to the access logs headers (`keep`, `redact` or `drop`). | drop | No |
-| `accesslog.headers.names` | Set the headers list to display in the access logs (format `name:mode`). | - | No |
+| `accesslog.headers.names` | Set the headers list to display in the access logs (format `name:mode`). | [ ] | No |
-#### CLF format fields
+### OpenTelemetry
+
+Traefik supports OpenTelemetry for access logs. To enable OpenTelemetry, you need to set the following in the static configuration:
+
+```yaml tab="File (YAML)"
+experimental:
+ otlpLogs: true
+```
+
+```toml tab="File (TOML)"
+[experimental]
+ otlpLogs = true
+```
+
+```sh tab="CLI"
+--experimental.otlpLogs=true
+```
+
+!!! warning
+ This is an experimental feature.
+
+#### Configuration Example
+
+```yaml tab="File (YAML)"
+experimental:
+ otlpLogs: true
+
+accesslog:
+ otlp:
+ http:
+ endpoint: https://collector:4318/v1/logs
+ headers:
+ Authorization: Bearer auth_asKXRhIMplM7El1JENjrotGouS1LYRdL
+```
+
+```toml tab="File (TOML)"
+[experimental]
+ otlpLogs = true
+
+[accesslog.otlp]
+ http.endpoint = "https://collector:4318/v1/logs"
+ http.headers.Authorization = "Bearer auth_asKXRhIMplM7El1JENjrotGouS1LYRdL"
+```
+
+```yaml tab="CLI"
+--experimental.otlpLogs=true
+--accesslog.otlp.http.endpoint=https://collector:4318/v1/logs
+--accesslog.otlp.http.headers.Authorization=Bearer auth_asKXRhIMplM7El1JENjrotGouS1LYRdL
+```
+
+#### Configuration Options
+
+| Field | Description | Default | Required |
+|:-----------|:--------------------------|:--------|:---------|
+| `accesslog.otlp.http` | This instructs the exporter to send access logs to the OpenTelemetry Collector using HTTP.| | No |
+| `accesslog.otlp.http.endpoint` | The endpoint of the OpenTelemetry Collector. (format=`://:`) | `https://localhost:4318/v1/logs` | No |
+| `accesslog.otlp.http.headers` | Additional headers sent with access logs by the exporter to the OpenTelemetry Collector. | [ ] | No |
+| `accesslog.otlp.http.tls` | Defines the Client TLS configuration used by the exporter to send access logs to the OpenTelemetry Collector. | | No |
+| `accesslog.otlp.http.tls.ca` | The path to the certificate authority used for the secure connection to the OpenTelemetry Collector, it defaults to the system bundle. | | No |
+| `accesslog.otlp.http.tls.cert` | The path to the certificate to use for the OpenTelemetry Collector. | | No |
+| `accesslog.otlp.http.tls.key` | The path to the key to use for the OpenTelemetry Collector. | | No |
+| `accesslog.otlp.http.tls.insecureSkipVerify` | Instructs the OpenTelemetry Collector to accept any certificate presented by the server regardless of the hostname in the certificate. | false | No |
+| `accesslog.otlp.grpc` | This instructs the exporter to send access logs to the OpenTelemetry Collector using gRPC.| | No |
+| `accesslog.otlp.grpc.endpoint` | The endpoint of the OpenTelemetry Collector. (format=`:`) | `localhost:4317` | No |
+| `accesslog.otlp.grpc.headers` | Additional headers sent with access logs by the exporter to the OpenTelemetry Collector. | [ ] | No |
+| `accesslog.otlp.grpc.insecure` | Instructs the exporter to send access logs to the OpenTelemetry Collector using an insecure protocol. | false | No |
+| `accesslog.otlp.grpc.tls` | Defines the Client TLS configuration used by the exporter to send access logs to the OpenTelemetry Collector. | | No |
+| `accesslog.otlp.grpc.tls.ca` | The path to the certificate authority used for the secure connection to the OpenTelemetry Collector, it defaults to the system bundle. | | No |
+| `accesslog.otlp.grpc.tls.cert` | The path to the certificate to use for the OpenTelemetry Collector. | | No |
+| `accesslog.otlp.grpc.tls.key` | The path to the key to use for the OpenTelemetry Collector. | | No |
+| `accesslog.otlp.grpc.tls.insecureSkipVerify` | Instructs the OpenTelemetry Collector to accept any certificate presented by the server regardless of the hostname in the certificate. | false | No |
+
+### CLF format fields
Below the fields displayed with the CLF format:
@@ -140,7 +287,7 @@ Below the fields displayed with the CLF format:
"" "" ms
```
-#### Available Fields
+### Available Fields
| Field | Description |
|-------------------------|------------------|
@@ -179,7 +326,7 @@ Below the fields displayed with the CLF format:
| `TLSCipher` | The TLS cipher used by the connection (e.g. `TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA`) (if connection is TLS). |
| `TLSClientSubject` | The string representation of the TLS client certificate's Subject (e.g. `CN=username,O=organization`). |
-#### Log Rotation
+### Log Rotation
Traefik close and reopen its log files, assuming they're configured, on receipt of a USR1 signal.
This allows the logs to be rotated and processed by an external program, such as `logrotate`.
@@ -187,7 +334,7 @@ This allows the logs to be rotated and processed by an external program, such as
!!! warning
This does not work on Windows due to the lack of USR signals.
-#### Time Zones
+### Time Zones
Traefik will timestamp each log line in UTC time by default.
diff --git a/docs/content/reference/install-configuration/providers/swarm.md b/docs/content/reference/install-configuration/providers/swarm.md
index 92ac6a562..64d058a87 100644
--- a/docs/content/reference/install-configuration/providers/swarm.md
+++ b/docs/content/reference/install-configuration/providers/swarm.md
@@ -43,25 +43,25 @@ services:
## Configuration Options
-| Field | Description | Default | Required |
-|:------|:----------------------------------------------------------|:---------------------|:---------|
-| `providers.providersThrottleDuration` | Minimum amount of time to wait for, after a configuration reload, before taking into account any new configuration refresh event.
If multiple events occur within this time, only the most recent one is taken into account, and all others are discarded.
**This option cannot be set per provider, but the throttling algorithm applies to each of them independently.** | 2s | No |
-| `providers.swarm.endpoint` | Specifies the Docker API endpoint. See [here](#endpoint) for more information| `unix:///var/run/docker.sock` | Yes |
-| `providers.swarm.username` | Defines the username for Basic HTTP authentication. This should be used when the Docker daemon socket is exposed through an HTTP proxy that requires Basic HTTP authentication.| "" | No |
-| `providers.swarm.password` | Defines the password for Basic HTTP authentication. This should be used when the Docker daemon socket is exposed through an HTTP proxy that requires Basic HTTP authentication.| "" | No |
-| `providers.swarm.useBindPortIP` | Instructs Traefik to use the IP/Port attached to the container's binding instead of its inner network IP/Port. See [here](#usebindportip) for more information | false | No |
-| `providers.swarm.exposedByDefault` | Expose containers by default through Traefik. See [here](./overview.md#restrict-the-scope-of-service-discovery) for additional information | true | No |
-| `providers.swarm.network` | Defines a default docker network to use for connections to all containers. This option can be overridden on a per-container basis with the `traefik.docker.network` label.| "" | No |
-| `providers.swarm.defaultRule` | Defines what routing rule to apply to a container if no rule is defined by a label. See [here](#defaultrule) for more information | ```"Host(`{{ normalize .Name }}`)"``` | No |
-| `providers.swarm.refreshSeconds` | Defines the polling interval for Swarm Mode. | "15s" | No |
-| `providers.swarm.httpClientTimeout` | Defines the client timeout (in seconds) for HTTP connections. If its value is 0, no timeout is set. | 0 | No |
-| `providers.swarm.watch` | Instructs Traefik to watch Docker events or not. | True | No |
-| `providers.swarm.constraints` | Defines an expression that Traefik matches against the container labels to determine whether to create any route for that container. See [here](#constraints) for more information. | "" | No |
-| `providers.swarm.allowEmptyServices` | Instructs the provider to create any [servers load balancer](../../../routing/services/index.md#servers-load-balancer) defined for Docker containers regardless of the [healthiness](https://docs.docker.com/engine/reference/builder/#healthcheck) of the corresponding containers. | false | No |
-| `providers.swarm.tls.ca` | Defines the path to the certificate authority used for the secure connection to Docker, it defaults to the system bundle. | "" | No |
-| `providers.swarm.tls.cert` | Defines the path to the public certificate used for the secure connection to Docker. When using this option, setting the `key` option is required. | "" | Yes |
-| `providers.swarm.tls.key` | Defines the path to the private key used for the secure connection to Docker. When using this option, setting the `cert` option is required. | "" | Yes |
-| `providers.swarm.tls.insecureSkipVerify` | Instructs the provider to accept any certificate presented by the Docker server when establishing a TLS connection, regardless of the hostnames the certificate covers. | false | No |
+| Field | Description | Default | Required |
+|:-----------------------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------------------------------------|:---------|
+| `providers.providersThrottleDuration` | Minimum amount of time to wait for, after a configuration reload, before taking into account any new configuration refresh event.
If multiple events occur within this time, only the most recent one is taken into account, and all others are discarded.
**This option cannot be set per provider, but the throttling algorithm applies to each of them independently.** | 2s | No |
+| `providers.swarm.endpoint` | Specifies the Docker API endpoint. See [here](#endpoint) for more information | `unix:///var/run/docker.sock` | Yes |
+| `providers.swarm.username` | Defines the username for Basic HTTP authentication. This should be used when the Docker daemon socket is exposed through an HTTP proxy that requires Basic HTTP authentication. | "" | No |
+| `providers.swarm.password` | Defines the password for Basic HTTP authentication. This should be used when the Docker daemon socket is exposed through an HTTP proxy that requires Basic HTTP authentication. | "" | No |
+| `providers.swarm.useBindPortIP` | Instructs Traefik to use the IP/Port attached to the container's binding instead of its inner network IP/Port. See [here](#usebindportip) for more information | false | No |
+| `providers.swarm.exposedByDefault` | Expose containers by default through Traefik. See [here](./overview.md#restrict-the-scope-of-service-discovery) for additional information | true | No |
+| `providers.swarm.network` | Defines a default docker network to use for connections to all containers. This option can be overridden on a per-container basis with the `traefik.swarm.network` label. | "" | No |
+| `providers.swarm.defaultRule` | Defines what routing rule to apply to a container if no rule is defined by a label. See [here](#defaultrule) for more information | ```"Host(`{{ normalize .Name }}`)"``` | No |
+| `providers.swarm.refreshSeconds` | Defines the polling interval for Swarm Mode. | "15s" | No |
+| `providers.swarm.httpClientTimeout` | Defines the client timeout (in seconds) for HTTP connections. If its value is 0, no timeout is set. | 0 | No |
+| `providers.swarm.watch` | Instructs Traefik to watch Docker events or not. | True | No |
+| `providers.swarm.constraints` | Defines an expression that Traefik matches against the container labels to determine whether to create any route for that container. See [here](#constraints) for more information. | "" | No |
+| `providers.swarm.allowEmptyServices` | Instructs the provider to create any [servers load balancer](../../../routing/services/index.md#servers-load-balancer) defined for Docker containers regardless of the [healthiness](https://docs.docker.com/engine/reference/builder/#healthcheck) of the corresponding containers. | false | No |
+| `providers.swarm.tls.ca` | Defines the path to the certificate authority used for the secure connection to Docker, it defaults to the system bundle. | "" | No |
+| `providers.swarm.tls.cert` | Defines the path to the public certificate used for the secure connection to Docker. When using this option, setting the `key` option is required. | "" | Yes |
+| `providers.swarm.tls.key` | Defines the path to the private key used for the secure connection to Docker. When using this option, setting the `cert` option is required. | "" | Yes |
+| `providers.swarm.tls.insecureSkipVerify` | Instructs the provider to accept any certificate presented by the Docker server when establishing a TLS connection, regardless of the hostnames the certificate covers. | false | No |
### `endpoint`
diff --git a/docs/content/setup/docker.md b/docs/content/setup/docker.md
new file mode 100644
index 000000000..beaf29f9d
--- /dev/null
+++ b/docs/content/setup/docker.md
@@ -0,0 +1,299 @@
+---
+title: Setup Traefik Proxy in Docker Standalone
+description: "Learn how to Setup Traefik on Docker with HTTP/HTTPS entrypoints, redirects, secure dashboard, basic TLS, metrics, tracing, access‑logs."
+---
+
+This guide provides an in-depth walkthrough for installing and configuring Traefik Proxy within a Docker container using the official Traefik Docker image & Docker Compose. In this guide, we'll cover the following:
+
+- Enable the [Docker provider](../reference/install-configuration/providers/docker.md)
+- Expose **web** (HTTP :80) and **websecure** (HTTPS :443) entrypoints
+- Redirect all HTTP traffic to HTTPS
+- Secure the Traefik dashboard with **basic‑auth**
+- Terminate TLS with a self‑signed certificate for `*.docker.localhost`
+- Deploy the **whoami** demo service
+- Enable access‑logs and Prometheus metrics
+
+## Prerequisites
+
+- Docker Desktop / Engine
+- Docker Compose
+- `openssl`
+- `htpasswd` from `apache2-utils`
+
+## Create a self‑signed certificate
+
+Before Traefik can serve HTTPS locally it needs a certificate. In production you’d use one from a trusted CA, but for a single‑machine stack a quick self‑signed cert is enough. We can create one with openssl by running the following commands:
+
+```bash
+mkdir -p certs
+openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
+ -keyout certs/local.key -out certs/local.crt \
+ -subj "/CN=*.docker.localhost"
+```
+
+The `certs` folder now holds `local.crt` and `local.key`, which will be mounted read‑only into Traefik.
+
+## Create the Traefik Dashboard Credentials
+
+In production, it is advisable to have some form of authentication/security for the Traefik dashboard. Traefik can be secured with the [basic‑auth middleware](../reference/routing-configuration/http/middlewares/basicauth.md). To do this, generate a hashed username / password pair that Traefik’s middleware will validate:
+
+```bash
+htpasswd -nb admin "P@ssw0rd" | sed -e 's/\$/\$\$/g'
+```
+
+Copy the full output (e.g., admin:$$apr1$$…) — we'll need this for the middleware configuration.
+
+## Create a docker-compose.yaml
+
+Now define the whole stack in a Compose file. This file declares Traefik, mounts the certificate, sets up a dedicated network, and later hosts the whoami demo service.
+
+!!! note
+ You can also choose to use the Docker CLI and a configuration file to run Traefik, but for this tutorial, we'll be using Docker Compose.
+
+First, create a folder named `dynamic` and create a file named `tls.yaml` for dynamic configuration. Paste the TLS certificate configuration into the file:
+
+```yaml
+tls:
+ certificates:
+ - certFile: /certs/local.crt
+ keyFile: /certs/local.key
+```
+
+In the same folder as the `dynamic/tls.yaml` file, create a `docker-compose.yaml` file and include the following:
+
+```yaml
+services:
+ traefik:
+ image: traefik:v3.4
+ container_name: traefik
+ restart: unless-stopped
+ security_opt:
+ - no-new-privileges:true
+
+ networks:
+ # Connect to the 'traefik_proxy' overlay network for inter-container communication across nodes
+ - proxy
+
+ ports:
+ - "80:80"
+ - "443:443"
+ - "8080:8080"
+
+ volumes:
+ - /var/run/docker.sock:/var/run/docker.sock:ro
+ - ./certs:/certs:ro
+ - ./dynamic:/dynamic:ro
+
+ command:
+ # EntryPoints
+ - "--entrypoints.web.address=:80"
+ - "--entrypoints.web.http.redirections.entrypoint.to=websecure"
+ - "--entrypoints.web.http.redirections.entrypoint.scheme=https"
+ - "--entrypoints.web.http.redirections.entrypoint.permanent=true"
+ - "--entrypoints.websecure.address=:443"
+ - "--entrypoints.websecure.http.tls=true"
+
+ # Attach the static configuration tls.yaml file that contains the tls configuration settings
+ - "--providers.file.filename=/dynamic/tls.yaml"
+
+ # Providers
+ - "--providers.docker=true"
+ - "--providers.docker.exposedbydefault=false"
+ - "--providers.docker.network=proxy"
+
+ # API & Dashboard
+ - "--api.dashboard=true"
+ - "--api.insecure=false"
+
+ # Observability
+ - "--log.level=INFO"
+ - "--accesslog=true"
+ - "--metrics.prometheus=true"
+
+ # Traefik Dynamic configuration via Docker labels
+ labels:
+ # Enable self‑routing
+ - "traefik.enable=true"
+
+ # Dashboard router
+ - "traefik.http.routers.dashboard.rule=Host(`dashboard.docker.localhost`)"
+ - "traefik.http.routers.dashboard.entrypoints=websecure"
+ - "traefik.http.routers.dashboard.service=api@internal"
+ - "traefik.http.routers.dashboard.tls=true"
+
+ # Basic‑auth middleware
+ - "traefik.http.middlewares.dashboard-auth.basicauth.users="
+ - "traefik.http.routers.dashboard.middlewares=dashboard-auth@docker"
+
+# Whoami application
+ whoami:
+ image: traefik/whoami
+ container_name: whoami
+ restart: unless-stopped
+ networks:
+ - proxy
+ labels:
+ - "traefik.enable=true"
+ - "traefik.http.routers.whoami.rule=Host(`whoami.docker.localhost`)"
+ - "traefik.http.routers.whoami.entrypoints=websecure"
+ - "traefik.http.routers.whoami.tls=true"
+
+networks:
+ proxy:
+ name: proxy
+```
+
+!!! info
+ - Remember to replace `` with the hash generated earlier.
+ - The `--api.insecure=false` flag is used to secure the API and prevent the dashboard from being exposed on port 8080. This is done because we are exposing the dashboard with a HTTPS router.
+
+## Launch the stack
+
+With the Compose file and supporting assets in place, start the containers and let Docker wire up networking behind the scenes:
+
+```bash
+docker compose up -d
+```
+
+Traefik will start, read its static configuration from the `command` arguments, connect to the Docker socket, detect its own labels for dynamic configuration (dashboard routing and auth), and begin listening on ports 80 and 443. HTTP requests will be redirected to HTTPS.
+
+## Access the Dashboard
+
+Now that Traefik is deployed, you can access the dashboard at [https://dashboard.docker.localhost](https://dashboard.docker.localhost) and it should prompt for the Basic Authentication credentials you configured:
+
+
+
+## Test the whoami Application
+
+You can test the application using curl:
+
+```bash
+curl -k https://whoami.docker.localhost/
+```
+
+```bash
+Hostname: whoami-76c9859cfc-k7jzs
+IP: 127.0.0.1
+IP: ::1
+IP: 10.42.0.59
+IP: fe80::50d7:a2ff:fed5:2530
+RemoteAddr: 10.42.0.60:54148
+GET / HTTP/1.1
+Host: whoami.docker.localhost
+User-Agent: curl/8.7.1
+Accept: */*
+Accept-Encoding: gzip
+X-Forwarded-For: 10.42.0.1
+X-Forwarded-Host: whoami.docker.localhost
+X-Forwarded-Port: 443
+X-Forwarded-Proto: https
+X-Forwarded-Server: traefik-644b7c67d9-f2tn9
+X-Real-Ip: 10.42.0.1
+```
+
+Making the same request to the HTTP entrypoint will return the following:
+
+```bash
+curl -k http://whoami.docker.localhost
+
+Moved Permanently
+```
+
+The above confirms that a redirection has taken place which means our setup works correctly.
+
+You can also open a browser and navigate to [https://whoami.docker.localhost](https://whoami.docker.localhost) to see a JSON dump from the service:
+
+
+
+!!! info
+ You can also navigate to the Traefik Dashboard at [https://dashboard.docker.localhost](https://dashboard.docker.localhost) to see that the route has been created.
+
+### Other Key Configuration Areas
+
+Beyond this initial setup, Traefik offers extensive configuration possibilities. Here are brief introductions and minimal examples using Docker Compose `command` arguments or `labels`. Consult the main documentation linked for comprehensive details.
+
+### TLS Certificate Management (Let's Encrypt)
+
+To make the `websecure` entry point serve valid HTTPS certificates automatically, enable Let's Encrypt (ACME).
+
+*Example `command` additions:*
+
+```yaml
+command:
+ # ... other command arguments ...
+ - "--certificatesresolvers.le.acme.email=your-email@example.com"
+ - "--certificatesresolvers.le.acme.storage=/letsencrypt/acme.json" # Path inside container volume
+ - "--certificatesresolvers.le.acme.httpchallenge.entrypoint=web"
+
+ # - "--certificatesresolvers.le.acme.dnschallenge.provider=your-dns-provider" # Needs provider setup
+
+ # Optionally make 'le' the default resolver for TLS-enabled routers
+ - "--entrypoints.websecure.http.tls.certresolver=le"
+```
+
+This defines a resolver named `le`, sets the required email and storage path (within the mounted `/letsencrypt` volume), and enables the HTTP challenge. Refer to the [HTTPS/TLS Documentation](../reference/install-configuration/tls/certificate-resolvers/overview.md) and [Let's Encrypt Documentation](../reference/install-configuration/tls/certificate-resolvers/acme.md) for details on challenges and DNS provider configuration.
+
+### Metrics (Prometheus)
+
+You can expose Traefik's internal metrics for monitoring with Prometheus. We already enabled prometheus in our setup but we can further configure it.
+*Example `command` additions:*
+
+```yaml
+command:
+ # If using a dedicated metrics entry point, define it:
+ - "--entrypoints.metrics.address=:8082"
+
+ # ... other command arguments ...
+ - "--metrics.prometheus=true"
+
+ # Optionally change the entry point metrics are exposed on (defaults to 'traefik')
+ - "--metrics.prometheus.entrypoint=metrics"
+
+ # Add labels to metrics for routers/services (can increase cardinality)
+ - "--metrics.prometheus.addrouterslabels=true"
+ - "--metrics.prometheus.addserviceslabels=true"
+```
+
+This enables the `/metrics` endpoint (typically accessed via the internal API port, often 8080 by default if not secured, or via a dedicated entry point). See the [Metrics Documentation](../reference/install-configuration/observability/metrics.md) for options.
+
+### Tracing (OTel):
+
+You can enable distributed tracing to follow requests through Traefik.
+*Example `command` additions:*
+
+```yaml
+command:
+ # ... other command arguments ...
+ - "--tracing.otel=true"
+ - "--tracing.otel.grpcendpoint=otel-collector:4317" # Adjust endpoint as needed
+ - "--tracing.otel.httpendpoint=otel-collector.observability:4318" # Adjust endpoint as needed
+```
+
+!!! note
+ This option requires a running OTEL collector accessible by Traefik. Consult the [Tracing Documentation](../reference/install-configuration/observability/tracing.md).
+
+### Access Logs
+
+You can configure Traefik to log incoming requests for debugging and analysis.
+*Example `command` additions:*
+
+```yaml
+command:
+ # ... other command arguments ...
+ - "--accesslog=true" # Enable access logs to stdout
+
+ # Optionally change format or output file (requires volume)
+ - "--accesslog.format=json"
+ - "--accesslog.filepath=/path/to/access.log"
+
+ # Optionally filter logs
+ - "--accesslog.filters.statuscodes=400-599"
+```
+
+This enables access logs to the container's standard output (viewable via `docker compose logs `). See the [Access Logs Documentation](../reference/install-configuration/observability/logs-and-accesslogs.md).
+
+### Conclusion
+
+You now have a basic Traefik setup in Docker with secure dashboard access and HTTP-to-HTTPS redirection.
+
+{!traefik-for-business-applications.md!}
diff --git a/docs/content/setup/kubernetes.md b/docs/content/setup/kubernetes.md
new file mode 100644
index 000000000..eda680271
--- /dev/null
+++ b/docs/content/setup/kubernetes.md
@@ -0,0 +1,398 @@
+---
+title: "Setup Traefik on Kubernetes"
+description: "Learn how to Setup Traefik on Kubernetes with HTTP/HTTPS entrypoints, redirects, secure dashboard, basic TLS, metrics, tracing, access‑logs."
+---
+
+This guide provides an in-depth walkthrough for installing and configuring Traefik Proxy within a Kubernetes cluster using the official Helm chart. In this guide, we'll cover the following:
+
+- Configure standard HTTP (`web`) and HTTPS (`websecure`) entry points,
+- Implement automatic redirection from HTTP to HTTPS
+- Secure the Traefik Dashboard using Basic Authentication.
+- Deploy a demo application to test the setup
+- Explore some other key configuration options
+
+## Prerequisites
+
+- A Kubernetes cluster
+- Helm v3,
+- Kubectl
+
+## Create the Cluster
+
+If you do not have a Kubernetes cluster already, you can spin up one with K3d:
+
+```bash
+k3d cluster create traefik \
+ --port 80:80@loadbalancer \
+ --port 443:443@loadbalancer \
+ --port 8000:8000@loadbalancer \
+ --k3s-arg "--disable=traefik@server:0"
+```
+
+Ports `80` and `443` reach Traefik from the host, while port `8000` remains free for later demos. The built-in Traefik shipped with k3s is disabled to avoid conflicts.
+
+Check the context:
+
+```bash
+kubectl cluster-info --context k3d-traefik
+```
+
+You should see something like this:
+
+```bash
+Kubernetes control plane is running at https://0.0.0.0:56049
+CoreDNS is running at https://0.0.0.0:56049/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
+Metrics-server is running at https://0.0.0.0:56049/api/v1/namespaces/kube-system/services/https:metrics-server:https/proxy
+
+To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
+```
+
+## Add the chart repo and namespace
+
+Using Helm streamlines Kubernetes application deployment. Helm packages applications into "charts," which are collections of template files describing Kubernetes resources. We use the official Traefik Helm chart for a managed and customizable installation.
+
+```bash
+helm repo add traefik https://traefik.github.io/charts
+helm repo update
+kubectl create namespace traefik
+```
+
+The first command registers the `traefik` repository alias pointing to the official chart location. The second command refreshes your local cache to ensure you have the latest list of charts and versions available from all configured repositories.
+
+## Create a Local Self‑Signed TLS Secret
+
+Traefik's Gateway listeners require a certificate whenever a listener uses `protocol: HTTPS`.
+
+For local development create a throw‑away self‑signed certificate and
+store it in a Kubernetes Secret named **local‑selfsigned‑tls**.
+The Gateway references this secret to terminate TLS on the `websecure` listener.
+
+```bash
+# 1) Generate a self‑signed certificate valid for *.docker.localhost
+openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
+ -keyout tls.key -out tls.crt \
+ -subj "/CN=*.docker.localhost"
+
+# 2) Create the TLS secret in the traefik namespace
+kubectl create secret tls local-selfsigned-tls \
+ --cert=tls.crt --key=tls.key \
+ --namespace traefik
+```
+
+### Why Do We Need To Do This
+
+The Gateway's HTTPS listener references this secret via `certificateRefs`.
+Without it, the helm chart validation fails and the HTTP→HTTPS redirect chain breaks.
+
+!!! info "Production tip"
+ The self-signed certificate above is **only for local development**. For production, either store a certificate issued by your organization's CA in a Secret or let an automated issuer such as cert-manager or Traefik's ACME (Let's Encrypt) generate certificates on demand. Update the `certificateRefs` in the `websecure` listener—or use `traefik.io/tls.certresolver`—so clients receive a trusted certificate and no longer see browser warnings.
+
+## Prepare Helm Chart Configuration Values
+
+Create a `values.yaml` file with the following content:
+
+```yaml
+
+# Configure Network Ports and EntryPoints
+# EntryPoints are the network listeners for incoming traffic.
+ports:
+ # Defines the HTTP entry point named 'web'
+ web:
+ port: 80
+ nodePort: 30000
+ # Instructs this entry point to redirect all traffic to the 'websecure' entry point
+ redirections:
+ entryPoint:
+ to: websecure
+ scheme: https
+ permanent: true
+
+ # Defines the HTTPS entry point named 'websecure'
+ websecure:
+ port: 443
+ nodePort: 30001
+
+# Enables the dashboard in Secure Mode
+api:
+ dashboard: true
+ insecure: false
+
+ingressRoute:
+ dashboard:
+ enabled: true
+ matchRule: Host(`dashboard.docker.localhost`)
+ entryPoints:
+ - websecure
+ middlewares:
+ - name: dashboard-auth
+
+# Creates a BasiAuth Middleware and Secret for the Dashboard Security
+extraObjects:
+ - apiVersion: v1
+ kind: Secret
+ metadata:
+ name: dashboard-auth-secret
+ type: kubernetes.io/basic-auth
+ stringData:
+ username: admin
+ password: "P@ssw0rd" # Replace with an Actual Password
+ - apiVersion: traefik.io/v1alpha1
+ kind: Middleware
+ metadata:
+ name: dashboard-auth
+ spec:
+ basicAuth:
+ secret: dashboard-auth-secret
+
+# We will route with Gateway API instead.
+ingressClass:
+ enabled: false
+
+# Enable Gateway API Provider & Disables the KubernetesIngress provider
+# Providers tell Traefik where to find routing configuration.
+providers:
+ kubernetesIngress:
+ enabled: false
+ kubernetesGateway:
+ enabled: true
+
+## Gateway Listeners
+gateway:
+ listeners:
+ web: # HTTP listener that matches entryPoint `web`
+ port: 80
+ protocol: HTTP
+ namespacePolicy: All
+
+ websecure: # HTTPS listener that matches entryPoint `websecure`
+ port: 443
+ protocol: HTTPS # TLS terminates inside Traefik
+ namespacePolicy: All
+ mode: Terminate
+ certificateRefs:
+ - kind: Secret
+ name: local-selfsigned-tls # the Secret we created before the installation
+ group: ""
+
+# Enable Observability
+logs:
+ general:
+ level: INFO
+ # This enables access logs, outputting them to Traefik's standard output by default. The [Access Logs Documentation](https://doc.traefik.io/traefik/observability/access-logs/) covers formatting, filtering, and output options.
+ access:
+ enabled: true
+
+# Enables Prometheus for Metrics
+metrics:
+ prometheus:
+ enabled: true
+```
+
+## Install the Traefik Using the Helm Values
+
+Now, apply the configuration using the Helm client.
+
+```bash
+# Install the chart into the 'traefik' namespace
+helm install traefik traefik/traefik \
+ --namespace traefik \
+ --values values.yaml
+```
+
+**Command Breakdown:**
+
+- `helm install traefik`: Instructs Helm to install a new release named `traefik`.
+- `traefik/traefik`: Specifies the chart to use (`traefik` chart from the `traefik` repository added earlier).
+- `--namespace traefik`: Specifies the Kubernetes namespace to install into. Using a dedicated namespace is recommended practice.
+- `--values values.yaml`: Applies the custom configuration from your `values.yaml` file.
+
+## Accessing the Dashboard
+
+Now that Traefik is deployed, you can access its dashboard at [https://dashboard.docker.localhost/](https://dashboard.docker.localhost/). When you access this link, your browser will prompt for the username and password. Ensure you use the credentials set in the `values.yaml` file to log in. Upon successful login, the dashboard will be displayed as shown below:
+
+
+
+## Deploy a Demo Application
+
+To test the setup, deploy the [Traefik whoami](https://github.com/traefik/whoami) application in the Kubernetes cluster. Create a file named `whoami.yaml` and paste the following:
+
+```yaml
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: whoami
+ namespace: traefik
+spec:
+ replicas: 2
+ selector:
+ matchLabels:
+ app: whoami
+ template:
+ metadata:
+ labels:
+ app: whoami
+ spec:
+ containers:
+ - name: whoami
+ image: traefik/whoami
+ ports:
+ - containerPort: 80
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: whoami
+ namespace: traefik
+spec:
+ selector:
+ app: whoami
+ ports:
+ - port: 80
+```
+
+Apply the manifest:
+
+```bash
+kubectl apply -f whoami.yaml
+```
+
+After deploying the application, you can expose the application by creating a [Gateway API HTTPRoute](https://gateway-api.sigs.k8s.io/api-types/httproute/). To do this, create a file named `whoami-route.yaml` and paste the following:
+
+```yaml
+apiVersion: gateway.networking.k8s.io/v1
+kind: HTTPRoute
+metadata:
+ name: whoami
+ namespace: traefik
+spec:
+ parentRefs:
+ - name: traefik-gateway # Name of the Gateway that Traefik creates when you enable the Gateway API provider
+ hostnames:
+ - "whoami.docker.localhost"
+ rules:
+ - matches:
+ - path:
+ type: PathPrefix
+ value: /
+ backendRefs:
+ - name: whoami
+ port: 80
+```
+
+Apply the manifest:
+
+```bash
+kubectl apply -f whoami-route.yaml
+```
+
+After you apply the manifest, navigate to the Routes in the Traefik Dashboard; you’ll see that the [https://whoami.docker.localhost](https://whoami.docker.localhost) route has been created.
+
+
+
+You can test the application using curl:
+
+```bash
+curl -k https://whoami.docker.localhost/
+```
+
+```bash
+Hostname: whoami-76c9859cfc-k7jzs
+IP: 127.0.0.1
+IP: ::1
+IP: 10.42.0.59
+IP: fe80::50d7:a2ff:fed5:2530
+RemoteAddr: 10.42.0.60:54148
+GET / HTTP/1.1
+Host: whoami.docker.localhost
+User-Agent: curl/8.7.1
+Accept: */*
+Accept-Encoding: gzip
+X-Forwarded-For: 10.42.0.1
+X-Forwarded-Host: whoami.docker.localhost
+X-Forwarded-Port: 443
+X-Forwarded-Proto: https
+X-Forwarded-Server: traefik-644b7c67d9-f2tn9
+X-Real-Ip: 10.42.0.1
+```
+
+You can also open a browser and navigate to [https://whoami.docker.localhost](https://whoami.docker.localhost) to see a JSON dump from the service.
+
+
+
+## Other Key Configuration Areas
+
+The above setup provides a secure base, but Traefik offers much more. Here's a brief overview of other essential configurations, with minimal examples using Helm `values.yaml` overrides.
+
+These examples illustrate how to enable features; consult the main documentation for detailed options.
+
+### TLS Certificate Management (Let's Encrypt)
+
+On the `websecure` entry point TLS is enabled by default. However, it currently lacks a valid certificate. Traefik can automatically obtain and renew TLS certificates from Let's Encrypt using the ACME protocol.
+
+*Example `values.yaml` addition:*
+
+```yaml
+additionalArguments:
+ - "--certificatesresolvers.le.acme.email=your-email@example.com"
+ - "--certificatesresolvers.le.acme.storage=/data/acme.json"
+ - "--certificatesresolvers.le.acme.httpchallenge.entrypoint=web"
+ # - "--certificatesresolvers.le.acme.dnschallenge.provider=your-dns-provider" # Requires provider-specific config, adjust if you control your DNS provider
+
+# Enable persistence for ACME data (certificates, account) to ensure it survives pod restarts:
+persistence:
+ enabled: true
+ name: data
+ size: 1Gi
+ storageClass: ""
+```
+
+This enables a certificate resolver named `le`, configures the mandatory email and storage file, and sets up the HTTP challenge (requires port 80 access). Refer to the [HTTPS/TLS Documentation](../reference/install-configuration/tls/certificate-resolvers/overview.md) and [Let's Encrypt Documentation](../reference/install-configuration/tls/certificate-resolvers/acme.md) for full details, including DNS challenge configuration.
+
+!!!info "Let's Encrypt in Production"
+ Let's Encrypt can only issue certificates for hostnames that point to a public IP address reachable on ports 80 (HTTP‑01) or via your DNS provider's API (DNS‑01). Replace the `*.docker.localhost` examples with a real domain you control, create the DNS records, and keep ports 80/443 open to your cluster so the validation can succeed.
+
+### Gateway API & ACME
+
+Traefik’s built‑in ACME/Let’s Encrypt integration works for IngressRoute and Ingress resources, but it does not issue certificates for Gateway API listeners.
+If you’re using the Gateway API, install [cert‑manager](https://cert-manager.io/docs/) (or another certificate controller) and reference the secret it creates in `gateway.listeners.websecure.certificateRefs`.
+
+### Metrics (Prometheus)
+
+Traefik can expose detailed metrics in Prometheus format, essential for monitoring its performance and the traffic it handles.
+
+*Example `values.yaml` addition:*
+
+```yaml
+# Enable metrics endpoint
+metrics:
+ prometheus:
+ # The entry point metrics will be available on (usually internal/admin)
+ entryPoint: metrics
+ # Add standard Prometheus metrics
+ addRoutersLabels: true
+ addServicesLabels: true
+ # ... other options available
+```
+
+This enables the Prometheus endpoint on a dedicated `metrics` entry point (port 9100). See the [Metrics Documentation](../reference/install-configuration/observability/metrics.md) for configuration details and available metrics.
+
+### Tracing (OTel)
+
+Distributed tracing helps understand request latency and flow through your system, including Traefik itself.
+
+*Example `values.yaml` addition:*
+
+```yaml
+additionalArguments:
+ - "--tracing.otel=true"
+ - "--tracing.otel.grpcendpoint=otel-collector.observability:4317" # Adjust endpoint as needed
+ - "--tracing.otel.httpendpoint=otel-collector.observability:4318" # Adjust endpoint as needed
+```
+
+This enables OTel tracing and specifies the collector endpoint. Consult the [Tracing Documentation](../reference/install-configuration/observability/tracing.md) for details on OTel tracing.
+
+## Conclusion
+
+This setup establishes Traefik with secure dashboard access and HTTPS redirection, along with pointers to enable observability & TLS.
+
+{!traefik-for-business-applications.md!}
diff --git a/docs/content/setup/swarm.md b/docs/content/setup/swarm.md
new file mode 100644
index 000000000..1b097831d
--- /dev/null
+++ b/docs/content/setup/swarm.md
@@ -0,0 +1,330 @@
+---
+title: Setup Traefik Proxy in Docker Swarm
+description: "Learn how to run Traefik v3 in Docker Swarm with HTTP/HTTPS entrypoints, redirects, a secured dashboard, self‑signed TLS, metrics, tracing, and access‑logs."
+---
+
+This guide provides an in‑depth walkthrough for installing and configuring Traefik Proxy as a **Swarm service** using `docker stack deploy`. It follows the same structure as the standalone‑Docker tutorial and covers:
+
+- Enable the [Swarm provider](../reference/install-configuration/providers/swarm.md)
+- Expose **web** (HTTP :80) and **websecure** (HTTPS :443) entrypoints
+- Redirect all HTTP traffic to HTTPS
+- Secure the Traefik dashboard with **basic‑auth**
+- Terminate TLS with a self‑signed certificate for `*.swarm.localhost`
+- Deploy the **whoami** demo service
+- Enable access‑logs and Prometheus metrics
+
+## Prerequisites
+
+- Docker Engine with **Swarm mode** initialised (`docker swarm init`)
+- Docker Compose
+- `openssl`
+- `htpasswd`
+
+## Create a self‑signed certificate
+
+Before Traefik can serve HTTPS locally it needs a certificate. In production you’d use one from a trusted CA, but for a multi‑node dev swarm a quick self‑signed cert is enough:
+
+```bash
+mkdir -p certs
+openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
+ -keyout certs/local.key -out certs/local.crt \
+ -subj "/CN=*.swarm.localhost"
+```
+
+## Create the Traefik Dashboard Credentials
+
+Generate a hashed username / password pair that Traefik’s middleware will validate:
+
+```bash
+htpasswd -nb admin "P@ssw0rd" | sed -e 's/\$/\$\$/g'
+```
+
+Copy the full output (e.g., `admin:$$apr1$$…`) — we’ll paste it into the middleware label.
+
+## Create a docker‑compose‑swarm.yaml
+
+!!! note
+ Swarm uses `docker stack deploy`. The compose file can be named anything; we’ll use `docker‑compose‑swarm.yaml`.
+
+First, create a folder named **dynamic** and add **tls.yaml** for dynamic TLS configuration:
+
+```yaml
+# dynamic/tls.yaml
+tls:
+ certificates:
+ - certFile: /certs/local.crt
+ keyFile: /certs/local.key
+```
+
+In the same directory, create `docker‑compose‑swarm.yaml`:
+
+```yaml
+services:
+ traefik:
+ image: traefik:v3.4
+
+ networks:
+ # Connect to the 'traefik_proxy' overlay network for inter-container communication across nodes
+ - traefik_proxy
+
+ ports:
+ # Expose Traefik's entry points to the Swarm
+ # Swarm requires the long syntax for ports.
+ - target: 80 # Container port (Traefik web entry point)
+ published: 80 # Host port exposed on the nodes
+ protocol: tcp
+ # 'host' mode binds directly to the node's IP where the task runs.
+ # 'ingress' mode uses Swarm's Routing Mesh (load balances across nodes).
+ # Choose based on your load balancing strategy. 'host' is often simpler if using an external LB.
+ mode: host
+ - target: 443 # Container port ( Traefik websecure entry point)
+ published: 443 # Host port
+ protocol: tcp
+ mode: host
+
+ volumes:
+ # Mount the Docker socket for the Swarm provider
+ # This MUST be run from a manager node to access the Swarm API via the socket.
+ - /var/run/docker.sock:/var/run/docker.sock:ro # Swarm API socket
+ - ./certs:/certs:ro
+ - ./dynamic:/dynamic:ro
+
+ # Traefik Static configuration via command-line arguments
+ command:
+ # HTTP EntryPoint
+ - "--entrypoints.web.address=:80"
+
+ # Configure HTTP to HTTPS Redirection
+ - "--entrypoints.web.http.redirections.entrypoint.to=websecure"
+ - "--entrypoints.web.http.redirections.entrypoint.scheme=https"
+ - "--entrypoints.web.http.redirections.entrypoint.permanent=true"
+
+ # HTTPS EntryPoint
+ - "--entrypoints.websecure.address=:443"
+ - "--entrypoints.websecure.http.tls=true"
+
+ # Attach dynamic TLS file
+ - "--providers.file.filename=/dynamic/tls.yaml"
+
+ # Providers
+
+ # Enable the Docker Swarm provider (instead of Docker provider)
+ - "--providers.swarm.endpoint=unix:///var/run/docker.sock"
+
+ # Watch for Swarm service changes (requires socket access)
+ - "--providers.swarm.watch=true"
+
+ # Recommended: Don't expose services by default; require explicit labels
+ - "--providers.swarm.exposedbydefault=false"
+
+ # Specify the default network for Traefik to connect to services
+ - "--providers.swarm.network=traefik_traefik_proxy"
+
+ # API & Dashboard
+ - "--api.dashboard=true" # Enable the dashboard
+ - "--api.insecure=false" # Explicitly disable insecure API mod
+
+ # Observability
+ - "--log.level=INFO" # Set the Log Level e.g INFO, DEBUG
+ - "--accesslog=true" # Enable Access Logs
+ - "--metrics.prometheus=true" # Enable Prometheus
+
+ deploy:
+ mode: replicated
+ replicas: 1
+ placement:
+
+ # Placement constraints restrict where Traefik tasks can run.
+ # Running on manager nodes is common for accessing the Swarm API via the socket.
+ constraints:
+ - node.role == manager
+
+ # Traefik Dynamic configuration via labels
+ # In Swarm, labels on the service definition configure Traefik routing for that service.
+ labels:
+ - "traefik.enable=true"
+
+ # Dashboard router
+ - "traefik.http.routers.dashboard.rule=Host(`dashboard.swarm.localhost`)"
+ - "traefik.http.routers.dashboard.entrypoints=websecure"
+ - "traefik.http.routers.dashboard.service=api@internal"
+ - "traefik.http.routers.dashboard.tls=true"
+
+ # Basic‑auth middleware
+ - "traefik.http.middlewares.dashboard-auth.basicauth.users="
+ - "traefik.http.routers.dashboard.middlewares=dashboard-auth@swarm"
+
+ # Service hint
+ - "traefik.http.services.traefik.loadbalancer.server.port=8080"
+
+ # Deploy the Whoami application
+ whoami:
+ image: traefik/whoami
+ networks:
+ - traefik_proxy
+ deploy:
+ labels:
+ # Enable Service discovery for Traefik
+ - "traefik.enable=true"
+ # Define the WHoami router rule
+ - "traefik.http.routers.whoami.rule=Host(`whoami.swarm.localhost`)"
+ # Expose Whoami on the HTTPS entrypoint
+ - "traefik.http.routers.whoami.entrypoints=websecure"
+ # Enable TLS
+ - "traefik.http.routers.whoami.tls=true"
+ # Expose the whoami port number to Traefik
+ - traefik.http.services.whoami.loadbalancer.server.port=80
+
+# Define the overlay network for Swarm
+networks:
+ traefik_proxy:
+ driver: overlay
+ attachable: true
+```
+
+!!! info
+ - Replace `` with the escaped hash from the previous step.
+ - The password hash is stored directly in a service label. This is fine for local development, but anyone with access to the Docker API can view it using `docker service inspect`. For production, use a more secure method to store secrets.
+
+## Launch the stack
+
+Create the overlay network once (if it doesn’t exist) and deploy:
+
+```bash
+docker network create --driver overlay --attachable traefik_proxy || true
+docker stack deploy -c docker-compose-swarm.yaml traefik
+```
+
+Swarm schedules the services on a manager node and binds ports 80/443.
+
+## Access the Dashboard
+
+Open **https://dashboard.swarm.localhost/** in your browser — the dashboard should prompt for the basic‑auth credentials you configured.
+
+
+
+## Test the whoami Application
+
+You can test the application using curl:
+
+```bash
+curl -k https://whoami.swarm.localhost/
+```
+
+```bash
+Hostname: whoami-76c9859cfc-k7jzs
+IP: 127.0.0.1
+IP: ::1
+IP: 10.42.0.59
+IP: fe80::50d7:a2ff:fed5:2530
+RemoteAddr: 10.42.0.60:54148
+GET / HTTP/1.1
+Host: whoami.swarm.localhost
+User-Agent: curl/8.7.1
+Accept: */*
+Accept-Encoding: gzip
+X-Forwarded-For: 10.42.0.1
+X-Forwarded-Host: whoami.swarm.localhost
+X-Forwarded-Port: 443
+X-Forwarded-Proto: https
+X-Forwarded-Server: traefik-644b7c67d9-f2tn9
+X-Real-Ip: 10.42.0.1
+```
+
+Making the same request to the HTTP entrypoint will return the following:
+
+```bash
+curl -k http://whoami.swarm.localhost
+
+Moved Permanently
+```
+
+Requesting the HTTP endpoint redirects to HTTPS, confirming the setup works.
+
+You can also open a browser and navigate to [https://whoami.swarm.localhost](https://whoami.swarm.localhost) to see a JSON dump from the service:
+
+
+
+### Other Key Configuration Areas
+
+Beyond this initial setup, Traefik offers extensive configuration possibilities. Here are brief introductions and minimal examples using Docker Compose `command` arguments or `labels`. Consult the main documentation linked for comprehensive details.
+
+#### TLS Certificate Management (Let’s Encrypt)
+
+To make the `websecure` entry point serve valid HTTPS certificates automatically, enable Let's Encrypt (ACME).
+
+```yaml
+command:
+ # ...
+ - "--certificatesresolvers.le.acme.email=you@example.com"
+ - "--certificatesresolvers.le.acme.storage=/letsencrypt/acme.json"
+ - "--certificatesresolvers.le.acme.httpchallenge.entrypoint=web"
+ - "--entrypoints.websecure.http.tls.certresolver=le"
+```
+
+This defines a resolver named `le`, sets the required email and storage path (within the mounted `/letsencrypt` volume), and enables the HTTP challenge. Refer to the [HTTPS/TLS Documentation](../reference/install-configuration/tls/certificate-resolvers/overview.md) and [Let's Encrypt Documentation](../reference/install-configuration/tls/certificate-resolvers/acme.md) for details on challenges and DNS provider configuration.
+
+!!! note
+
+ - Ensure the `/letsencrypt` path is on a **shared volume** or NFS so all nodes can read certificates.
+ - Ensure to mount the `/letsencrypt` volume in the `traefik` service in the `docker-compose-swarm.yaml` file.
+
+#### Metrics (Prometheus)
+
+You can expose Traefik's internal metrics for monitoring with Prometheus. We already enabled prometheus in our setup but we can further configure it.
+*Example `command` additions:*
+
+```yaml
+command:
+ # If using a dedicated metrics entry point, define it:
+ - "--entrypoints.metrics.address=:8082"
+
+ - "--metrics.prometheus=true"
+
+ # Optionally change the entry point metrics are exposed on (defaults to 'traefik')
+ - "--metrics.prometheus.entrypoint=metrics"
+
+ # Add labels to metrics for routers/services (can increase cardinality)
+ - "--metrics.prometheus.addrouterslabels=true"
+```
+
+This enables the `/metrics` endpoint (typically accessed via the internal API port, often 8080 by default if not secured, or via a dedicated entry point). See the [Metrics Documentation](../reference/install-configuration/observability/metrics.md) for options.
+
+#### Tracing (OTel)
+
+You can enable distributed tracing to follow requests through Traefik.
+*Example `command` additions:*
+
+```yaml
+command:
+ # ...
+ - "--tracing.otel=true"
+ - "--tracing.otel.grpcendpoint=otel-collector:4317"
+```
+
+!!! note
+ This option requires a running OTEL collector accessible by Traefik. Consult the [Tracing Documentation](../reference/install-configuration/observability/tracing.md).
+
+#### Access Logs
+
+You can configure Traefik to log incoming requests for debugging and analysis.
+*Example `command` additions:*
+
+```yaml
+command:
+ # ... other command arguments ...
+ - "--accesslog=true" # Enable access logs to stdout
+
+ # Optionally change format or output file (requires volume)
+ - "--accesslog.format=json"
+ - "--accesslog.filepath=/path/to/access.log"
+
+ # Optionally filter logs
+ - "--accesslog.filters.statuscodes=400-599"
+```
+
+### Conclusion
+
+You now have Traefik running on Docker Swarm with HTTPS, a secured dashboard, automatic HTTP → HTTPS redirects, and foundational observability. Expand this stack with Let’s Encrypt, additional middlewares, or multiple Traefik replicas as your Swarm grows.
+
+{!traefik-for-business-applications.md!}
diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml
index 69bf48461..5a28948f0 100644
--- a/docs/mkdocs.yml
+++ b/docs/mkdocs.yml
@@ -50,6 +50,7 @@ markdown_extensions:
- pymdownx.inlinehilite
- pymdownx.highlight:
use_pygments: false # hljs is used instead of pygment for TOML highlighting support
+
- pymdownx.smartsymbols
- pymdownx.superfences
- pymdownx.tabbed
@@ -72,6 +73,15 @@ nav:
- 'Docker': 'getting-started/docker.md'
- 'Configuration Introduction': 'getting-started/configuration-overview.md'
- 'Frequently Asked Questions': 'getting-started/faq.md'
+ - 'Setup':
+ - 'Kubernetes': 'setup/kubernetes.md'
+ - 'Docker': 'setup/docker.md'
+ - 'Swarm': 'setup/swarm.md'
+ - 'Expose':
+ - 'Overview': 'expose/overview.md'
+ - 'Kubernetes': 'expose/kubernetes.md'
+ - 'Docker': 'expose/docker.md'
+ - 'Swarm': 'expose/swarm.md'
- 'Observe':
- 'Overview': 'observe/overview.md'
- 'Logs & Access Logs': 'observe/logs-and-access-logs.md'
diff --git a/go.mod b/go.mod
index e290e4b4c..70498e498 100644
--- a/go.mod
+++ b/go.mod
@@ -207,7 +207,7 @@ require (
github.com/go-playground/validator/v10 v10.16.0 // indirect
github.com/go-resty/resty/v2 v2.16.5 // indirect
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
- github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
+ github.com/go-viper/mapstructure/v2 v2.3.0 // indirect
github.com/go-zookeeper/zk v1.0.3 // indirect
github.com/goccy/go-json v0.10.5 // indirect
github.com/gofrs/flock v0.12.1 // indirect
diff --git a/go.sum b/go.sum
index ededf4f5e..b844481f3 100644
--- a/go.sum
+++ b/go.sum
@@ -423,8 +423,8 @@ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/me
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
-github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
-github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
+github.com/go-viper/mapstructure/v2 v2.3.0 h1:27XbWsHIqhbdR5TIC911OfYvgSaW93HM+dX7970Q7jk=
+github.com/go-viper/mapstructure/v2 v2.3.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/go-zookeeper/zk v1.0.2/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw=
github.com/go-zookeeper/zk v1.0.3 h1:7M2kwOsc//9VeeFiPtf+uSJlVpU66x9Ba5+8XK7/TDg=
github.com/go-zookeeper/zk v1.0.3/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw=
diff --git a/pkg/provider/kubernetes/crd/fixtures/services.yml b/pkg/provider/kubernetes/crd/fixtures/services.yml
index 86094ef8a..7c33a2c63 100644
--- a/pkg/provider/kubernetes/crd/fixtures/services.yml
+++ b/pkg/provider/kubernetes/crd/fixtures/services.yml
@@ -301,6 +301,38 @@ spec:
type: ClusterIP
clusterIP: 10.10.0.1
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: native-disabled-svc
+ namespace: default
+spec:
+ ports:
+ - name: web
+ port: 80
+ type: ClusterIP
+ clusterIP: 10.10.0.1
+
+---
+kind: EndpointSlice
+apiVersion: discovery.k8s.io/v1
+metadata:
+ name: native-disabled-svc-abc
+ namespace: default
+ labels:
+ kubernetes.io/service-name: native-disabled-svc
+addressType: IPv4
+ports:
+ - name: web
+ port: 80
+endpoints:
+ - addresses:
+ - 10.10.0.20
+ - 10.10.0.21
+ conditions:
+ ready: true
+
---
apiVersion: v1
kind: Service
diff --git a/pkg/provider/kubernetes/crd/fixtures/tcp/services.yml b/pkg/provider/kubernetes/crd/fixtures/tcp/services.yml
index 8c29006e8..05f49a53f 100644
--- a/pkg/provider/kubernetes/crd/fixtures/tcp/services.yml
+++ b/pkg/provider/kubernetes/crd/fixtures/tcp/services.yml
@@ -298,11 +298,46 @@ metadata:
spec:
ports:
- - name: myapp
- port: 8000
+ - name: tcp
+ protocol: TCP
+ port: 9000
type: ClusterIP
clusterIP: 10.10.0.1
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: native-disabled-svc-tcp
+ namespace: default
+spec:
+ ports:
+ - name: tcp
+ protocol: TCP
+ port: 9000
+ type: ClusterIP
+ clusterIP: 10.10.0.1
+
+---
+kind: EndpointSlice
+apiVersion: discovery.k8s.io/v1
+metadata:
+ name: native-disabled-tcp-abc
+ namespace: default
+ labels:
+ kubernetes.io/service-name: native-disabled-svc-tcp
+addressType: IPv4
+ports:
+ - name: tcp
+ protocol: TCP
+ port: 9000
+endpoints:
+ - addresses:
+ - 10.10.0.30
+ - 10.10.0.31
+ conditions:
+ ready: true
+
---
apiVersion: v1
kind: Service
diff --git a/pkg/provider/kubernetes/crd/fixtures/tcp/with_global_native_service_lb.yml b/pkg/provider/kubernetes/crd/fixtures/tcp/with_global_native_service_lb.yml
index d8832498e..712119c00 100644
--- a/pkg/provider/kubernetes/crd/fixtures/tcp/with_global_native_service_lb.yml
+++ b/pkg/provider/kubernetes/crd/fixtures/tcp/with_global_native_service_lb.yml
@@ -12,4 +12,4 @@ spec:
- match: HostSNI(`foo.com`)
services:
- name: native-svc-tcp
- port: 8000
+ port: 9000
diff --git a/pkg/provider/kubernetes/crd/fixtures/tcp/with_native_service_lb.yml b/pkg/provider/kubernetes/crd/fixtures/tcp/with_native_service_lb.yml
index c2dbfca31..ad8305ac7 100644
--- a/pkg/provider/kubernetes/crd/fixtures/tcp/with_native_service_lb.yml
+++ b/pkg/provider/kubernetes/crd/fixtures/tcp/with_native_service_lb.yml
@@ -12,5 +12,5 @@ spec:
- match: HostSNI(`foo.com`)
services:
- name: native-svc-tcp
- port: 8000
+ port: 9000
nativeLB: true
diff --git a/pkg/provider/kubernetes/crd/fixtures/tcp/with_native_service_lb_disabled.yml b/pkg/provider/kubernetes/crd/fixtures/tcp/with_native_service_lb_disabled.yml
new file mode 100644
index 000000000..e54b5b3c0
--- /dev/null
+++ b/pkg/provider/kubernetes/crd/fixtures/tcp/with_native_service_lb_disabled.yml
@@ -0,0 +1,12 @@
+apiVersion: traefik.io/v1alpha1
+kind: IngressRouteTCP
+metadata:
+ name: tcp.route.native-disabled
+ namespace: default
+spec:
+ routes:
+ - match: HostSNI(`foo.com`)
+ services:
+ - name: native-disabled-svc-tcp
+ port: 9000
+ nativeLB: false
diff --git a/pkg/provider/kubernetes/crd/fixtures/udp/services.yml b/pkg/provider/kubernetes/crd/fixtures/udp/services.yml
index f77643d36..8993ca084 100644
--- a/pkg/provider/kubernetes/crd/fixtures/udp/services.yml
+++ b/pkg/provider/kubernetes/crd/fixtures/udp/services.yml
@@ -252,11 +252,46 @@ metadata:
spec:
ports:
- - name: myapp
+ - name: udp
+ protocol: UDP
port: 8000
type: ClusterIP
clusterIP: 10.10.0.1
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: native-disabled-svc-udp
+ namespace: default
+spec:
+ ports:
+ - name: udp
+ protocol: UDP
+ port: 8000
+ type: ClusterIP
+ clusterIP: 10.10.0.1
+
+---
+kind: EndpointSlice
+apiVersion: discovery.k8s.io/v1
+metadata:
+ name: native-disabled-udp-abc
+ namespace: default
+ labels:
+ kubernetes.io/service-name: native-disabled-svc-udp
+addressType: IPv4
+ports:
+ - name: udp
+ protocol: UDP
+ port: 8000
+endpoints:
+ - addresses:
+ - 10.10.0.30
+ - 10.10.0.31
+ conditions:
+ ready: true
+
---
apiVersion: v1
kind: Service
diff --git a/pkg/provider/kubernetes/crd/fixtures/udp/with_native_service_lb_disabled.yml b/pkg/provider/kubernetes/crd/fixtures/udp/with_native_service_lb_disabled.yml
new file mode 100644
index 000000000..f8db343fa
--- /dev/null
+++ b/pkg/provider/kubernetes/crd/fixtures/udp/with_native_service_lb_disabled.yml
@@ -0,0 +1,11 @@
+apiVersion: traefik.io/v1alpha1
+kind: IngressRouteUDP
+metadata:
+ name: udp.route.native-disabled
+ namespace: default
+spec:
+ routes:
+ - services:
+ - name: native-disabled-svc-udp
+ port: 8000
+ nativeLB: false
diff --git a/pkg/provider/kubernetes/crd/fixtures/with_native_service_lb_disabled.yml b/pkg/provider/kubernetes/crd/fixtures/with_native_service_lb_disabled.yml
new file mode 100644
index 000000000..07a0accf8
--- /dev/null
+++ b/pkg/provider/kubernetes/crd/fixtures/with_native_service_lb_disabled.yml
@@ -0,0 +1,13 @@
+apiVersion: traefik.io/v1alpha1
+kind: IngressRoute
+metadata:
+ name: test.route.native-disabled
+ namespace: default
+spec:
+ routes:
+ - match: Host(`foo.com`)
+ kind: Rule
+ services:
+ - name: native-disabled-svc
+ port: 80
+ nativeLB: false
diff --git a/pkg/provider/kubernetes/crd/kubernetes_test.go b/pkg/provider/kubernetes/crd/kubernetes_test.go
index b2381998c..ec6651091 100644
--- a/pkg/provider/kubernetes/crd/kubernetes_test.go
+++ b/pkg/provider/kubernetes/crd/kubernetes_test.go
@@ -7778,7 +7778,7 @@ func TestNativeLB(t *testing.T) {
LoadBalancer: &dynamic.TCPServersLoadBalancer{
Servers: []dynamic.TCPServer{
{
- Address: "10.10.0.1:8000",
+ Address: "10.10.0.1:9000",
Port: "",
},
},
@@ -8375,6 +8375,52 @@ func TestGlobalNativeLB(t *testing.T) {
TLS: &dynamic.TLSConfiguration{},
},
},
+ {
+ desc: "HTTP with global native Service LB but service reference has nativeLB disabled",
+ paths: []string{"services.yml", "with_native_service_lb_disabled.yml"},
+ NativeLBByDefault: true,
+ expected: &dynamic.Configuration{
+ UDP: &dynamic.UDPConfiguration{
+ Routers: map[string]*dynamic.UDPRouter{},
+ Services: map[string]*dynamic.UDPService{},
+ },
+ TCP: &dynamic.TCPConfiguration{
+ ServersTransports: map[string]*dynamic.TCPServersTransport{},
+ Routers: map[string]*dynamic.TCPRouter{},
+ Middlewares: map[string]*dynamic.TCPMiddleware{},
+ Services: map[string]*dynamic.TCPService{},
+ },
+ HTTP: &dynamic.HTTPConfiguration{
+ ServersTransports: map[string]*dynamic.ServersTransport{},
+ Routers: map[string]*dynamic.Router{
+ "default-test-route-native-disabled-6f97418635c7e18853da": {
+ Service: "default-test-route-native-disabled-6f97418635c7e18853da",
+ Rule: "Host(`foo.com`)",
+ Priority: 0,
+ },
+ },
+ Middlewares: map[string]*dynamic.Middleware{},
+ Services: map[string]*dynamic.Service{
+ "default-test-route-native-disabled-6f97418635c7e18853da": {
+ LoadBalancer: &dynamic.ServersLoadBalancer{
+ Strategy: dynamic.BalancerStrategyWRR,
+ ResponseForwarding: &dynamic.ResponseForwarding{FlushInterval: dynamic.DefaultFlushInterval},
+ Servers: []dynamic.Server{
+ {
+ URL: "http://10.10.0.20:80",
+ },
+ {
+ URL: "http://10.10.0.21:80",
+ },
+ },
+ PassHostHeader: pointer(true),
+ },
+ },
+ },
+ },
+ TLS: &dynamic.TLSConfiguration{},
+ },
+ },
{
desc: "HTTP with native Service LB in ingressroute",
paths: []string{"services.yml", "with_native_service_lb.yml"},
@@ -8448,7 +8494,51 @@ func TestGlobalNativeLB(t *testing.T) {
LoadBalancer: &dynamic.TCPServersLoadBalancer{
Servers: []dynamic.TCPServer{
{
- Address: "10.10.0.1:8000",
+ Address: "10.10.0.1:9000",
+ Port: "",
+ },
+ },
+ },
+ },
+ },
+ },
+ TLS: &dynamic.TLSConfiguration{},
+ },
+ },
+ {
+ desc: "TCP with global native Service LB but service reference has nativeLB disabled",
+ paths: []string{"tcp/services.yml", "tcp/with_native_service_lb_disabled.yml"},
+ NativeLBByDefault: true,
+ expected: &dynamic.Configuration{
+ UDP: &dynamic.UDPConfiguration{
+ Routers: map[string]*dynamic.UDPRouter{},
+ Services: map[string]*dynamic.UDPService{},
+ },
+ HTTP: &dynamic.HTTPConfiguration{
+ ServersTransports: map[string]*dynamic.ServersTransport{},
+ Routers: map[string]*dynamic.Router{},
+ Middlewares: map[string]*dynamic.Middleware{},
+ Services: map[string]*dynamic.Service{},
+ },
+ TCP: &dynamic.TCPConfiguration{
+ ServersTransports: map[string]*dynamic.TCPServersTransport{},
+ Routers: map[string]*dynamic.TCPRouter{
+ "default-tcp.route.native-disabled-fdd3e9338e47a45efefc": {
+ Service: "default-tcp.route.native-disabled-fdd3e9338e47a45efefc",
+ Rule: "HostSNI(`foo.com`)",
+ },
+ },
+ Middlewares: map[string]*dynamic.TCPMiddleware{},
+ Services: map[string]*dynamic.TCPService{
+ "default-tcp.route.native-disabled-fdd3e9338e47a45efefc": {
+ LoadBalancer: &dynamic.TCPServersLoadBalancer{
+ Servers: []dynamic.TCPServer{
+ {
+ Address: "10.10.0.30:9000",
+ Port: "",
+ },
+ {
+ Address: "10.10.0.31:9000",
Port: "",
},
},
@@ -8488,7 +8578,7 @@ func TestGlobalNativeLB(t *testing.T) {
LoadBalancer: &dynamic.TCPServersLoadBalancer{
Servers: []dynamic.TCPServer{
{
- Address: "10.10.0.1:8000",
+ Address: "10.10.0.1:9000",
Port: "",
},
},
@@ -8578,6 +8668,49 @@ func TestGlobalNativeLB(t *testing.T) {
TLS: &dynamic.TLSConfiguration{},
},
},
+ {
+ desc: "UDP with global native Service LB but service reference has nativeLB disabled",
+ paths: []string{"udp/services.yml", "udp/with_native_service_lb_disabled.yml"},
+ NativeLBByDefault: true,
+ expected: &dynamic.Configuration{
+ UDP: &dynamic.UDPConfiguration{
+ Routers: map[string]*dynamic.UDPRouter{
+ "default-udp.route.native-disabled-0": {
+ Service: "default-udp.route.native-disabled-0",
+ },
+ },
+ Services: map[string]*dynamic.UDPService{
+ "default-udp.route.native-disabled-0": {
+ LoadBalancer: &dynamic.UDPServersLoadBalancer{
+ Servers: []dynamic.UDPServer{
+ {
+ Address: "10.10.0.30:8000",
+ Port: "",
+ },
+ {
+ Address: "10.10.0.31:8000",
+ Port: "",
+ },
+ },
+ },
+ },
+ },
+ },
+ HTTP: &dynamic.HTTPConfiguration{
+ ServersTransports: map[string]*dynamic.ServersTransport{},
+ Routers: map[string]*dynamic.Router{},
+ Middlewares: map[string]*dynamic.Middleware{},
+ Services: map[string]*dynamic.Service{},
+ },
+ TCP: &dynamic.TCPConfiguration{
+ ServersTransports: map[string]*dynamic.TCPServersTransport{},
+ Routers: map[string]*dynamic.TCPRouter{},
+ Middlewares: map[string]*dynamic.TCPMiddleware{},
+ Services: map[string]*dynamic.TCPService{},
+ },
+ TLS: &dynamic.TLSConfiguration{},
+ },
+ },
}
for _, test := range testCases {
@@ -8595,8 +8728,6 @@ func TestGlobalNativeLB(t *testing.T) {
objects := k8s.MustParseYaml(yamlContent)
for _, obj := range objects {
switch o := obj.(type) {
- case *corev1.Service, *corev1.Endpoints, *corev1.Secret:
- k8sObjects = append(k8sObjects, o)
case *traefikv1alpha1.IngressRoute:
crdObjects = append(crdObjects, o)
case *traefikv1alpha1.IngressRouteTCP:
@@ -8612,6 +8743,7 @@ func TestGlobalNativeLB(t *testing.T) {
case *traefikv1alpha1.TLSStore:
crdObjects = append(crdObjects, o)
default:
+ k8sObjects = append(k8sObjects, o)
}
}
}
diff --git a/pkg/provider/kubernetes/gateway/annotations.go b/pkg/provider/kubernetes/gateway/annotations.go
index 8e82ae603..b17cc3cb1 100644
--- a/pkg/provider/kubernetes/gateway/annotations.go
+++ b/pkg/provider/kubernetes/gateway/annotations.go
@@ -16,7 +16,7 @@ type ServiceConfig struct {
// Service is the service's configuration from annotations.
type Service struct {
- NativeLB bool `json:"nativeLB"`
+ NativeLB *bool `json:"nativeLB"`
}
func parseServiceAnnotations(annotations map[string]string) (ServiceConfig, error) {
diff --git a/pkg/provider/kubernetes/gateway/annotations_test.go b/pkg/provider/kubernetes/gateway/annotations_test.go
index 80537526a..5ca8c994d 100644
--- a/pkg/provider/kubernetes/gateway/annotations_test.go
+++ b/pkg/provider/kubernetes/gateway/annotations_test.go
@@ -5,6 +5,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "k8s.io/utils/ptr"
)
func Test_parseServiceConfig(t *testing.T) {
@@ -22,7 +23,7 @@ func Test_parseServiceConfig(t *testing.T) {
},
expected: ServiceConfig{
Service: Service{
- NativeLB: true,
+ NativeLB: ptr.To(true),
},
},
},
diff --git a/pkg/provider/kubernetes/gateway/fixtures/httproute/simple_nativelb_disabled.yml b/pkg/provider/kubernetes/gateway/fixtures/httproute/simple_nativelb_disabled.yml
new file mode 100644
index 000000000..989de4446
--- /dev/null
+++ b/pkg/provider/kubernetes/gateway/fixtures/httproute/simple_nativelb_disabled.yml
@@ -0,0 +1,51 @@
+---
+kind: GatewayClass
+apiVersion: gateway.networking.k8s.io/v1
+metadata:
+ name: my-gateway-class
+spec:
+ controllerName: traefik.io/gateway-controller
+
+---
+kind: Gateway
+apiVersion: gateway.networking.k8s.io/v1
+metadata:
+ name: my-gateway
+ namespace: default
+spec:
+ gatewayClassName: my-gateway-class
+ listeners: # Use GatewayClass defaults for listener definition.
+ - name: http
+ protocol: HTTP
+ port: 80
+ allowedRoutes:
+ kinds:
+ - kind: HTTPRoute
+ group: gateway.networking.k8s.io
+ namespaces:
+ from: Same
+
+---
+kind: HTTPRoute
+apiVersion: gateway.networking.k8s.io/v1
+metadata:
+ name: http-app-1
+ namespace: default
+spec:
+ parentRefs:
+ - name: my-gateway
+ kind: Gateway
+ group: gateway.networking.k8s.io
+ hostnames:
+ - "foo.com"
+ rules:
+ - matches:
+ - path:
+ type: Exact
+ value: /bar
+ backendRefs:
+ - name: whoami-native-disabled
+ port: 80
+ weight: 1
+ kind: Service
+ group: ""
diff --git a/pkg/provider/kubernetes/gateway/fixtures/services.yml b/pkg/provider/kubernetes/gateway/fixtures/services.yml
index 65376431b..2f864c2f5 100644
--- a/pkg/provider/kubernetes/gateway/fixtures/services.yml
+++ b/pkg/provider/kubernetes/gateway/fixtures/services.yml
@@ -471,3 +471,82 @@ spec:
- protocol: TCP
port: 10000
name: tcp-2
+
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: whoami-native-disabled
+ namespace: default
+ annotations:
+ traefik.io/service.nativelb: "false"
+spec:
+ clusterIP: 10.10.10.2
+ ports:
+ - name: web
+ protocol: TCP
+ port: 80
+ targetPort: web
+ selector:
+ app: containous
+ task: whoami
+
+---
+kind: EndpointSlice
+apiVersion: discovery.k8s.io/v1
+metadata:
+ name: whoami-native-disabled-abc
+ namespace: default
+ labels:
+ kubernetes.io/service-name: whoami-native-disabled
+addressType: IPv4
+ports:
+ - name: web
+ port: 80
+endpoints:
+ - addresses:
+ - 10.10.0.20
+ - 10.10.0.21
+ conditions:
+ ready: true
+
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: whoamitcp-native-disabled
+ namespace: default
+ annotations:
+ traefik.io/service.nativelb: "false"
+spec:
+ clusterIP: 10.10.10.3
+ ports:
+ - protocol: TCP
+ port: 9000
+ name: tcp-1
+ - protocol: TCP
+ port: 10000
+ name: tcp-2
+
+---
+kind: EndpointSlice
+apiVersion: discovery.k8s.io/v1
+metadata:
+ name: whoamitcp-native-disabled-abc
+ namespace: default
+ labels:
+ kubernetes.io/service-name: whoamitcp-native-disabled
+addressType: IPv4
+ports:
+ - name: tcp-1
+ protocol: TCP
+ port: 9000
+ - name: tcp-2
+ protocol: TCP
+ port: 10000
+endpoints:
+ - addresses:
+ - 10.10.0.30
+ - 10.10.0.31
+ conditions:
+ ready: true
diff --git a/pkg/provider/kubernetes/gateway/fixtures/tcproute/simple_nativelb_disabled.yml b/pkg/provider/kubernetes/gateway/fixtures/tcproute/simple_nativelb_disabled.yml
new file mode 100644
index 000000000..15f2518fd
--- /dev/null
+++ b/pkg/provider/kubernetes/gateway/fixtures/tcproute/simple_nativelb_disabled.yml
@@ -0,0 +1,46 @@
+---
+kind: GatewayClass
+apiVersion: gateway.networking.k8s.io/v1
+metadata:
+ name: my-gateway-class
+ namespace: default
+spec:
+ controllerName: traefik.io/gateway-controller
+
+---
+kind: Gateway
+apiVersion: gateway.networking.k8s.io/v1
+metadata:
+ name: my-tcp-gateway
+ namespace: default
+spec:
+ gatewayClassName: my-gateway-class
+ listeners: # Use GatewayClass defaults for listener definition.
+ - name: tcp
+ protocol: TCP
+ port: 9000
+ allowedRoutes:
+ namespaces:
+ from: Same
+ kinds:
+ - kind: TCPRoute
+ group: gateway.networking.k8s.io
+
+---
+kind: TCPRoute
+apiVersion: gateway.networking.k8s.io/v1alpha2
+metadata:
+ name: tcp-app-1
+ namespace: default
+spec:
+ parentRefs:
+ - name: my-tcp-gateway
+ kind: Gateway
+ group: gateway.networking.k8s.io
+ rules:
+ - backendRefs:
+ - name: whoamitcp-native-disabled
+ port: 9000
+ weight: 1
+ kind: Service
+ group: ""
\ No newline at end of file
diff --git a/pkg/provider/kubernetes/gateway/kubernetes.go b/pkg/provider/kubernetes/gateway/kubernetes.go
index 1f31045ca..3edd9d799 100644
--- a/pkg/provider/kubernetes/gateway/kubernetes.go
+++ b/pkg/provider/kubernetes/gateway/kubernetes.go
@@ -918,7 +918,12 @@ func (p *Provider) getBackendAddresses(namespace string, ref gatev1.BackendRef)
return nil, corev1.ServicePort{}, fmt.Errorf("parsing service annotations config: %w", err)
}
- if p.NativeLBByDefault || annotationsConfig.Service.NativeLB {
+ nativeLB := p.NativeLBByDefault
+ if annotationsConfig.Service.NativeLB != nil {
+ nativeLB = *annotationsConfig.Service.NativeLB
+ }
+
+ if nativeLB {
if service.Spec.ClusterIP == "" || service.Spec.ClusterIP == "None" {
return nil, corev1.ServicePort{}, fmt.Errorf("no clusterIP found for service: %s/%s", service.Namespace, service.Name)
}
diff --git a/pkg/provider/kubernetes/gateway/kubernetes_test.go b/pkg/provider/kubernetes/gateway/kubernetes_test.go
index 89101ca45..f16051051 100644
--- a/pkg/provider/kubernetes/gateway/kubernetes_test.go
+++ b/pkg/provider/kubernetes/gateway/kubernetes_test.go
@@ -2522,6 +2522,69 @@ func TestLoadHTTPRoutes(t *testing.T) {
TLS: &dynamic.TLSConfiguration{},
},
},
+ {
+ desc: "Simple HTTPRoute with NativeLBByDefault enabled but service has disabled nativelb",
+ paths: []string{"services.yml", "httproute/simple_nativelb_disabled.yml"},
+ nativeLB: true,
+ entryPoints: map[string]Entrypoint{"web": {
+ Address: ":80",
+ }},
+ expected: &dynamic.Configuration{
+ UDP: &dynamic.UDPConfiguration{
+ Routers: map[string]*dynamic.UDPRouter{},
+ Services: map[string]*dynamic.UDPService{},
+ },
+ TCP: &dynamic.TCPConfiguration{
+ Routers: map[string]*dynamic.TCPRouter{},
+ Middlewares: map[string]*dynamic.TCPMiddleware{},
+ Services: map[string]*dynamic.TCPService{},
+ ServersTransports: map[string]*dynamic.TCPServersTransport{},
+ },
+ HTTP: &dynamic.HTTPConfiguration{
+ Routers: map[string]*dynamic.Router{
+ "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-1c0cf64bde37d9d0df06": {
+ EntryPoints: []string{"web"},
+ Service: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-1c0cf64bde37d9d0df06-wrr",
+ Rule: "Host(`foo.com`) && Path(`/bar`)",
+ Priority: 100008,
+ RuleSyntax: "default",
+ },
+ },
+ Middlewares: map[string]*dynamic.Middleware{},
+ Services: map[string]*dynamic.Service{
+ "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-1c0cf64bde37d9d0df06-wrr": {
+ Weighted: &dynamic.WeightedRoundRobin{
+ Services: []dynamic.WRRService{
+ {
+ Name: "default-whoami-native-disabled-http-80",
+ Weight: ptr.To(1),
+ },
+ },
+ },
+ },
+ "default-whoami-native-disabled-http-80": {
+ LoadBalancer: &dynamic.ServersLoadBalancer{
+ Strategy: dynamic.BalancerStrategyWRR,
+ Servers: []dynamic.Server{
+ {
+ URL: "http://10.10.0.20:80",
+ },
+ {
+ URL: "http://10.10.0.21:80",
+ },
+ },
+ PassHostHeader: ptr.To(true),
+ ResponseForwarding: &dynamic.ResponseForwarding{
+ FlushInterval: ptypes.Duration(100 * time.Millisecond),
+ },
+ },
+ },
+ },
+ ServersTransports: map[string]*dynamic.ServersTransport{},
+ },
+ TLS: &dynamic.TLSConfiguration{},
+ },
+ },
}
for _, test := range testCases {
@@ -4429,6 +4492,63 @@ func TestLoadTCPRoutes(t *testing.T) {
TLS: &dynamic.TLSConfiguration{},
},
},
+ {
+ desc: "Simple TCPRoute with NativeLBByDefault enabled but service has disabled nativelb",
+ paths: []string{"services.yml", "tcproute/simple_nativelb_disabled.yml"},
+ nativeLB: true,
+ entryPoints: map[string]Entrypoint{
+ "tcp": {Address: ":9000"},
+ },
+ expected: &dynamic.Configuration{
+ UDP: &dynamic.UDPConfiguration{
+ Routers: map[string]*dynamic.UDPRouter{},
+ Services: map[string]*dynamic.UDPService{},
+ },
+ TCP: &dynamic.TCPConfiguration{
+ Routers: map[string]*dynamic.TCPRouter{
+ "tcproute-default-tcp-app-1-gw-default-my-tcp-gateway-ep-tcp-0-e3b0c44298fc1c149afb": {
+ EntryPoints: []string{"tcp"},
+ Service: "tcproute-default-tcp-app-1-gw-default-my-tcp-gateway-ep-tcp-0-e3b0c44298fc1c149afb-wrr",
+ Rule: "HostSNI(`*`)",
+ RuleSyntax: "default",
+ },
+ },
+ Middlewares: map[string]*dynamic.TCPMiddleware{},
+ Services: map[string]*dynamic.TCPService{
+ "tcproute-default-tcp-app-1-gw-default-my-tcp-gateway-ep-tcp-0-e3b0c44298fc1c149afb-wrr": {
+ Weighted: &dynamic.TCPWeightedRoundRobin{
+ Services: []dynamic.TCPWRRService{
+ {
+ Name: "default-whoamitcp-native-disabled-9000",
+ Weight: ptr.To(1),
+ },
+ },
+ },
+ },
+ "default-whoamitcp-native-disabled-9000": {
+ LoadBalancer: &dynamic.TCPServersLoadBalancer{
+ Servers: []dynamic.TCPServer{
+ {
+ Address: "10.10.0.30:9000",
+ },
+ {
+ Address: "10.10.0.31:9000",
+ },
+ },
+ },
+ },
+ },
+ ServersTransports: map[string]*dynamic.TCPServersTransport{},
+ },
+ HTTP: &dynamic.HTTPConfiguration{
+ Routers: map[string]*dynamic.Router{},
+ Middlewares: map[string]*dynamic.Middleware{},
+ Services: map[string]*dynamic.Service{},
+ ServersTransports: map[string]*dynamic.ServersTransport{},
+ },
+ TLS: &dynamic.TLSConfiguration{},
+ },
+ },
}
for _, test := range testCases {
diff --git a/pkg/provider/kubernetes/ingress/fixtures/Ingress-with-native-lb-by-default-but-service-has-disabled-nativelb.yml b/pkg/provider/kubernetes/ingress/fixtures/Ingress-with-native-lb-by-default-but-service-has-disabled-nativelb.yml
new file mode 100644
index 000000000..a076f3c3e
--- /dev/null
+++ b/pkg/provider/kubernetes/ingress/fixtures/Ingress-with-native-lb-by-default-but-service-has-disabled-nativelb.yml
@@ -0,0 +1,53 @@
+kind: Ingress
+apiVersion: networking.k8s.io/v1
+metadata:
+ name: global-native-lb
+ namespace: default
+spec:
+ rules:
+ - host: traefik.tchouk
+ http:
+ paths:
+ - path: /bar
+ backend:
+ service:
+ name: native-disabled-svc
+ port:
+ name: web
+ number: 8080
+ pathType: Prefix
+
+---
+kind: Service
+apiVersion: v1
+metadata:
+ name: native-disabled-svc
+ namespace: default
+ annotations:
+ traefik.ingress.kubernetes.io/service.nativelb: "false"
+spec:
+ ports:
+ - name: web
+ port: 8080
+ clusterIP: 10.0.0.1
+ type: ClusterIP
+ externalName: traefik.wtf
+
+---
+kind: EndpointSlice
+apiVersion: discovery.k8s.io/v1
+metadata:
+ name: native-disabled-svc-abc
+ namespace: default
+ labels:
+ kubernetes.io/service-name: native-disabled-svc
+addressType: IPv4
+ports:
+ - name: web
+ port: 8080
+endpoints:
+ - addresses:
+ - 10.10.0.20
+ - 10.10.0.21
+ conditions:
+ ready: true
\ No newline at end of file
diff --git a/pkg/provider/kubernetes/ingress/kubernetes_test.go b/pkg/provider/kubernetes/ingress/kubernetes_test.go
index b67ed4058..6cf0113a4 100644
--- a/pkg/provider/kubernetes/ingress/kubernetes_test.go
+++ b/pkg/provider/kubernetes/ingress/kubernetes_test.go
@@ -2179,6 +2179,37 @@ func TestLoadConfigurationFromIngressesWithNativeLBByDefault(t *testing.T) {
},
},
},
+ {
+ desc: "Ingress with native lb by default but service has disabled nativelb",
+ expected: &dynamic.Configuration{
+ HTTP: &dynamic.HTTPConfiguration{
+ Middlewares: map[string]*dynamic.Middleware{},
+ Routers: map[string]*dynamic.Router{
+ "default-global-native-lb-traefik-tchouk-bar": {
+ Rule: "Host(`traefik.tchouk`) && PathPrefix(`/bar`)",
+ Service: "default-native-disabled-svc-web",
+ },
+ },
+ Services: map[string]*dynamic.Service{
+ "default-native-disabled-svc-web": {
+ LoadBalancer: &dynamic.ServersLoadBalancer{
+ Strategy: dynamic.BalancerStrategyWRR,
+ ResponseForwarding: &dynamic.ResponseForwarding{FlushInterval: dynamic.DefaultFlushInterval},
+ PassHostHeader: pointer(true),
+ Servers: []dynamic.Server{
+ {
+ URL: "http://10.10.0.20:8080",
+ },
+ {
+ URL: "http://10.10.0.21:8080",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
}
for _, test := range testCases {
diff --git a/pkg/server/service/loadbalancer/p2c/p2c.go b/pkg/server/service/loadbalancer/p2c/p2c.go
index d6d31413c..3bf06a512 100644
--- a/pkg/server/service/loadbalancer/p2c/p2c.go
+++ b/pkg/server/service/loadbalancer/p2c/p2c.go
@@ -43,6 +43,7 @@ type rnd interface {
type Balancer struct {
wantsHealthCheck bool
+ // handlersMu is a mutex to protect the handlers slice, the status and the fenced maps.
handlersMu sync.RWMutex
handlers []*namedHandler
// status is a record of which child services of the Balancer are healthy, keyed
@@ -50,11 +51,12 @@ type Balancer struct {
// created via Add, and it is later removed or added to the map as needed,
// through the SetStatus method.
status map[string]struct{}
+ // fenced is the list of terminating yet still serving child services.
+ fenced map[string]struct{}
+
// updaters is the list of hooks that are run (to update the Balancer
// parent(s)), whenever the Balancer status changes.
updaters []func(bool)
- // fenced is the list of terminating yet still serving child services.
- fenced map[string]struct{}
sticky *loadbalancer.Sticky
@@ -181,7 +183,10 @@ func (b *Balancer) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
if err != nil {
log.Error().Err(err).Msg("Error while getting sticky handler")
} else if h != nil {
- if _, ok := b.status[h.Name]; ok {
+ b.handlersMu.RLock()
+ _, ok := b.status[h.Name]
+ b.handlersMu.RUnlock()
+ if ok {
if rewrite {
if err := b.sticky.WriteStickyCookie(rw, h.Name); err != nil {
log.Error().Err(err).Msg("Writing sticky cookie")
diff --git a/pkg/server/service/loadbalancer/wrr/wrr.go b/pkg/server/service/loadbalancer/wrr/wrr.go
index 8206b97aa..2c9bff0ee 100644
--- a/pkg/server/service/loadbalancer/wrr/wrr.go
+++ b/pkg/server/service/loadbalancer/wrr/wrr.go
@@ -27,6 +27,7 @@ type namedHandler struct {
type Balancer struct {
wantsHealthCheck bool
+ // handlersMu is a mutex to protect the handlers slice, the status and the fenced maps.
handlersMu sync.RWMutex
handlers []*namedHandler
// status is a record of which child services of the Balancer are healthy, keyed
@@ -34,11 +35,12 @@ type Balancer struct {
// created via Add, and it is later removed or added to the map as needed,
// through the SetStatus method.
status map[string]struct{}
+ // fenced is the list of terminating yet still serving child services.
+ fenced map[string]struct{}
+
// updaters is the list of hooks that are run (to update the Balancer
// parent(s)), whenever the Balancer status changes.
updaters []func(bool)
- // fenced is the list of terminating yet still serving child services.
- fenced map[string]struct{}
sticky *loadbalancer.Sticky
@@ -180,7 +182,10 @@ func (b *Balancer) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
if err != nil {
log.Error().Err(err).Msg("Error while getting sticky handler")
} else if h != nil {
- if _, ok := b.status[h.Name]; ok {
+ b.handlersMu.RLock()
+ _, ok := b.status[h.Name]
+ b.handlersMu.RUnlock()
+ if ok {
if rewrite {
if err := b.sticky.WriteStickyCookie(rw, h.Name); err != nil {
log.Error().Err(err).Msg("Writing sticky cookie")
diff --git a/script/gcg/traefik-bugfix.toml b/script/gcg/traefik-bugfix.toml
index 75f553f47..a1b6f4ab9 100644
--- a/script/gcg/traefik-bugfix.toml
+++ b/script/gcg/traefik-bugfix.toml
@@ -4,11 +4,11 @@ RepositoryName = "traefik"
OutputType = "file"
FileName = "traefik_changelog.md"
-# example new bugfix v3.4.3
+# example new bugfix v3.4.4
CurrentRef = "v3.4"
-PreviousRef = "v3.4.2"
+PreviousRef = "v3.4.3"
BaseBranch = "v3.4"
-FutureCurrentRefName = "v3.4.3"
+FutureCurrentRefName = "v3.4.4"
ThresholdPreviousRef = 10
ThresholdCurrentRef = 10