diff --git a/docs/content/routing/providers/kubernetes-crd.md b/docs/content/routing/providers/kubernetes-crd.md
index a82cc35335de80a88a6596946448f02a480e4de4..f3d5ad360e0601dce3d9276c65ffdaefe502e745 100644
--- a/docs/content/routing/providers/kubernetes-crd.md
+++ b/docs/content/routing/providers/kubernetes-crd.md
@@ -646,7 +646,6 @@ referencing services in the [`IngressRoute`](#kind-ingressroute) objects, or rec
 * services [Weighted Round Robin](#weighted-round-robin) load balancing.
 * services [mirroring](#mirroring).
 
-
 #### Server Load Balancing
 
 More information in the dedicated server [load balancing](../services/index.md#load-balancing) section.
@@ -916,6 +915,154 @@ More information in the dedicated [mirroring](../services/index.md#mirroring-ser
     
     Specifying a namespace attribute in this case would not make any sense, and will be ignored (except if the provider is `kubernetescrd`).
 
+#### Stickiness and load-balancing
+
+As explained in the section about [Sticky sessions](../../services/#sticky-sessions), for stickiness to work all the way,
+it must be specified at each load-balancing level.
+
+For instance, in the example below, there is a first level of load-balancing because there is a (Weighted Round Robin) load-balancing of the two `whoami` services,
+and there is a second level because each whoami service is a `replicaset` and is thus handled as a load-balancer of servers.
+
+??? "Stickiness on two load-balancing levels"
+
+    ```yaml tab="IngressRoute"
+    apiVersion: traefik.containo.us/v1alpha1
+    kind: IngressRoute
+    metadata:
+      name: ingressroutebar
+      namespace: default
+
+    spec:
+      entryPoints:
+        - web
+      routes:
+      - match: Host(`example.com`) && PathPrefix(`/foo`)
+        kind: Rule
+        services:
+        - name: wrr1
+          namespace: default
+          kind: TraefikService
+    ```
+
+    ```yaml tab="Weighted Round Robin"
+    apiVersion: traefik.containo.us/v1alpha1
+    kind: TraefikService
+    metadata:
+      name: wrr1
+      namespace: default
+
+    spec:
+      weighted:
+        services:
+          - name: whoami1
+            kind: Service
+            port: 80
+            weight: 1
+            sticky:
+              cookie:
+                name: lvl2
+          - name: whoami2
+            kind: Service
+            weight: 1
+            port: 80
+            sticky:
+              cookie:
+                name: lvl2
+        sticky:
+          cookie:
+            name: lvl1
+    ```
+
+    ```yaml tab="K8s Service"
+    apiVersion: v1
+    kind: Service
+    metadata:
+      name: whoami1
+
+    spec:
+      ports:
+        - protocol: TCP
+          name: web
+          port: 80
+      selector:
+        app: whoami1
+
+    ---
+    apiVersion: v1
+    kind: Service
+    metadata:
+      name: whoami2
+
+    spec:
+      ports:
+        - protocol: TCP
+          name: web
+          port: 80
+      selector:
+        app: whoami2
+    ```
+
+    ```yaml tab="Deployment (to illustrate replicas)"
+    kind: Deployment
+    apiVersion: apps/v1
+    metadata:
+      namespace: default
+      name: whoami1
+      labels:
+        app: whoami1
+
+    spec:
+      replicas: 2
+      selector:
+        matchLabels:
+          app: whoami1
+      template:
+        metadata:
+          labels:
+            app: whoami1
+        spec:
+          containers:
+            - name: whoami1
+              image: containous/whoami
+              ports:
+                - name: web
+                  containerPort: 80
+
+    ---
+    kind: Deployment
+    apiVersion: apps/v1
+    metadata:
+      namespace: default
+      name: whoami2
+      labels:
+        app: whoami2
+
+    spec:
+      replicas: 2
+      selector:
+        matchLabels:
+          app: whoami2
+      template:
+        metadata:
+          labels:
+            app: whoami2
+        spec:
+          containers:
+            - name: whoami2
+              image: containous/whoami
+              ports:
+                - name: web
+                  containerPort: 80
+    ```
+
+    To keep a session open with the same server, the client would then need to specify the two levels within the cookie for each request, e.g. with curl:
+
+    ```bash
+    curl -H Host:example.com -b "lvl1=default-whoami1-80; lvl2=http://10.42.0.6:80" http://localhost:8000/foo
+    ```
+
+    assuming `10.42.0.6` is the IP address of one of the replicas (a pod then) of the `whoami1` service.
+
 ### Kind `IngressRouteTCP`
 
 `IngressRouteTCP` is the CRD implementation of a [Traefik TCP router](../routers/index.md#configuring-tcp-routers).
@@ -1192,7 +1339,7 @@ Register the `IngressRouteUDP` [kind](../../reference/dynamic-configuration/kube
           port: 8081
           weight: 10
     ```
-    
+
 ### Kind: `TLSOption`
 
 `TLSOption` is the CRD implementation of a [Traefik "TLS Option"](../../https/tls.md#tls-options).
@@ -1386,6 +1533,7 @@ or referencing TLS stores in the [`IngressRoute`](#kind-ingressroute) / [`Ingres
       tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0=
       tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCi0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0=
     ```
+
 ## Further
 
 Also see the [full example](../../user-guides/crd-acme/index.md) with Let's Encrypt.
diff --git a/docs/content/routing/services/index.md b/docs/content/routing/services/index.md
index 1f8abc9367a49aae2bdccd663420f1e63a7212d1..8211b0cd0aa259c956a89714171c5fac0e10fe34 100644
--- a/docs/content/routing/services/index.md
+++ b/docs/content/routing/services/index.md
@@ -167,8 +167,12 @@ For now, only round robin load balancing is supported:
 
 #### Sticky sessions
 
-When sticky sessions are enabled, a cookie is set on the initial request to track which server handles the first response.
-On subsequent requests, the client is forwarded to the same server.
+When sticky sessions are enabled, a cookie is set on the initial request and response to let the client know which server handles the first response.
+On subsequent requests, to keep the session alive with the same server, the client should resend the same cookie.
+
+!!! info "Stickiness on multiple levels"
+
+    When chaining or mixing load-balancers (e.g. a load-balancer of servers is one of the "children" of a load-balancer of services), for stickiness to work all the way, the option needs to be specified at all required levels. Which means the client needs to send a cookie with as many key/value pairs as there are sticky levels.
 
 !!! info "Stickiness & Unhealthy Servers"
 
@@ -226,6 +230,80 @@ On subsequent requests, the client is forwarded to the same server.
                 httpOnly: true
     ```
 
+??? example "Setting Stickiness on all the required levels -- Using the [File Provider](../../providers/file.md)"
+
+    ```toml tab="TOML"
+    ## Dynamic configuration
+    [http.services]
+      [http.services.wrr1]
+        [http.services.wrr1.weighted.sticky.cookie]
+          name = "lvl1"
+        [[http.services.wrr1.weighted.services]]
+          name = "whoami1"
+          weight = 1
+        [[http.services.wrr1.weighted.services]]
+          name = "whoami2"
+          weight = 1
+
+      [http.services.whoami1]
+        [http.services.whoami1.loadBalancer]
+          [http.services.whoami1.loadBalancer.sticky.cookie]
+            name = "lvl2"
+          [[http.services.whoami1.loadBalancer.servers]]
+            url = "http://127.0.0.1:8081"
+          [[http.services.whoami1.loadBalancer.servers]]
+            url = "http://127.0.0.1:8082"
+
+      [http.services.whoami2]
+        [http.services.whoami2.loadBalancer]
+          [http.services.whoami2.loadBalancer.sticky.cookie]
+            name = "lvl2"
+          [[http.services.whoami2.loadBalancer.servers]]
+            url = "http://127.0.0.1:8083"
+          [[http.services.whoami2.loadBalancer.servers]]
+            url = "http://127.0.0.1:8084"
+    ```
+
+    ```yaml tab="YAML"
+    ## Dynamic configuration
+    http:
+      services:
+        wrr1:
+          weighted:
+            sticky:
+              cookie:
+                name: lvl1
+            services:
+              - name: whoami1
+                weight: 1
+              - name: whoami2
+                weight: 1
+
+        whoami1:
+          loadBalancer:
+            sticky:
+              cookie:
+                name: lvl2
+            servers:
+              - url: http://127.0.0.1:8081
+              - url: http://127.0.0.1:8082
+
+        whoami2:
+          loadBalancer:
+            sticky:
+              cookie:
+                name: lvl2
+            servers:
+              - url: http://127.0.0.1:8083
+              - url: http://127.0.0.1:8084
+    ```
+
+    To keep a session open with the same server, the client would then need to specify the two levels within the cookie for each request, e.g. with curl:
+    
+    ```
+    curl -b "lvl1=whoami1; lvl2=http://127.0.0.1:8081" http://localhost:8000
+    ```
+
 #### Health Check
 
 Configure health check to remove unhealthy servers from the load balancing rotation.
diff --git a/docs/content/user-guides/crd-acme/index.md b/docs/content/user-guides/crd-acme/index.md
index e9d917d2a4c048dfc8685623545a6de4818cb800..8b14a5bd47d3ae2a2ddd0a14cad07623c4460026 100644
--- a/docs/content/user-guides/crd-acme/index.md
+++ b/docs/content/user-guides/crd-acme/index.md
@@ -101,7 +101,7 @@ curl [-k] https://your.example.com/tls
 ```
 
 ```bash
-curl [-k] http://your.example.com:8000/notls
+curl http://your.example.com:8000/notls
 ```
 
 Note that you'll have to use `-k` as long as you're using the staging server of Let's Encrypt, since it is not an authorized certificate authority on systems where it hasn't been manually added.
diff --git a/pkg/provider/kubernetes/crd/traefik/v1alpha1/ingressroute.go b/pkg/provider/kubernetes/crd/traefik/v1alpha1/ingressroute.go
index a8b6eae22d7353100e0737a7524160dd3d9deda9..9cc3face39465999f991fcdba8cf1f2eb36704f6 100644
--- a/pkg/provider/kubernetes/crd/traefik/v1alpha1/ingressroute.go
+++ b/pkg/provider/kubernetes/crd/traefik/v1alpha1/ingressroute.go
@@ -1,9 +1,6 @@
 package v1alpha1
 
 import (
-	"errors"
-	"fmt"
-
 	"github.com/containous/traefik/v2/pkg/config/dynamic"
 	"github.com/containous/traefik/v2/pkg/types"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -82,30 +79,6 @@ type LoadBalancerSpec struct {
 	Weight *int `json:"weight,omitempty"`
 }
 
-// IsServersLB reports whether lb is a load-balancer of servers
-// (as opposed to a traefik load-balancer of services).
-func (lb LoadBalancerSpec) IsServersLB() (bool, error) {
-	if lb.Name == "" {
-		return false, errors.New("missing Name field in service")
-	}
-	if lb.Kind == "" || lb.Kind == "Service" {
-		return true, nil
-	}
-	if lb.Kind != "TraefikService" {
-		return false, fmt.Errorf("invalid kind value: %v", lb.Kind)
-	}
-	if lb.Port != 0 ||
-		lb.Scheme != "" ||
-		lb.HealthCheck != nil ||
-		lb.Strategy != "" ||
-		lb.PassHostHeader != nil ||
-		lb.ResponseForwarding != nil ||
-		lb.Sticky != nil {
-		return false, fmt.Errorf("service of kind %v is incompatible with Kubernetes Service related fields", lb.Kind)
-	}
-	return false, nil
-}
-
 // Service defines an upstream to proxy traffic.
 type Service struct {
 	LoadBalancerSpec