Repository: containous/traefik
Pull request #5237 (2019-08-21 14:19:48)

WeightedRoundRobin load balancer
### What does this PR do?

Add a new load balancer to make weighted round-robin load balancing.
Weighted RoundRobin balanced on services and not on servers.

### Motivation

Be able to do weighted round-robin load balancing (like canary).

### More

- [x] Added/updated tests
- [x] Added/updated documentation

### Additional Notes

#### Breaking changes

To be homogeneous with WRR, we renamed stickyness on server loadBalancer to sticky.

```yaml
# BEFORE
- "traefik.http.services.service0.loadbalancer.stickiness=true"
- "traefik.http.services.service0.loadbalancer.stickiness.cookiename=foobar"
- "traefik.http.services.service0.loadbalancer.stickiness.httponlycookie=true"
- "traefik.http.services.service0.loadbalancer.stickiness.securecookie=true" 

# AFTER
- "traefik.http.services.service0.loadbalancer.sticky=true"
- "traefik.http.services.service0.loadbalancer.sticky.cookie.name=foobar"
- "traefik.http.services.service0.loadbalancer.sticky.cookie.httponly=true"
- "traefik.http.services.service0.loadbalancer.sticky.cookie.secure=true"
```

```toml
# BEFORE
[http.services.Service0.loadBalancer.stickiness]	        
  cookieName = "foobar"
  secureCookie = true
  httpOnlyCookie = true

# AFTER
[http.services.Service0.loadBalancer.sticky.cookie]
  name = "foobar"
  secure = true
  httpOnly = true
```

Co-authored-by: Ludovic Fernandez <ldez@users.noreply.github.com>


Patch:
diff --git a/docs/content/providers/kubernetes-crd.md b/docs/content/providers/kubernetes-crd.md
index a2f396ef5..637024e8b 100644
--- a/docs/content/providers/kubernetes-crd.md
+++ b/docs/content/providers/kubernetes-crd.md
@@ -198,11 +198,13 @@ spec:
     # "Parameter", etc, to support simpler forms of rule matching, but for now we
     # only support "Rule".
     kind: Rule
-    # Priority disambiguates rules of the same length, for route matching.
+    # (optional) Priority disambiguates rules of the same length, for route matching.
     priority: 12
     services:
     - name: whoami
       port: 80
+      # (default 1) A weight used by the weighted round-robin strategy (WRR).  
+      weight: 1
 
 ---
 apiVersion: traefik.containo.us/v1alpha1
diff --git a/docs/content/reference/dynamic-configuration/docker-labels.yml b/docs/content/reference/dynamic-configuration/docker-labels.yml
index bde9e1c2c..69c40b9f5 100644
--- a/docs/content/reference/dynamic-configuration/docker-labels.yml
+++ b/docs/content/reference/dynamic-configuration/docker-labels.yml
@@ -130,10 +130,10 @@
 - "traefik.http.services.service0.loadbalancer.healthcheck.timeout=foobar"
 - "traefik.http.services.service0.loadbalancer.passhostheader=true"
 - "traefik.http.services.service0.loadbalancer.responseforwarding.flushinterval=foobar"
-- "traefik.http.services.service0.loadbalancer.stickiness=true"
-- "traefik.http.services.service0.loadbalancer.stickiness.cookiename=foobar"
-- "traefik.http.services.service0.loadbalancer.stickiness.httponlycookie=true"
-- "traefik.http.services.service0.loadbalancer.stickiness.securecookie=true"
+- "traefik.http.services.service0.loadbalancer.sticky=true"
+- "traefik.http.services.service0.loadbalancer.sticky.cookie.name=foobar"
+- "traefik.http.services.service0.loadbalancer.sticky.cookie.httponly=true"
+- "traefik.http.services.service0.loadbalancer.sticky.cookie.secure=true"
 - "traefik.http.services.service0.loadbalancer.server.port=foobar"
 - "traefik.http.services.service0.loadbalancer.server.scheme=foobar"
 - "traefik.http.services.service1.loadbalancer.healthcheck.headers.name0=foobar"
@@ -146,10 +146,10 @@
 - "traefik.http.services.service1.loadbalancer.healthcheck.timeout=foobar"
 - "traefik.http.services.service1.loadbalancer.passhostheader=true"
 - "traefik.http.services.service1.loadbalancer.responseforwarding.flushinterval=foobar"
-- "traefik.http.services.service1.loadbalancer.stickiness=true"
-- "traefik.http.services.service1.loadbalancer.stickiness.cookiename=foobar"
-- "traefik.http.services.service1.loadbalancer.stickiness.httponlycookie=true"
-- "traefik.http.services.service1.loadbalancer.stickiness.securecookie=true"
+- "traefik.http.services.service1.loadbalancer.sticky=true"
+- "traefik.http.services.service1.loadbalancer.sticky.cookie.name=foobar"
+- "traefik.http.services.service1.loadbalancer.sticky.cookie.httponly=true"
+- "traefik.http.services.service1.loadbalancer.sticky.cookie.secure=true"
 - "traefik.http.services.service1.loadbalancer.server.port=foobar"
 - "traefik.http.services.service1.loadbalancer.server.scheme=foobar"
 - "traefik.tcp.routers.tcprouter0.entrypoints=foobar, foobar"
diff --git a/docs/content/reference/dynamic-configuration/file.toml b/docs/content/reference/dynamic-configuration/file.toml
index 1bc9c258c..1e0a6bfb5 100644
--- a/docs/content/reference/dynamic-configuration/file.toml
+++ b/docs/content/reference/dynamic-configuration/file.toml
@@ -35,56 +35,47 @@
           main = "foobar"
           sans = ["foobar", "foobar"]
   [http.services]
-    [http.services.Service0]
-      [http.services.Service0.loadBalancer]
+    [http.services.Service01]
+      [http.services.Service01.loadBalancer]
         passHostHeader = true
-        [http.services.Service0.loadBalancer.stickiness]
-          cookieName = "foobar"
-          secureCookie = true
-          httpOnlyCookie = true
+        [http.services.Service01.loadBalancer.sticky]
+          [http.services.Service01.loadBalancer.sticky.cookie]
+            name = "foobar"
+            secure = true
+            httpOnly = true
 
-        [[http.services.Service0.loadBalancer.servers]]
+        [[http.services.Service01.loadBalancer.servers]]
           url = "foobar"
 
-        [[http.services.Service0.loadBalancer.servers]]
+        [[http.services.Service01.loadBalancer.servers]]
           url = "foobar"
-        [http.services.Service0.loadBalancer.healthCheck]
+        [http.services.Service01.loadBalancer.healthCheck]
           scheme = "foobar"
           path = "foobar"
           port = 42
           interval = "foobar"
           timeout = "foobar"
           hostname = "foobar"
-          [http.services.Service0.loadBalancer.healthCheck.headers]
+          [http.services.Service01.loadBalancer.healthCheck.headers]
             name0 = "foobar"
             name1 = "foobar"
-        [http.services.Service0.loadBalancer.responseForwarding]
+        [http.services.Service01.loadBalancer.responseForwarding]
           flushInterval = "foobar"
-    [http.services.Service1]
-      [http.services.Service1.loadBalancer]
-        passHostHeader = true
-        [http.services.Service1.loadBalancer.stickiness]
-          cookieName = "foobar"
-          secureCookie = true
-          httpOnlyCookie = true
+    [http.services.Service02]
+      [http.services.Service02.weighted]
 
-        [[http.services.Service1.loadBalancer.servers]]
-          url = "foobar"
+        [[http.services.Service02.weighted.services]]
+          name = "foobar"
+          weight = 42
 
-        [[http.services.Service1.loadBalancer.servers]]
-          url = "foobar"
-        [http.services.Service1.loadBalancer.healthCheck]
-          scheme = "foobar"
-          path = "foobar"
-          port = 42
-          interval = "foobar"
-          timeout = "foobar"
-          hostname = "foobar"
-          [http.services.Service1.loadBalancer.healthCheck.headers]
-            name0 = "foobar"
-            name1 = "foobar"
-        [http.services.Service1.loadBalancer.responseForwarding]
-          flushInterval = "foobar"
+        [[http.services.Service02.weighted.services]]
+          name = "foobar"
+          weight = 42
+        [http.services.Service02.weighted.sticky]
+          [http.services.Service02.weighted.sticky.cookie]
+            name = "foobar"
+            secure = true
+            httpOnly = true
   [http.middlewares]
     [http.middlewares.Middleware00]
       [http.middlewares.Middleware00.addPrefix]
diff --git a/docs/content/reference/dynamic-configuration/file.yaml b/docs/content/reference/dynamic-configuration/file.yaml
index a789b6d0d..bea253fdc 100644
--- a/docs/content/reference/dynamic-configuration/file.yaml
+++ b/docs/content/reference/dynamic-configuration/file.yaml
@@ -45,34 +45,13 @@ http:
           - foobar
           - foobar
   services:
-    Service0:
+    Service01:
       loadBalancer:
-        stickiness:
-          cookieName: foobar
-          secureCookie: true
-          httpOnlyCookie: true
-        servers:
-        - url: foobar
-        - url: foobar
-        healthCheck:
-          scheme: foobar
-          path: foobar
-          port: 42
-          interval: foobar
-          timeout: foobar
-          hostname: foobar
-          headers:
-            name0: foobar
-            name1: foobar
-        passHostHeader: true
-        responseForwarding:
-          flushInterval: foobar
-    Service1:
-      loadBalancer:
-        stickiness:
-          cookieName: foobar
-          secureCookie: true
-          httpOnlyCookie: true
+        sticky:
+          cookie:
+            name: foobar
+            secure: true
+            httpOnly: true
         servers:
         - url: foobar
         - url: foobar
@@ -89,6 +68,18 @@ http:
         passHostHeader: true
         responseForwarding:
           flushInterval: foobar
+    Service02:
+      weighted:
+        services:
+        - name: foobar
+          weight: 42
+        - name: foobar
+          weight: 42
+        sticky:
+          cookie:
+            name: foobar
+            secure: true
+            httpOnly: true
   middlewares:
     Middleware00:
       addPrefix:
diff --git a/docs/content/reference/dynamic-configuration/marathon-labels.json b/docs/content/reference/dynamic-configuration/marathon-labels.json
index a3419d75e..6c2432fdb 100644
--- a/docs/content/reference/dynamic-configuration/marathon-labels.json
+++ b/docs/content/reference/dynamic-configuration/marathon-labels.json
@@ -130,10 +130,10 @@
 "traefik.http.services.service0.loadbalancer.healthcheck.timeout": "foobar",
 "traefik.http.services.service0.loadbalancer.passhostheader": "true",
 "traefik.http.services.service0.loadbalancer.responseforwarding.flushinterval": "foobar",
-"traefik.http.services.service0.loadbalancer.stickiness": "true",
-"traefik.http.services.service0.loadbalancer.stickiness.cookiename": "foobar",
-"traefik.http.services.service0.loadbalancer.stickiness.httponlycookie": "true",
-"traefik.http.services.service0.loadbalancer.stickiness.securecookie": "true",
+"traefik.http.services.service0.loadbalancer.sticky": "true",
+"traefik.http.services.service0.loadbalancer.sticky.cookie.name": "foobar",
+"traefik.http.services.service0.loadbalancer.sticky.cookie.httponly": "true",
+"traefik.http.services.service0.loadbalancer.sticky.cookie.secure": "true",
 "traefik.http.services.service0.loadbalancer.server.port": "foobar",
 "traefik.http.services.service0.loadbalancer.server.scheme": "foobar",
 "traefik.http.services.service1.loadbalancer.healthcheck.headers.name0": "foobar",
@@ -146,10 +146,10 @@
 "traefik.http.services.service1.loadbalancer.healthcheck.timeout": "foobar",
 "traefik.http.services.service1.loadbalancer.passhostheader": "true",
 "traefik.http.services.service1.loadbalancer.responseforwarding.flushinterval": "foobar",
-"traefik.http.services.service1.loadbalancer.stickiness": "true",
-"traefik.http.services.service1.loadbalancer.stickiness.cookiename": "foobar",
-"traefik.http.services.service1.loadbalancer.stickiness.httponlycookie": "true",
-"traefik.http.services.service1.loadbalancer.stickiness.securecookie": "true",
+"traefik.http.services.service1.loadbalancer.sticky": "true",
+"traefik.http.services.service1.loadbalancer.sticky.cookie.name": "foobar",
+"traefik.http.services.service1.loadbalancer.sticky.cookie.secure": "true",
+"traefik.http.services.service1.loadbalancer.sticky.cookie.httponly": "true",
 "traefik.http.services.service1.loadbalancer.server.port": "foobar",
 "traefik.http.services.service1.loadbalancer.server.scheme": "foobar",
 "traefik.tcp.routers.tcprouter0.entrypoints": "foobar, foobar",
diff --git a/docs/content/routing/services/index.md b/docs/content/routing/services/index.md
index cef456c93..5bc1fa05b 100644
--- a/docs/content/routing/services/index.md
+++ b/docs/content/routing/services/index.md
@@ -54,13 +54,7 @@ The `Services` are responsible for configuring how to reach the actual services
 
 ## Configuring HTTP Services
 
-### General
-
-Currently, `LoadBalancer` is the only supported kind of HTTP `Service` (see below).
-However, since Traefik is an ever evolving project, other kind of HTTP Services will be available in the future,
-reason why you have to specify it. 
-
-### Load Balancer
+### Servers Load Balancer
 
 The load balancers are able to load balance the requests between multiple instances of your programs. 
 
@@ -161,7 +155,7 @@ On subsequent requests, the client is forwarded to the same server.
     ```toml tab="TOML"
     [http.services]
       [http.services.my-service]
-        [http.services.my-service.loadBalancer.stickiness]
+        [http.services.my-service.loadBalancer.sticky.cookie]
     ```
     
     ```yaml tab="YAML"
@@ -169,18 +163,19 @@ On subsequent requests, the client is forwarded to the same server.
       services:
         my-service:
           loadBalancer:
-            stickiness: {}
+            sticky:
+             cookie: {}
     ```
 
-??? example "Adding Stickiness with a Custom Cookie Name"
+??? example "Adding Stickiness with custom Options"
 
     ```toml tab="TOML"
     [http.services]
       [http.services.my-service]
-        [http.services.my-service.loadBalancer.stickiness]
-          cookieName = "my_stickiness_cookie_name"
-          secureCookie = true
-          httpOnlyCookie = true
+        [http.services.my-service.loadBalancer.sticky.cookie]
+          name = "my_sticky_cookie_name"
+          secure = true
+          httpOnly = true
     ```
 
     ```yaml tab="YAML"
@@ -188,10 +183,11 @@ On subsequent requests, the client is forwarded to the same server.
       services:
         my-service:
           loadBalancer:
-            stickiness:
-              cookieName: my_stickiness_cookie_name
-              secureCookie: true
-              httpOnlyCookie: true
+            sticky:
+              cookie:
+                name: my_sticky_cookie_name
+                secure: true
+                httpOnly: true
     ```
 
 #### Health Check
@@ -306,6 +302,57 @@ Below are the available options for the health check mechanism:
                 My-Header: bar
     ```
 
+### Weighted Round Robin (service)
+
+The WRR is able to load balance the requests between multiple services based on weights.
+
+This strategy is only available to load balance between [services](./index.md) and not between [servers](./index.md#servers).
+
+This strategy can be defined only with [File](../../providers/file.md).
+
+```toml tab="TOML"
+[http.services]
+  [http.services.canary]
+    [[http.services.canary.weighted.services]]
+      name = "appv1"
+      weight = 3
+    [[http.services.canary.weighted.services]]
+      name = "appv2"
+      weight = 1
+
+  [http.services.appv1]
+    [http.services.appv1.loadBalancer]
+      [[http.services.appv1.loadBalancer.servers]]
+        url = "http://private-ip-server-1/"
+
+  [http.services.appv2]
+    [http.services.appv2.loadBalancer]
+      [[http.services.appv2.loadBalancer.servers]]
+        url = "http://private-ip-server-2/"
+```
+
+```yaml tab="YAML"
+http:
+  services:
+    canary:
+      weighted:
+        services:
+        - name: appv1
+          weight: 3
+        - name: appv2
+          weight: 1
+
+    appv1:
+      loadBalancer:
+        servers:
+        - url: "http://private-ip-server-1/"
+
+    appv2:
+      loadBalancer:
+        servers:
+        - url: "http://private-ip-server-2/"
+```
+
 ## Configuring TCP Services
 
 ### General
diff --git a/integration/fixtures/tcp/multi-tls-options.toml b/integration/fixtures/tcp/multi-tls-options.toml
index 9aca38006..b36dae76a 100644
--- a/integration/fixtures/tcp/multi-tls-options.toml
+++ b/integration/fixtures/tcp/multi-tls-options.toml
@@ -34,7 +34,6 @@
 
     [tcp.services.whoami-no-cert]
       [tcp.services.whoami-no-cert.loadBalancer]
-        method = "wrr"
         [[tcp.services.whoami-no-cert.loadBalancer.servers]]
         address = "localhost:8083"
 
diff --git a/integration/fixtures/wrr.toml b/integration/fixtures/wrr.toml
new file mode 100644
index 000000000..ad63ceb0d
--- /dev/null
+++ b/integration/fixtures/wrr.toml
@@ -0,0 +1,38 @@
+[global]
+  checkNewVersion = false
+  sendAnonymousUsage = false
+
+[api]
+
+[log]
+  level = "DEBUG"
+
+[entryPoints]
+
+  [entryPoints.web]
+    address = ":8000"
+
+[providers.file]
+  filename = "{{ .SelfFilename }}"
+
+## dynamic configuration ##
+
+[http.routers]
+  [http.routers.router]
+    service = "wrr"
+    rule = "Path(`/whoami`)"
+
+[http.services]
+  [[http.services.wrr.weighted.services]]
+    name = "service1"
+    weight = 3
+  [[http.services.wrr.weighted.services]]
+    name = "service2"
+
+  [http.services.service1.loadBalancer]
+    [[http.services.service1.loadBalancer.servers]]
+      url = "{{ .Server1 }}"
+  [http.services.service2.loadBalancer]
+    [[http.services.service2.loadBalancer.servers]]
+      url = "{{ .Server2 }}"
+
diff --git a/integration/fixtures/wrr_sticky.toml b/integration/fixtures/wrr_sticky.toml
new file mode 100644
index 000000000..4181dde54
--- /dev/null
+++ b/integration/fixtures/wrr_sticky.toml
@@ -0,0 +1,41 @@
+[global]
+  checkNewVersion = false
+  sendAnonymousUsage = false
+
+[api]
+
+[log]
+  level = "DEBUG"
+
+[entryPoints]
+
+  [entryPoints.web]
+    address = ":8000"
+
+[providers.file]
+  filename = "{{ .SelfFilename }}"
+
+## dynamic configuration ##
+
+[http.routers]
+  [http.routers.router]
+    service = "wrr"
+    rule = "Path(`/whoami`)"
+
+[http.services]
+  [http.services.wrr.weighted.sticky.cookie]
+    name = "test"
+
+  [[http.services.wrr.weighted.services]]
+    name = "service1"
+    weight = 3
+  [[http.services.wrr.weighted.services]]
+    name = "service2"
+    weight = 1
+
+  [http.services.service1.loadBalancer]
+    [[http.services.service1.loadBalancer.servers]]
+      url = "{{ .Server1 }}"
+  [http.services.service2.loadBalancer]
+    [[http.services.service2.loadBalancer.servers]]
+      url = "{{ .Server2 }}"
diff --git a/pkg/config/dynamic/fixtures/sample.toml b/pkg/config/dynamic/fixtures/sample.toml
index 017d1db0d..748cf6497 100644
--- a/pkg/config/dynamic/fixtures/sample.toml
+++ b/pkg/config/dynamic/fixtures/sample.toml
@@ -404,8 +404,8 @@
     [http.services.Service0]
       [http.services.Service0.loadBalancer]
         passHostHeader = true
-        [http.services.Service0.loadBalancer.stickiness]
-          cookieName = "foobar"
+        [http.services.Service0.loadBalancer.sticky.cookie]
+          name = "foobar"
 
         [[http.services.Service0.loadBalancer.servers]]
           url = "foobar"
diff --git a/pkg/config/dynamic/http_config.go b/pkg/config/dynamic/http_config.go
index 6bd05ba00..2b855d32f 100644
--- a/pkg/config/dynamic/http_config.go
+++ b/pkg/config/dynamic/http_config.go
@@ -19,7 +19,8 @@ type HTTPConfiguration struct {
 
 // Service holds a service configuration (can only be of one type at the same time).
 type Service struct {
-	LoadBalancer *LoadBalancerService `json:"loadBalancer,omitempty" toml:"loadBalancer,omitempty" yaml:"loadBalancer,omitempty"`
+	LoadBalancer *ServersLoadBalancer `json:"loadBalancer,omitempty" toml:"loadBalancer,omitempty" yaml:"loadBalancer,omitempty"`
+	Weighted     *WeightedRoundRobin  `json:"weighted,omitempty" toml:"weighted,omitempty" yaml:"weighted,omitempty" label:"-"`
 }
 
 // +k8s:deepcopy-gen=true
@@ -45,9 +46,47 @@ type RouterTLSConfig struct {
 
 // +k8s:deepcopy-gen=true
 
-// LoadBalancerService holds the LoadBalancerService configuration.
-type LoadBalancerService struct {
-	Stickiness         *Stickiness         `json:"stickiness,omitempty" toml:"stickiness,omitempty" yaml:"stickiness,omitempty" label:"allowEmpty"`
+// WeightedRoundRobin is a weighted round robin load-balancer of services.
+type WeightedRoundRobin struct {
+	Services []WRRService `json:"services,omitempty" toml:"services,omitempty" yaml:"services,omitempty"`
+	Sticky   *Sticky      `json:"sticky,omitempty" toml:"sticky,omitempty" yaml:"sticky,omitempty"`
+}
+
+// +k8s:deepcopy-gen=true
+
+// WRRService is a reference to a service load-balanced with weighted round robin.
+type WRRService struct {
+	Name   string `json:"name,omitempty" toml:"name,omitempty" yaml:"name,omitempty"`
+	Weight *int   `json:"weight,omitempty" toml:"weight,omitempty" yaml:"weight,omitempty"`
+}
+
+// SetDefaults Default values for a ServersLoadBalancer.
+func (w *WRRService) SetDefaults() {
+	defaultWeight := 1
+	w.Weight = &defaultWeight
+}
+
+// +k8s:deepcopy-gen=true
+
+// Sticky holds the sticky configuration.
+type Sticky struct {
+	Cookie *Cookie `json:"cookie,omitempty" toml:"cookie,omitempty" yaml:"cookie,omitempty"`
+}
+
+// +k8s:deepcopy-gen=true
+
+// Cookie holds the sticky configuration based on cookie.
+type Cookie struct {
+	Name     string `json:"name,omitempty" toml:"name,omitempty" yaml:"name,omitempty"`
+	Secure   bool   `json:"secure,omitempty" toml:"secure,omitempty" yaml:"secure,omitempty"`
+	HTTPOnly bool   `json:"httpOnly,omitempty" toml:"httpOnly,omitempty" yaml:"httpOnly,omitempty"`
+}
+
+// +k8s:deepcopy-gen=true
+
+// ServersLoadBalancer holds the ServersLoadBalancer configuration.
+type ServersLoadBalancer struct {
+	Sticky             *Sticky             `json:"sticky,omitempty" toml:"sticky,omitempty" yaml:"sticky,omitempty" label:"allowEmpty"`
 	Servers            []Server            `json:"servers,omitempty" toml:"servers,omitempty" yaml:"servers,omitempty" label-slice-as-struct:"server"`
 	HealthCheck        *HealthCheck        `json:"healthCheck,omitempty" toml:"healthCheck,omitempty" yaml:"healthCheck,omitempty"`
 	PassHostHeader     bool                `json:"passHostHeader" toml:"passHostHeader" yaml:"passHostHeader"`
@@ -55,7 +94,7 @@ type LoadBalancerService struct {
 }
 
 // Mergeable tells if the given service is mergeable.
-func (l *LoadBalancerService) Mergeable(loadBalancer *LoadBalancerService) bool {
+func (l *ServersLoadBalancer) Mergeable(loadBalancer *ServersLoadBalancer) bool {
 	savedServers := l.Servers
 	defer func() {
 		l.Servers = savedServers
@@ -71,8 +110,8 @@ func (l *LoadBalancerService) Mergeable(loadBalancer *LoadBalancerService) bool
 	return reflect.DeepEqual(l, loadBalancer)
 }
 
-// SetDefaults Default values for a LoadBalancerService.
-func (l *LoadBalancerService) SetDefaults() {
+// SetDefaults Default values for a ServersLoadBalancer.
+func (l *ServersLoadBalancer) SetDefaults() {
 	l.PassHostHeader = true
 }
 
@@ -85,15 +124,6 @@ type ResponseForwarding struct {
 
 // +k8s:deepcopy-gen=true
 
-// Stickiness holds the stickiness configuration.
-type Stickiness struct {
-	CookieName     string `json:"cookieName,omitempty" toml:"cookieName,omitempty" yaml:"cookieName,omitempty"`
-	SecureCookie   bool   `json:"secureCookie,omitempty" toml:"secureCookie,omitempty" yaml:"secureCookie,omitempty"`
-	HTTPOnlyCookie bool   `json:"httpOnlyCookie,omitempty" toml:"httpOnlyCookie,omitempty" yaml:"httpOnlyCookie,omitempty"`
-}
-
-// +k8s:deepcopy-gen=true
-
 // Server holds the server configuration.
 type Server struct {
 	URL    string `json:"url,omitempty" toml:"url,omitempty" yaml:"url,omitempty" label:"-"`
diff --git a/pkg/config/dynamic/tcp_config.go b/pkg/config/dynamic/tcp_config.go
index 3e1c1e826..e3e42ab68 100644
--- a/pkg/config/dynamic/tcp_config.go
+++ b/pkg/config/dynamic/tcp_config.go
@@ -45,7 +45,7 @@ type RouterTCPTLSConfig struct {
 
 // TCPLoadBalancerService holds the LoadBalancerService configuration.
 type TCPLoadBalancerService struct {
-	Servers []TCPServer `json:"servers,omitempty" toml:"servers,omitempty" yaml:"servers,omitempty" label-slice-as-struct:"server" label-slice-as-struct:"server"`
+	Servers []TCPServer `json:"servers,omitempty" toml:"servers,omitempty" yaml:"servers,omitempty" label-slice-as-struct:"server"`
 }
 
 // Mergeable tells if the given service is mergeable.
diff --git a/pkg/config/dynamic/zz_generated.deepcopy.go b/pkg/config/dynamic/zz_generated.deepcopy.go
index 09f24794e..454015d9c 100644
--- a/pkg/config/dynamic/zz_generated.deepcopy.go
+++ b/pkg/config/dynamic/zz_generated.deepcopy.go
@@ -247,6 +247,22 @@ func (in Configurations) DeepCopy() Configurations {
 	return *out
 }
 
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *Cookie) DeepCopyInto(out *Cookie) {
+	*out = *in
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Cookie.
+func (in *Cookie) DeepCopy() *Cookie {
+	if in == nil {
+		return nil
+	}
+	out := new(Cookie)
+	in.DeepCopyInto(out)
+	return out
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *DigestAuth) DeepCopyInto(out *DigestAuth) {
 	*out = *in
@@ -508,42 +524,6 @@ func (in *IPWhiteList) DeepCopy() *IPWhiteList {
 	return out
 }
 
-// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
-func (in *LoadBalancerService) DeepCopyInto(out *LoadBalancerService) {
-	*out = *in
-	if in.Stickiness != nil {
-		in, out := &in.Stickiness, &out.Stickiness
-		*out = new(Stickiness)
-		**out = **in
-	}
-	if in.Servers != nil {
-		in, out := &in.Servers, &out.Servers
-		*out = make([]Server, len(*in))
-		copy(*out, *in)
-	}
-	if in.HealthCheck != nil {
-		in, out := &in.HealthCheck, &out.HealthCheck
-		*out = new(HealthCheck)
-		(*in).DeepCopyInto(*out)
-	}
-	if in.ResponseForwarding != nil {
-		in, out := &in.ResponseForwarding, &out.ResponseForwarding
-		*out = new(ResponseForwarding)
-		**out = **in
-	}
-	return
-}
-
-// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LoadBalancerService.
-func (in *LoadBalancerService) DeepCopy() *LoadBalancerService {
-	if in == nil {
-		return nil
-	}
-	out := new(LoadBalancerService)
-	in.DeepCopyInto(out)
-	return out
-}
-
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *MaxConn) DeepCopyInto(out *MaxConn) {
 	*out = *in
@@ -954,12 +934,53 @@ func (in *Server) DeepCopy() *Server {
 	return out
 }
 
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ServersLoadBalancer) DeepCopyInto(out *ServersLoadBalancer) {
+	*out = *in
+	if in.Sticky != nil {
+		in, out := &in.Sticky, &out.Sticky
+		*out = new(Sticky)
+		(*in).DeepCopyInto(*out)
+	}
+	if in.Servers != nil {
+		in, out := &in.Servers, &out.Servers
+		*out = make([]Server, len(*in))
+		copy(*out, *in)
+	}
+	if in.HealthCheck != nil {
+		in, out := &in.HealthCheck, &out.HealthCheck
+		*out = new(HealthCheck)
+		(*in).DeepCopyInto(*out)
+	}
+	if in.ResponseForwarding != nil {
+		in, out := &in.ResponseForwarding, &out.ResponseForwarding
+		*out = new(ResponseForwarding)
+		**out = **in
+	}
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServersLoadBalancer.
+func (in *ServersLoadBalancer) DeepCopy() *ServersLoadBalancer {
+	if in == nil {
+		return nil
+	}
+	out := new(ServersLoadBalancer)
+	in.DeepCopyInto(out)
+	return out
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *Service) DeepCopyInto(out *Service) {
 	*out = *in
 	if in.LoadBalancer != nil {
 		in, out := &in.LoadBalancer, &out.LoadBalancer
-		*out = new(LoadBalancerService)
+		*out = new(ServersLoadBalancer)
+		(*in).DeepCopyInto(*out)
+	}
+	if in.Weighted != nil {
+		in, out := &in.Weighted, &out.Weighted
+		*out = new(WeightedRoundRobin)
 		(*in).DeepCopyInto(*out)
 	}
 	return
@@ -976,17 +997,22 @@ func (in *Service) DeepCopy() *Service {
 }
 
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
-func (in *Stickiness) DeepCopyInto(out *Stickiness) {
+func (in *Sticky) DeepCopyInto(out *Sticky) {
 	*out = *in
+	if in.Cookie != nil {
+		in, out := &in.Cookie, &out.Cookie
+		*out = new(Cookie)
+		**out = **in
+	}
 	return
 }
 
-// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Stickiness.
-func (in *Stickiness) DeepCopy() *Stickiness {
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Sticky.
+func (in *Sticky) DeepCopy() *Sticky {
 	if in == nil {
 		return nil
 	}
-	out := new(Stickiness)
+	out := new(Sticky)
 	in.DeepCopyInto(out)
 	return out
 }
@@ -1265,3 +1291,45 @@ func (in Users) DeepCopy() Users {
 	in.DeepCopyInto(out)
 	return *out
 }
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *WRRService) DeepCopyInto(out *WRRService) {
+	*out = *in
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WRRService.
+func (in *WRRService) DeepCopy() *WRRService {
+	if in == nil {
+		return nil
+	}
+	out := new(WRRService)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *WeightedRoundRobin) DeepCopyInto(out *WeightedRoundRobin) {
+	*out = *in
+	if in.Services != nil {
+		in, out := &in.Services, &out.Services
+		*out = make([]WRRService, len(*in))
+		copy(*out, *in)
+	}
+	if in.Sticky != nil {
+		in, out := &in.Sticky, &out.Sticky
+		*out = new(Sticky)
+		(*in).DeepCopyInto(*out)
+	}
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WeightedRoundRobin.
+func (in *WeightedRoundRobin) DeepCopy() *WeightedRoundRobin {
+	if in == nil {
+		return nil
+	}
+	out := new(WeightedRoundRobin)
+	in.DeepCopyInto(out)
+	return out
+}
diff --git a/pkg/config/file/fixtures/sample.toml b/pkg/config/file/fixtures/sample.toml
index c0050f7db..94dd9e694 100644
--- a/pkg/config/file/fixtures/sample.toml
+++ b/pkg/config/file/fixtures/sample.toml
@@ -396,8 +396,8 @@
     [http.services.Service0]
       [http.services.Service0.loadBalancer]
         passHostHeader = true
-        [http.services.Service0.loadBalancer.stickiness]
-          cookieName = "foobar"
+        [http.services.Service0.loadBalancer.sticky.cookie]
+          name = "foobar"
 
         [[http.services.Service0.loadBalancer.servers]]
           url = "foobar"
diff --git a/pkg/provider/docker/config.go b/pkg/provider/docker/config.go
index 63858c17a..aa5aaf376 100644
--- a/pkg/provider/docker/config.go
+++ b/pkg/provider/docker/config.go
@@ -99,7 +99,7 @@ func (p *Provider) buildServiceConfiguration(ctx context.Context, container dock
 
 	if len(configuration.Services) == 0 {
 		configuration.Services = make(map[string]*dynamic.Service)
-		lb := &dynamic.LoadBalancerService{}
+		lb := &dynamic.ServersLoadBalancer{}
 		lb.SetDefaults()
 		configuration.Services[serviceName] = &dynamic.Service{
 			LoadBalancer: lb,
@@ -171,7 +171,7 @@ func (p *Provider) addServerTCP(ctx context.Context, container dockerData, loadB
 	return nil
 }
 
-func (p *Provider) addServer(ctx context.Context, container dockerData, loadBalancer *dynamic.LoadBalancerService) error {
+func (p *Provider) addServer(ctx context.Context, container dockerData, loadBalancer *dynamic.ServersLoadBalancer) error {
 	serverPort := getLBServerPort(loadBalancer)
 	ip, port, err := p.getIPPort(ctx, container, serverPort)
 	if err != nil {
@@ -291,7 +291,7 @@ func (p *Provider) getPortBinding(container dockerData, serverPort string) (*nat
 	return nil, fmt.Errorf("unable to find the external IP:Port for the container %q", container.Name)
 }
 
-func getLBServerPort(loadBalancer *dynamic.LoadBalancerService) string {
+func getLBServerPort(loadBalancer *dynamic.ServersLoadBalancer) string {
 	if loadBalancer != nil && len(loadBalancer.Servers) > 0 {
 		return loadBalancer.Servers[0].Port
 	}
diff --git a/pkg/provider/file/fixtures/yaml/template_file.yml b/pkg/provider/file/fixtures/yaml/template_file.yml
index 66903fbc8..f427dac71 100644
--- a/pkg/provider/file/fixtures/yaml/template_file.yml
+++ b/pkg/provider/file/fixtures/yaml/template_file.yml
@@ -1,6 +1,6 @@
 http:
-{{ range $i, $e := until 20 }}
   routers:
+  {{ range $i, $e := until 20 }}
     router{{ $e }}:
       service: application-1
-{{ end }}
\ No newline at end of file
+  {{ end }}
\ No newline at end of file
diff --git a/pkg/provider/kubernetes/crd/fixtures/with_two_services_weight.yml b/pkg/provider/kubernetes/crd/fixtures/with_two_services_weight.yml
new file mode 100644
index 000000000..76a5c7c3e
--- /dev/null
+++ b/pkg/provider/kubernetes/crd/fixtures/with_two_services_weight.yml
@@ -0,0 +1,22 @@
+apiVersion: traefik.containo.us/v1alpha1
+kind: IngressRoute
+metadata:
+  name: test.route
+  namespace: default
+
+spec:
+  entryPoints:
+    - web
+
+  routes:
+  - match: Host(`foo.com`) && PathPrefix(`/foo`)
+    kind: Rule
+    priority: 12
+    services:
+    - name: whoami
+      port: 80
+      weight: 10
+    - name: whoami2
+      port: 8080
+      weight: 0
+
diff --git a/pkg/provider/kubernetes/crd/kubernetes.go b/pkg/provider/kubernetes/crd/kubernetes.go
index 9e3726927..f3307ab8d 100644
--- a/pkg/provider/kubernetes/crd/kubernetes.go
+++ b/pkg/provider/kubernetes/crd/kubernetes.go
@@ -3,7 +3,6 @@ package crd
 import (
 	"context"
 	"crypto/sha256"
-	"errors"
 	"fmt"
 	"os"
 	"reflect"
@@ -16,7 +15,6 @@ import (
 	"github.com/containous/traefik/v2/pkg/config/dynamic"
 	"github.com/containous/traefik/v2/pkg/job"
 	"github.com/containous/traefik/v2/pkg/log"
-	"github.com/containous/traefik/v2/pkg/provider/kubernetes/crd/traefik/v1alpha1"
 	"github.com/containous/traefik/v2/pkg/safe"
 	"github.com/containous/traefik/v2/pkg/tls"
 	corev1 "k8s.io/api/core/v1"
@@ -136,159 +134,22 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
 	return nil
 }
 
-func checkStringQuoteValidity(value string) error {
-	_, err := strconv.Unquote(`"` + value + `"`)
-	return err
-}
-
-func loadTCPServers(client Client, namespace string, svc v1alpha1.ServiceTCP) ([]dynamic.TCPServer, error) {
-	service, exists, err := client.GetService(namespace, svc.Name)
-	if err != nil {
-		return nil, err
-	}
-
-	if !exists {
-		return nil, errors.New("service not found")
-	}
-
-	var portSpec *corev1.ServicePort
-	for _, p := range service.Spec.Ports {
-		if svc.Port == p.Port {
-			portSpec = &p
-			break
-		}
-	}
-
-	if portSpec == nil {
-		return nil, errors.New("service port not found")
-	}
-
-	var servers []dynamic.TCPServer
-	if service.Spec.Type == corev1.ServiceTypeExternalName {
-		servers = append(servers, dynamic.TCPServer{
-			Address: fmt.Sprintf("%s:%d", service.Spec.ExternalName, portSpec.Port),
-		})
-	} else {
-		endpoints, endpointsExists, endpointsErr := client.GetEndpoints(namespace, svc.Name)
-		if endpointsErr != nil {
-			return nil, endpointsErr
-		}
-
-		if !endpointsExists {
-			return nil, errors.New("endpoints not found")
-		}
-
-		if len(endpoints.Subsets) == 0 {
-			return nil, errors.New("subset not found")
-		}
-
-		var port int32
-		for _, subset := range endpoints.Subsets {
-			for _, p := range subset.Ports {
-				if portSpec.Name == p.Name {
-					port = p.Port
-					break
-				}
-			}
-
-			if port == 0 {
-				return nil, errors.New("cannot define a port")
-			}
-
-			for _, addr := range subset.Addresses {
-				servers = append(servers, dynamic.TCPServer{
-					Address: fmt.Sprintf("%s:%d", addr.IP, port),
-				})
-			}
-		}
-	}
-
-	return servers, nil
-}
-
-func loadServers(client Client, namespace string, svc v1alpha1.Service) ([]dynamic.Server, error) {
-	strategy := svc.Strategy
-	if strategy == "" {
-		strategy = "RoundRobin"
-	}
-	if strategy != "RoundRobin" {
-		return nil, fmt.Errorf("load balancing strategy %v is not supported", strategy)
-	}
-
-	service, exists, err := client.GetService(namespace, svc.Name)
-	if err != nil {
-		return nil, err
-	}
-
-	if !exists {
-		return nil, errors.New("service not found")
-	}
-
-	var portSpec *corev1.ServicePort
-	for _, p := range service.Spec.Ports {
-		if svc.Port == p.Port {
-			portSpec = &p
-			break
-		}
-	}
-
-	if portSpec == nil {
-		return nil, errors.New("service port not found")
+func (p *Provider) loadConfigurationFromCRD(ctx context.Context, client Client) *dynamic.Configuration {
+	tlsConfigs := make(map[string]*tls.CertAndStores)
+	conf := &dynamic.Configuration{
+		HTTP: p.loadIngressRouteConfiguration(ctx, client, tlsConfigs),
+		TCP:  p.loadIngressRouteTCPConfiguration(ctx, client, tlsConfigs),
+		TLS: &dynamic.TLSConfiguration{
+			Certificates: getTLSConfig(tlsConfigs),
+			Options:      buildTLSOptions(ctx, client),
+		},
 	}
 
-	var servers []dynamic.Server
-	if service.Spec.Type == corev1.ServiceTypeExternalName {
-		servers = append(servers, dynamic.Server{
-			URL: fmt.Sprintf("http://%s:%d", service.Spec.ExternalName, portSpec.Port),
-		})
-	} else {
-		endpoints, endpointsExists, endpointsErr := client.GetEndpoints(namespace, svc.Name)
-		if endpointsErr != nil {
-			return nil, endpointsErr
-		}
-
-		if !endpointsExists {
-			return nil, errors.New("endpoints not found")
-		}
-
-		if len(endpoints.Subsets) == 0 {
-			return nil, errors.New("subset not found")
-		}
-
-		var port int32
-		for _, subset := range endpoints.Subsets {
-			for _, p := range subset.Ports {
-				if portSpec.Name == p.Name {
-					port = p.Port
-					break
-				}
-			}
-
-			if port == 0 {
-				return nil, errors.New("cannot define a port")
-			}
-
-			protocol := "http"
-			switch svc.Scheme {
-			case "http", "https", "h2c":
-				protocol = svc.Scheme
-			case "":
-				if portSpec.Port == 443 || strings.HasPrefix(portSpec.Name, "https") {
-					protocol = "https"
-				}
-			default:
-				return nil, fmt.Errorf("invalid scheme %q specified", svc.Scheme)
-			}
-
-			for _, addr := range subset.Addresses {
-				servers = append(servers, dynamic.Server{
-					URL: fmt.Sprintf("%s://%s:%d", protocol, addr.IP, port),
-				})
-			}
-		}
+	for _, middleware := range client.GetMiddlewares() {
+		conf.HTTP.Middlewares[makeID(middleware.Namespace, middleware.Name)] = &middleware.Spec
 	}
 
-	return servers, nil
+	return conf
 }
 
 func buildTLSOptions(ctx context.Context, client Client) map[string]tls.Options {
@@ -338,250 +199,9 @@ func buildTLSOptions(ctx context.Context, client Client) map[string]tls.Options
 	return tlsOptions
 }
 
-func (p *Provider) loadIngressRouteConfiguration(ctx context.Context, client Client, tlsConfigs map[string]*tls.CertAndStores) *dynamic.HTTPConfiguration {
-	conf := &dynamic.HTTPConfiguration{
-		Routers:     map[string]*dynamic.Router{},
-		Middlewares: map[string]*dynamic.Middleware{},
-		Services:    map[string]*dynamic.Service{},
-	}
-
-	for _, ingressRoute := range client.GetIngressRoutes() {
-		logger := log.FromContext(log.With(ctx, log.Str("ingress", ingressRoute.Name), log.Str("namespace", ingressRoute.Namespace)))
-
-		// TODO keep the name ingressClass?
-		if !shouldProcessIngress(p.IngressClass, ingressRoute.Annotations[annotationKubernetesIngressClass]) {
-			continue
-		}
-
-		err := getTLSHTTP(ctx, ingressRoute, client, tlsConfigs)
-		if err != nil {
-			logger.Errorf("Error configuring TLS: %v", err)
-		}
-
-		ingressName := ingressRoute.Name
-		if len(ingressName) == 0 {
-			ingressName = ingressRoute.GenerateName
-		}
-
-		for _, route := range ingressRoute.Spec.Routes {
-			if route.Kind != "Rule" {
-				logger.Errorf("Unsupported match kind: %s. Only \"Rule\" is supported for now.", route.Kind)
-				continue
-			}
-
-			if len(route.Match) == 0 {
-				logger.Errorf("Empty match rule")
-				continue
-			}
-
-			if err := checkStringQuoteValidity(route.Match); err != nil {
-				logger.Errorf("Invalid syntax for match rule: %s", route.Match)
-				continue
-			}
-
-			var allServers []dynamic.Server
-			for _, service := range route.Services {
-				servers, err := loadServers(client, ingressRoute.Namespace, service)
-				if err != nil {
-					logger.
-						WithField("serviceName", service.Name).
-						WithField("servicePort", service.Port).
-						Errorf("Cannot create service: %v", err)
-					continue
-				}
-
-				allServers = append(allServers, servers...)
-			}
-
-			var mds []string
-			for _, mi := range route.Middlewares {
-				if strings.Contains(mi.Name, "@") {
-					if len(mi.Namespace) > 0 {
-						logger.
-							WithField(log.MiddlewareName, mi.Name).
-							Warnf("namespace %q is ignored in cross-provider context", mi.Namespace)
-					}
-					mds = append(mds, mi.Name)
-					continue
-				}
-
-				ns := mi.Namespace
-				if len(ns) == 0 {
-					ns = ingressRoute.Namespace
-				}
-				mds = append(mds, makeID(ns, mi.Name))
-			}
-
-			key, err := makeServiceKey(route.Match, ingressName)
-			if err != nil {
-				logger.Error(err)
-				continue
-			}
-
-			serviceName := makeID(ingressRoute.Namespace, key)
-
-			conf.Routers[serviceName] = &dynamic.Router{
-				Middlewares: mds,
-				Priority:    route.Priority,
-				EntryPoints: ingressRoute.Spec.EntryPoints,
-				Rule:        route.Match,
-				Service:     serviceName,
-			}
-
-			if ingressRoute.Spec.TLS != nil {
-				tlsConf := &dynamic.RouterTLSConfig{
-					CertResolver: ingressRoute.Spec.TLS.CertResolver,
-				}
-
-				if ingressRoute.Spec.TLS.Options != nil && len(ingressRoute.Spec.TLS.Options.Name) > 0 {
-					tlsOptionsName := ingressRoute.Spec.TLS.Options.Name
-					// Is a Kubernetes CRD reference, (i.e. not a cross-provider reference)
-					ns := ingressRoute.Spec.TLS.Options.Namespace
-					if !strings.Contains(tlsOptionsName, "@") {
-						if len(ns) == 0 {
-							ns = ingressRoute.Namespace
-						}
-						tlsOptionsName = makeID(ns, tlsOptionsName)
-					} else if len(ns) > 0 {
-						logger.
-							WithField("TLSoptions", ingressRoute.Spec.TLS.Options.Name).
-							Warnf("namespace %q is ignored in cross-provider context", ns)
-					}
-
-					tlsConf.Options = tlsOptionsName
-				}
-				conf.Routers[serviceName].TLS = tlsConf
-			}
-
-			conf.Services[serviceName] = &dynamic.Service{
-				LoadBalancer: &dynamic.LoadBalancerService{
-					Servers: allServers,
-					// TODO: support other strategies.
-					PassHostHeader: true,
-				},
-			}
-		}
-	}
-
-	return conf
-}
-
-func (p *Provider) loadIngressRouteTCPConfiguration(ctx context.Context, client Client, tlsConfigs map[string]*tls.CertAndStores) *dynamic.TCPConfiguration {
-	conf := &dynamic.TCPConfiguration{
-		Routers:  map[string]*dynamic.TCPRouter{},
-		Services: map[string]*dynamic.TCPService{},
-	}
-
-	for _, ingressRouteTCP := range client.GetIngressRouteTCPs() {
-		logger := log.FromContext(log.With(ctx, log.Str("ingress", ingressRouteTCP.Name), log.Str("namespace", ingressRouteTCP.Namespace)))
-
-		if !shouldProcessIngress(p.IngressClass, ingressRouteTCP.Annotations[annotationKubernetesIngressClass]) {
-			continue
-		}
-
-		if ingressRouteTCP.Spec.TLS != nil && !ingressRouteTCP.Spec.TLS.Passthrough {
-			err := getTLSTCP(ctx, ingressRouteTCP, client, tlsConfigs)
-			if err != nil {
-				logger.Errorf("Error configuring TLS: %v", err)
-			}
-		}
-
-		ingressName := ingressRouteTCP.Name
-		if len(ingressName) == 0 {
-			ingressName = ingressRouteTCP.GenerateName
-		}
-
-		for _, route := range ingressRouteTCP.Spec.Routes {
-			if len(route.Match) == 0 {
-				logger.Errorf("Empty match rule")
-				continue
-			}
-
-			if err := checkStringQuoteValidity(route.Match); err != nil {
-				logger.Errorf("Invalid syntax for match rule: %s", route.Match)
-				continue
-			}
-
-			var allServers []dynamic.TCPServer
-			for _, service := range route.Services {
-				servers, err := loadTCPServers(client, ingressRouteTCP.Namespace, service)
-				if err != nil {
-					logger.
-						WithField("serviceName", service.Name).
-						WithField("servicePort", service.Port).
-						Errorf("Cannot create service: %v", err)
-					continue
-				}
-
-				allServers = append(allServers, servers...)
-			}
-
-			key, e := makeServiceKey(route.Match, ingressName)
-			if e != nil {
-				logger.Error(e)
-				continue
-			}
-
-			serviceName := makeID(ingressRouteTCP.Namespace, key)
-			conf.Routers[serviceName] = &dynamic.TCPRouter{
-				EntryPoints: ingressRouteTCP.Spec.EntryPoints,
-				Rule:        route.Match,
-				Service:     serviceName,
-			}
-
-			if ingressRouteTCP.Spec.TLS != nil {
-				conf.Routers[serviceName].TLS = &dynamic.RouterTCPTLSConfig{
-					Passthrough:  ingressRouteTCP.Spec.TLS.Passthrough,
-					CertResolver: ingressRouteTCP.Spec.TLS.CertResolver,
-				}
-
-				if ingressRouteTCP.Spec.TLS.Options != nil && len(ingressRouteTCP.Spec.TLS.Options.Name) > 0 {
-					tlsOptionsName := ingressRouteTCP.Spec.TLS.Options.Name
-					// Is a Kubernetes CRD reference (i.e. not a cross-provider reference)
-					ns := ingressRouteTCP.Spec.TLS.Options.Namespace
-					if !strings.Contains(tlsOptionsName, "@") {
-						if len(ns) == 0 {
-							ns = ingressRouteTCP.Namespace
-						}
-						tlsOptionsName = makeID(ns, tlsOptionsName)
-					} else if len(ns) > 0 {
-						logger.
-							WithField("TLSoptions", ingressRouteTCP.Spec.TLS.Options.Name).
-							Warnf("namespace %q is ignored in cross-provider context", ns)
-					}
-
-					conf.Routers[serviceName].TLS.Options = tlsOptionsName
-
-				}
-			}
-
-			conf.Services[serviceName] = &dynamic.TCPService{
-				LoadBalancer: &dynamic.TCPLoadBalancerService{
-					Servers: allServers,
-				},
-			}
-		}
-	}
-
-	return conf
-}
-
-func (p *Provider) loadConfigurationFromCRD(ctx context.Context, client Client) *dynamic.Configuration {
-	tlsConfigs := make(map[string]*tls.CertAndStores)
-	conf := &dynamic.Configuration{
-		HTTP: p.loadIngressRouteConfiguration(ctx, client, tlsConfigs),
-		TCP:  p.loadIngressRouteTCPConfiguration(ctx, client, tlsConfigs),
-		TLS: &dynamic.TLSConfiguration{
-			Certificates: getTLSConfig(tlsConfigs),
-			Options:      buildTLSOptions(ctx, client),
-		},
-	}
-
-	for _, middleware := range client.GetMiddlewares() {
-		conf.HTTP.Middlewares[makeID(middleware.Namespace, middleware.Name)] = &middleware.Spec
-	}
-
-	return conf
+func checkStringQuoteValidity(value string) error {
+	_, err := strconv.Unquote(`"` + value + `"`)
+	return err
 }
 
 func makeServiceKey(rule, ingressName string) (string, error) {
@@ -608,50 +228,6 @@ func shouldProcessIngress(ingressClass string, ingressClassAnnotation string) bo
 		(len(ingressClass) == 0 && ingressClassAnnotation == traefikDefaultIngressClass)
 }
 
-func getTLSHTTP(ctx context.Context, ingressRoute *v1alpha1.IngressRoute, k8sClient Client, tlsConfigs map[string]*tls.CertAndStores) error {
-	if ingressRoute.Spec.TLS == nil {
-		return nil
-	}
-	if ingressRoute.Spec.TLS.SecretName == "" {
-		log.FromContext(ctx).Debugf("Skipping TLS sub-section: No secret name provided")
-		return nil
-	}
-
-	configKey := ingressRoute.Namespace + "/" + ingressRoute.Spec.TLS.SecretName
-	if _, tlsExists := tlsConfigs[configKey]; !tlsExists {
-		tlsConf, err := getTLS(k8sClient, ingressRoute.Spec.TLS.SecretName, ingressRoute.Namespace)
-		if err != nil {
-			return err
-		}
-
-		tlsConfigs[configKey] = tlsConf
-	}
-
-	return nil
-}
-
-func getTLSTCP(ctx context.Context, ingressRoute *v1alpha1.IngressRouteTCP, k8sClient Client, tlsConfigs map[string]*tls.CertAndStores) error {
-	if ingressRoute.Spec.TLS == nil {
-		return nil
-	}
-	if ingressRoute.Spec.TLS.SecretName == "" {
-		log.FromContext(ctx).Debugf("Skipping TLS sub-section for TCP: No secret name provided")
-		return nil
-	}
-
-	configKey := ingressRoute.Namespace + "/" + ingressRoute.Spec.TLS.SecretName
-	if _, tlsExists := tlsConfigs[configKey]; !tlsExists {
-		tlsConf, err := getTLS(k8sClient, ingressRoute.Spec.TLS.SecretName, ingressRoute.Namespace)
-		if err != nil {
-			return err
-		}
-
-		tlsConfigs[configKey] = tlsConf
-	}
-
-	return nil
-}
-
 func getTLS(k8sClient Client, secretName, namespace string) (*tls.CertAndStores, error) {
 	secret, exists, err := k8sClient.GetSecret(namespace, secretName)
 	if err != nil {
diff --git a/pkg/provider/kubernetes/crd/kubernetes_http.go b/pkg/provider/kubernetes/crd/kubernetes_http.go
new file mode 100644
index 000000000..52d18dd30
--- /dev/null
+++ b/pkg/provider/kubernetes/crd/kubernetes_http.go
@@ -0,0 +1,274 @@
+package crd
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"strings"
+
+	"github.com/containous/traefik/v2/pkg/config/dynamic"
+	"github.com/containous/traefik/v2/pkg/log"
+	"github.com/containous/traefik/v2/pkg/provider/kubernetes/crd/traefik/v1alpha1"
+	"github.com/containous/traefik/v2/pkg/tls"
+	corev1 "k8s.io/api/core/v1"
+)
+
+func (p *Provider) loadIngressRouteConfiguration(ctx context.Context, client Client, tlsConfigs map[string]*tls.CertAndStores) *dynamic.HTTPConfiguration {
+	conf := &dynamic.HTTPConfiguration{
+		Routers:     map[string]*dynamic.Router{},
+		Middlewares: map[string]*dynamic.Middleware{},
+		Services:    map[string]*dynamic.Service{},
+	}
+
+	for _, ingressRoute := range client.GetIngressRoutes() {
+		ctxRt := log.With(ctx, log.Str("ingress", ingressRoute.Name), log.Str("namespace", ingressRoute.Namespace))
+		logger := log.FromContext(ctxRt)
+
+		// TODO keep the name ingressClass?
+		if !shouldProcessIngress(p.IngressClass, ingressRoute.Annotations[annotationKubernetesIngressClass]) {
+			continue
+		}
+
+		err := getTLSHTTP(ctx, ingressRoute, client, tlsConfigs)
+		if err != nil {
+			logger.Errorf("Error configuring TLS: %v", err)
+		}
+
+		ingressName := ingressRoute.Name
+		if len(ingressName) == 0 {
+			ingressName = ingressRoute.GenerateName
+		}
+
+		for _, route := range ingressRoute.Spec.Routes {
+			if route.Kind != "Rule" {
+				logger.Errorf("Unsupported match kind: %s. Only \"Rule\" is supported for now.", route.Kind)
+				continue
+			}
+
+			if len(route.Match) == 0 {
+				logger.Errorf("Empty match rule")
+				continue
+			}
+
+			if err := checkStringQuoteValidity(route.Match); err != nil {
+				logger.Errorf("Invalid syntax for match rule: %s", route.Match)
+				continue
+			}
+
+			key, err := makeServiceKey(route.Match, ingressName)
+			if err != nil {
+				logger.Error(err)
+				continue
+			}
+
+			serviceName := makeID(ingressRoute.Namespace, key)
+
+			for _, service := range route.Services {
+				balancerServerHTTP, err := createLoadBalancerServerHTTP(client, ingressRoute, service)
+				if err != nil {
+					logger.
+						WithField("serviceName", service.Name).
+						WithField("servicePort", service.Port).
+						Errorf("Cannot create service: %v", err)
+					continue
+				}
+
+				if len(route.Services) == 1 {
+					conf.Services[serviceName] = balancerServerHTTP
+					break
+				}
+
+				serviceKey := fmt.Sprintf("%s-%s-%d", serviceName, service.Name, service.Port)
+				conf.Services[serviceKey] = balancerServerHTTP
+
+				srv := dynamic.WRRService{Name: serviceKey}
+				srv.SetDefaults()
+				if service.Weight != nil {
+					srv.Weight = service.Weight
+				}
+
+				if conf.Services[serviceName] == nil {
+					conf.Services[serviceName] = &dynamic.Service{Weighted: &dynamic.WeightedRoundRobin{}}
+				}
+				conf.Services[serviceName].Weighted.Services = append(conf.Services[serviceName].Weighted.Services, srv)
+			}
+
+			var mds []string
+			for _, mi := range route.Middlewares {
+				if strings.Contains(mi.Name, "@") {
+					if len(mi.Namespace) > 0 {
+						logger.
+							WithField(log.MiddlewareName, mi.Name).
+							Warnf("namespace %q is ignored in cross-provider context", mi.Namespace)
+					}
+					mds = append(mds, mi.Name)
+					continue
+				}
+
+				ns := mi.Namespace
+				if len(ns) == 0 {
+					ns = ingressRoute.Namespace
+				}
+				mds = append(mds, makeID(ns, mi.Name))
+			}
+
+			conf.Routers[serviceName] = &dynamic.Router{
+				Middlewares: mds,
+				Priority:    route.Priority,
+				EntryPoints: ingressRoute.Spec.EntryPoints,
+				Rule:        route.Match,
+				Service:     serviceName,
+			}
+
+			if ingressRoute.Spec.TLS != nil {
+				tlsConf := &dynamic.RouterTLSConfig{
+					CertResolver: ingressRoute.Spec.TLS.CertResolver,
+				}
+
+				if ingressRoute.Spec.TLS.Options != nil && len(ingressRoute.Spec.TLS.Options.Name) > 0 {
+					tlsOptionsName := ingressRoute.Spec.TLS.Options.Name
+					// Is a Kubernetes CRD reference, (i.e. not a cross-provider reference)
+					ns := ingressRoute.Spec.TLS.Options.Namespace
+					if !strings.Contains(tlsOptionsName, "@") {
+						if len(ns) == 0 {
+							ns = ingressRoute.Namespace
+						}
+						tlsOptionsName = makeID(ns, tlsOptionsName)
+					} else if len(ns) > 0 {
+						logger.
+							WithField("TLSoptions", ingressRoute.Spec.TLS.Options.Name).
+							Warnf("namespace %q is ignored in cross-provider context", ns)
+					}
+
+					tlsConf.Options = tlsOptionsName
+				}
+				conf.Routers[serviceName].TLS = tlsConf
+			}
+
+		}
+	}
+
+	return conf
+}
+
+func createLoadBalancerServerHTTP(client Client, ingressRoute *v1alpha1.IngressRoute, service v1alpha1.Service) (*dynamic.Service, error) {
+	servers, err := loadServers(client, ingressRoute.Namespace, service)
+	if err != nil {
+		return nil, err
+	}
+
+	return &dynamic.Service{
+		LoadBalancer: &dynamic.ServersLoadBalancer{
+			Servers: servers,
+			// TODO: support other strategies.
+			PassHostHeader: true,
+		},
+	}, nil
+}
+
+func loadServers(client Client, namespace string, svc v1alpha1.Service) ([]dynamic.Server, error) {
+	strategy := svc.Strategy
+	if strategy == "" {
+		strategy = "RoundRobin"
+	}
+	if strategy != "RoundRobin" {
+		return nil, fmt.Errorf("load balancing strategy %v is not supported", strategy)
+	}
+
+	service, exists, err := client.GetService(namespace, svc.Name)
+	if err != nil {
+		return nil, err
+	}
+
+	if !exists {
+		return nil, errors.New("service not found")
+	}
+
+	var portSpec *corev1.ServicePort
+	for _, p := range service.Spec.Ports {
+		if svc.Port == p.Port {
+			portSpec = &p
+			break
+		}
+	}
+
+	if portSpec == nil {
+		return nil, errors.New("service port not found")
+	}
+
+	var servers []dynamic.Server
+	if service.Spec.Type == corev1.ServiceTypeExternalName {
+		servers = append(servers, dynamic.Server{
+			URL: fmt.Sprintf("http://%s:%d", service.Spec.ExternalName, portSpec.Port),
+		})
+	} else {
+		endpoints, endpointsExists, endpointsErr := client.GetEndpoints(namespace, svc.Name)
+		if endpointsErr != nil {
+			return nil, endpointsErr
+		}
+
+		if !endpointsExists {
+			return nil, errors.New("endpoints not found")
+		}
+
+		if len(endpoints.Subsets) == 0 {
+			return nil, errors.New("subset not found")
+		}
+
+		var port int32
+		for _, subset := range endpoints.Subsets {
+			for _, p := range subset.Ports {
+				if portSpec.Name == p.Name {
+					port = p.Port
+					break
+				}
+			}
+
+			if port == 0 {
+				return nil, errors.New("cannot define a port")
+			}
+
+			protocol := "http"
+			switch svc.Scheme {
+			case "http", "https", "h2c":
+				protocol = svc.Scheme
+			case "":
+				if portSpec.Port == 443 || strings.HasPrefix(portSpec.Name, "https") {
+					protocol = "https"
+				}
+			default:
+				return nil, fmt.Errorf("invalid scheme %q specified", svc.Scheme)
+			}
+
+			for _, addr := range subset.Addresses {
+				servers = append(servers, dynamic.Server{
+					URL: fmt.Sprintf("%s://%s:%d", protocol, addr.IP, port),
+				})
+			}
+		}
+	}
+
+	return servers, nil
+}
+
+func getTLSHTTP(ctx context.Context, ingressRoute *v1alpha1.IngressRoute, k8sClient Client, tlsConfigs map[string]*tls.CertAndStores) error {
+	if ingressRoute.Spec.TLS == nil {
+		return nil
+	}
+	if ingressRoute.Spec.TLS.SecretName == "" {
+		log.FromContext(ctx).Debugf("No secret name provided")
+		return nil
+	}
+
+	configKey := ingressRoute.Namespace + "/" + ingressRoute.Spec.TLS.SecretName
+	if _, tlsExists := tlsConfigs[configKey]; !tlsExists {
+		tlsConf, err := getTLS(k8sClient, ingressRoute.Spec.TLS.SecretName, ingressRoute.Namespace)
+		if err != nil {
+			return err
+		}
+
+		tlsConfigs[configKey] = tlsConf
+	}
+
+	return nil
+}
diff --git a/pkg/provider/kubernetes/crd/kubernetes_tcp.go b/pkg/provider/kubernetes/crd/kubernetes_tcp.go
new file mode 100644
index 000000000..ea64d89ca
--- /dev/null
+++ b/pkg/provider/kubernetes/crd/kubernetes_tcp.go
@@ -0,0 +1,201 @@
+package crd
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"strings"
+
+	"github.com/containous/traefik/v2/pkg/config/dynamic"
+	"github.com/containous/traefik/v2/pkg/log"
+	"github.com/containous/traefik/v2/pkg/provider/kubernetes/crd/traefik/v1alpha1"
+	"github.com/containous/traefik/v2/pkg/tls"
+	corev1 "k8s.io/api/core/v1"
+)
+
+func (p *Provider) loadIngressRouteTCPConfiguration(ctx context.Context, client Client, tlsConfigs map[string]*tls.CertAndStores) *dynamic.TCPConfiguration {
+	conf := &dynamic.TCPConfiguration{
+		Routers:  map[string]*dynamic.TCPRouter{},
+		Services: map[string]*dynamic.TCPService{},
+	}
+
+	for _, ingressRouteTCP := range client.GetIngressRouteTCPs() {
+		logger := log.FromContext(log.With(ctx, log.Str("ingress", ingressRouteTCP.Name), log.Str("namespace", ingressRouteTCP.Namespace)))
+
+		if !shouldProcessIngress(p.IngressClass, ingressRouteTCP.Annotations[annotationKubernetesIngressClass]) {
+			continue
+		}
+
+		if ingressRouteTCP.Spec.TLS != nil && !ingressRouteTCP.Spec.TLS.Passthrough {
+			err := getTLSTCP(ctx, ingressRouteTCP, client, tlsConfigs)
+			if err != nil {
+				logger.Errorf("Error configuring TLS: %v", err)
+			}
+		}
+
+		ingressName := ingressRouteTCP.Name
+		if len(ingressName) == 0 {
+			ingressName = ingressRouteTCP.GenerateName
+		}
+
+		for _, route := range ingressRouteTCP.Spec.Routes {
+			if len(route.Match) == 0 {
+				logger.Errorf("Empty match rule")
+				continue
+			}
+
+			if err := checkStringQuoteValidity(route.Match); err != nil {
+				logger.Errorf("Invalid syntax for match rule: %s", route.Match)
+				continue
+			}
+
+			var allServers []dynamic.TCPServer
+			for _, service := range route.Services {
+				servers, err := loadTCPServers(client, ingressRouteTCP.Namespace, service)
+				if err != nil {
+					logger.
+						WithField("serviceName", service.Name).
+						WithField("servicePort", service.Port).
+						Errorf("Cannot create service: %v", err)
+					continue
+				}
+
+				allServers = append(allServers, servers...)
+			}
+
+			key, e := makeServiceKey(route.Match, ingressName)
+			if e != nil {
+				logger.Error(e)
+				continue
+			}
+
+			serviceName := makeID(ingressRouteTCP.Namespace, key)
+			conf.Routers[serviceName] = &dynamic.TCPRouter{
+				EntryPoints: ingressRouteTCP.Spec.EntryPoints,
+				Rule:        route.Match,
+				Service:     serviceName,
+			}
+
+			if ingressRouteTCP.Spec.TLS != nil {
+				conf.Routers[serviceName].TLS = &dynamic.RouterTCPTLSConfig{
+					Passthrough:  ingressRouteTCP.Spec.TLS.Passthrough,
+					CertResolver: ingressRouteTCP.Spec.TLS.CertResolver,
+				}
+
+				if ingressRouteTCP.Spec.TLS.Options != nil && len(ingressRouteTCP.Spec.TLS.Options.Name) > 0 {
+					tlsOptionsName := ingressRouteTCP.Spec.TLS.Options.Name
+					// Is a Kubernetes CRD reference (i.e. not a cross-provider reference)
+					ns := ingressRouteTCP.Spec.TLS.Options.Namespace
+					if !strings.Contains(tlsOptionsName, "@") {
+						if len(ns) == 0 {
+							ns = ingressRouteTCP.Namespace
+						}
+						tlsOptionsName = makeID(ns, tlsOptionsName)
+					} else if len(ns) > 0 {
+						logger.
+							WithField("TLSoptions", ingressRouteTCP.Spec.TLS.Options.Name).
+							Warnf("namespace %q is ignored in cross-provider context", ns)
+					}
+
+					conf.Routers[serviceName].TLS.Options = tlsOptionsName
+
+				}
+			}
+
+			conf.Services[serviceName] = &dynamic.TCPService{
+				LoadBalancer: &dynamic.TCPLoadBalancerService{
+					Servers: allServers,
+				},
+			}
+		}
+	}
+
+	return conf
+}
+
+func loadTCPServers(client Client, namespace string, svc v1alpha1.ServiceTCP) ([]dynamic.TCPServer, error) {
+	service, exists, err := client.GetService(namespace, svc.Name)
+	if err != nil {
+		return nil, err
+	}
+
+	if !exists {
+		return nil, errors.New("service not found")
+	}
+
+	var portSpec *corev1.ServicePort
+	for _, p := range service.Spec.Ports {
+		if svc.Port == p.Port {
+			portSpec = &p
+			break
+		}
+	}
+
+	if portSpec == nil {
+		return nil, errors.New("service port not found")
+	}
+
+	var servers []dynamic.TCPServer
+	if service.Spec.Type == corev1.ServiceTypeExternalName {
+		servers = append(servers, dynamic.TCPServer{
+			Address: fmt.Sprintf("%s:%d", service.Spec.ExternalName, portSpec.Port),
+		})
+	} else {
+		endpoints, endpointsExists, endpointsErr := client.GetEndpoints(namespace, svc.Name)
+		if endpointsErr != nil {
+			return nil, endpointsErr
+		}
+
+		if !endpointsExists {
+			return nil, errors.New("endpoints not found")
+		}
+
+		if len(endpoints.Subsets) == 0 {
+			return nil, errors.New("subset not found")
+		}
+
+		var port int32
+		for _, subset := range endpoints.Subsets {
+			for _, p := range subset.Ports {
+				if portSpec.Name == p.Name {
+					port = p.Port
+					break
+				}
+			}
+
+			if port == 0 {
+				return nil, errors.New("cannot define a port")
+			}
+
+			for _, addr := range subset.Addresses {
+				servers = append(servers, dynamic.TCPServer{
+					Address: fmt.Sprintf("%s:%d", addr.IP, port),
+				})
+			}
+		}
+	}
+
+	return servers, nil
+}
+
+func getTLSTCP(ctx context.Context, ingressRoute *v1alpha1.IngressRouteTCP, k8sClient Client, tlsConfigs map[string]*tls.CertAndStores) error {
+	if ingressRoute.Spec.TLS == nil {
+		return nil
+	}
+	if ingressRoute.Spec.TLS.SecretName == "" {
+		log.FromContext(ctx).Debugf("No secret name provided")
+		return nil
+	}
+
+	configKey := ingressRoute.Namespace + "/" + ingressRoute.Spec.TLS.SecretName
+	if _, tlsExists := tlsConfigs[configKey]; !tlsExists {
+		tlsConf, err := getTLS(k8sClient, ingressRoute.Spec.TLS.SecretName, ingressRoute.Namespace)
+		if err != nil {
+			return err
+		}
+
+		tlsConfigs[configKey] = tlsConf
+	}
+
+	return nil
+}
diff --git a/pkg/provider/kubernetes/crd/traefik/v1alpha1/ingressroute.go b/pkg/provider/kubernetes/crd/traefik/v1alpha1/ingressroute.go
index ac7eb32e3..bf6074039 100644
--- a/pkg/provider/kubernetes/crd/traefik/v1alpha1/ingressroute.go
+++ b/pkg/provider/kubernetes/crd/traefik/v1alpha1/ingressroute.go
@@ -49,6 +49,7 @@ type Service struct {
 	Scheme      string       `json:"scheme,omitempty"`
 	HealthCheck *HealthCheck `json:"healthCheck,omitempty"`
 	Strategy    string       `json:"strategy,omitempty"`
+	Weight      *int         `json:"weight,omitempty"`
 }
 
 // MiddlewareRef is a ref to the Middleware resources.
diff --git a/pkg/provider/kubernetes/crd/traefik/v1alpha1/zz_generated.deepcopy.go b/pkg/provider/kubernetes/crd/traefik/v1alpha1/zz_generated.deepcopy.go
index 502a1d854..047f0fcaa 100644
--- a/pkg/provider/kubernetes/crd/traefik/v1alpha1/zz_generated.deepcopy.go
+++ b/pkg/provider/kubernetes/crd/traefik/v1alpha1/zz_generated.deepcopy.go
@@ -395,6 +395,11 @@ func (in *Service) DeepCopyInto(out *Service) {
 		*out = new(HealthCheck)
 		(*in).DeepCopyInto(*out)
 	}
+	if in.Weight != nil {
+		in, out := &in.Weight, &out.Weight
+		*out = new(int)
+		**out = **in
+	}
 	return
 }
 
diff --git a/pkg/provider/kubernetes/ingress/kubernetes.go b/pkg/provider/kubernetes/ingress/kubernetes.go
index c4874547e..8f17bf174 100644
--- a/pkg/provider/kubernetes/ingress/kubernetes.go
+++ b/pkg/provider/kubernetes/ingress/kubernetes.go
@@ -229,7 +229,7 @@ func loadService(client Client, namespace string, backend v1beta1.IngressBackend
 	}
 
 	return &dynamic.Service{
-		LoadBalancer: &dynamic.LoadBalancerService{
+		LoadBalancer: &dynamic.ServersLoadBalancer{
 			Servers:        servers,
 			PassHostHeader: true,
 		},
diff --git a/pkg/provider/marathon/config.go b/pkg/provider/marathon/config.go
index cd1d81939..3f9adfebe 100644
--- a/pkg/provider/marathon/config.go
+++ b/pkg/provider/marathon/config.go
@@ -98,7 +98,7 @@ func (p *Provider) buildServiceConfiguration(ctx context.Context, app marathon.A
 
 	if len(conf.Services) == 0 {
 		conf.Services = make(map[string]*dynamic.Service)
-		lb := &dynamic.LoadBalancerService{}
+		lb := &dynamic.ServersLoadBalancer{}
 		lb.SetDefaults()
 		conf.Services[appName] = &dynamic.Service{
 			LoadBalancer: lb,
diff --git a/pkg/provider/rancher/config.go b/pkg/provider/rancher/config.go
index 24a32e66c..4d6f63a13 100644
--- a/pkg/provider/rancher/config.go
+++ b/pkg/provider/rancher/config.go
@@ -96,7 +96,7 @@ func (p *Provider) buildServiceConfiguration(ctx context.Context, service ranche
 
 	if len(configuration.Services) == 0 {
 		configuration.Services = make(map[string]*dynamic.Service)
-		lb := &dynamic.LoadBalancerService{}
+		lb := &dynamic.ServersLoadBalancer{}
 		lb.SetDefaults()
 		configuration.Services[serviceName] = &dynamic.Service{
 			LoadBalancer: lb,
@@ -183,7 +183,7 @@ func (p *Provider) addServerTCP(ctx context.Context, service rancherData, loadBa
 
 }
 
-func (p *Provider) addServers(ctx context.Context, service rancherData, loadBalancer *dynamic.LoadBalancerService) error {
+func (p *Provider) addServers(ctx context.Context, service rancherData, loadBalancer *dynamic.ServersLoadBalancer) error {
 	log.FromContext(ctx).Debugf("Trying to add servers for service  %s \n", service.Name)
 
 	serverPort := getLBServerPort(loadBalancer)
@@ -216,7 +216,7 @@ func (p *Provider) addServers(ctx context.Context, service rancherData, loadBala
 	return nil
 }
 
-func getLBServerPort(loadBalancer *dynamic.LoadBalancerService) string {
+func getLBServerPort(loadBalancer *dynamic.ServersLoadBalancer) string {
 	if loadBalancer != nil && len(loadBalancer.Servers) > 0 {
 		return loadBalancer.Servers[0].Port
 	}
diff --git a/pkg/server/service/loadbalancer/wrr/wrr.go b/pkg/server/service/loadbalancer/wrr/wrr.go
new file mode 100644
index 000000000..a805c66a9
--- /dev/null
+++ b/pkg/server/service/loadbalancer/wrr/wrr.go
@@ -0,0 +1,155 @@
+package wrr
+
+import (
+	"fmt"
+	"net/http"
+	"sync"
+
+	"github.com/containous/traefik/v2/pkg/config/dynamic"
+	"github.com/containous/traefik/v2/pkg/log"
+)
+
+type namedHandler struct {
+	http.Handler
+	name   string
+	weight int
+}
+
+type stickyCookie struct {
+	name     string
+	secure   bool
+	httpOnly bool
+}
+
+// New creates a new load balancer.
+func New(sticky *dynamic.Sticky) *Balancer {
+	balancer := &Balancer{
+		mutex: &sync.Mutex{},
+		index: -1,
+	}
+	if sticky != nil && sticky.Cookie != nil {
+		balancer.stickyCookie = &stickyCookie{
+			name:     sticky.Cookie.Name,
+			secure:   sticky.Cookie.Secure,
+			httpOnly: sticky.Cookie.HTTPOnly,
+		}
+	}
+	return balancer
+}
+
+// Balancer is a WeightedRoundRobin load balancer.
+type Balancer struct {
+	handlers []*namedHandler
+	mutex    *sync.Mutex
+	// Current index (starts from -1)
+	index         int
+	currentWeight int
+	stickyCookie  *stickyCookie
+}
+
+func (b *Balancer) maxWeight() int {
+	max := -1
+	for _, s := range b.handlers {
+		if s.weight > max {
+			max = s.weight
+		}
+	}
+	return max
+}
+
+func (b *Balancer) weightGcd() int {
+	divisor := -1
+	for _, s := range b.handlers {
+		if divisor == -1 {
+			divisor = s.weight
+		} else {
+			divisor = gcd(divisor, s.weight)
+		}
+	}
+	return divisor
+}
+
+func gcd(a, b int) int {
+	for b != 0 {
+		a, b = b, a%b
+	}
+	return a
+}
+
+func (b *Balancer) nextServer() (*namedHandler, error) {
+	b.mutex.Lock()
+	defer b.mutex.Unlock()
+
+	if len(b.handlers) == 0 {
+		return nil, fmt.Errorf("no servers in the pool")
+	}
+
+	// The algo below may look messy, but is actually very simple
+	// it calculates the GCD  and subtracts it on every iteration, what interleaves servers
+	// and allows us not to build an iterator every time we readjust weights
+
+	// GCD across all enabled servers
+	gcd := b.weightGcd()
+	// Maximum weight across all enabled servers
+	max := b.maxWeight()
+
+	for {
+		b.index = (b.index + 1) % len(b.handlers)
+		if b.index == 0 {
+			b.currentWeight -= gcd
+			if b.currentWeight <= 0 {
+				b.currentWeight = max
+				if b.currentWeight == 0 {
+					return nil, fmt.Errorf("all servers have 0 weight")
+				}
+			}
+		}
+		srv := b.handlers[b.index]
+		if srv.weight >= b.currentWeight {
+			log.WithoutContext().Debugf("Service Select: %s", srv.name)
+			return srv, nil
+		}
+	}
+}
+
+func (b *Balancer) ServeHTTP(w http.ResponseWriter, req *http.Request) {
+	if b.stickyCookie != nil {
+		cookie, err := req.Cookie(b.stickyCookie.name)
+
+		if err != nil && err != http.ErrNoCookie {
+			log.WithoutContext().Warnf("Error while reading cookie: %v", err)
+		}
+
+		if err == nil && cookie != nil {
+			for _, handler := range b.handlers {
+				if handler.name == cookie.Value {
+					handler.ServeHTTP(w, req)
+					return
+				}
+			}
+		}
+	}
+
+	server, err := b.nextServer()
+	if err != nil {
+		http.Error(w, http.StatusText(http.StatusInternalServerError)+err.Error(), http.StatusInternalServerError)
+		return
+	}
+
+	if b.stickyCookie != nil {
+		cookie := &http.Cookie{Name: b.stickyCookie.name, Value: server.name, Path: "/", HttpOnly: b.stickyCookie.httpOnly, Secure: b.stickyCookie.secure}
+		http.SetCookie(w, cookie)
+	}
+
+	server.ServeHTTP(w, req)
+}
+
+// AddService adds a handler.
+// It is not thread safe with ServeHTTP.
+func (b *Balancer) AddService(name string, handler http.Handler, weight *int) {
+	w := 1
+	if weight != nil {
+		w = *weight
+	}
+	b.handlers = append(b.handlers, &namedHandler{Handler: handler, name: name, weight: w})
+}
diff --git a/pkg/server/service/service.go b/pkg/server/service/service.go
index 7e2a0e8f1..1ea363817 100644
--- a/pkg/server/service/service.go
+++ b/pkg/server/service/service.go
@@ -2,6 +2,7 @@ package service
 
 import (
 	"context"
+	"errors"
 	"fmt"
 	"net/http"
 	"net/http/httputil"
@@ -20,6 +21,7 @@ import (
 	"github.com/containous/traefik/v2/pkg/middlewares/pipelining"
 	"github.com/containous/traefik/v2/pkg/server/cookie"
 	"github.com/containous/traefik/v2/pkg/server/internal"
+	"github.com/containous/traefik/v2/pkg/server/service/loadbalancer/wrr"
 	"github.com/vulcand/oxy/roundrobin"
 )
 
@@ -60,27 +62,58 @@ func (m *Manager) BuildHTTP(rootCtx context.Context, serviceName string, respons
 		return nil, fmt.Errorf("the service %q does not exist", serviceName)
 	}
 
-	// TODO Should handle multiple service types
-	// FIXME Check if the service is declared multiple times with different types
-	if conf.LoadBalancer == nil {
-		sErr := fmt.Errorf("the service %q doesn't have any load balancer", serviceName)
+	if conf.LoadBalancer != nil && conf.Weighted != nil {
+		return nil, errors.New("cannot create service: multi-types service not supported, consider declaring two different pieces of service instead")
+	}
+
+	var lb http.Handler
+
+	switch {
+	case conf.LoadBalancer != nil:
+		var err error
+		lb, err = m.getLoadBalancerServiceHandler(ctx, serviceName, conf.LoadBalancer, responseModifier)
+		if err != nil {
+			conf.AddError(err, true)
+			return nil, err
+		}
+	case conf.Weighted != nil:
+		var err error
+		lb, err = m.getLoadBalancerWRRServiceHandler(ctx, serviceName, conf.Weighted, responseModifier)
+		if err != nil {
+			conf.AddError(err, true)
+			return nil, err
+		}
+	default:
+		sErr := fmt.Errorf("the service %q does not have any type defined", serviceName)
 		conf.AddError(sErr, true)
 		return nil, sErr
 	}
 
-	lb, err := m.getLoadBalancerServiceHandler(ctx, serviceName, conf.LoadBalancer, responseModifier)
-	if err != nil {
-		conf.AddError(err, true)
-		return nil, err
+	return lb, nil
+}
+
+func (m *Manager) getLoadBalancerWRRServiceHandler(ctx context.Context, serviceName string, config *dynamic.WeightedRoundRobin, responseModifier func(*http.Response) error) (http.Handler, error) {
+	// TODO Handle accesslog and metrics with multiple service name
+	if config.Sticky != nil && config.Sticky.Cookie != nil {
+		config.Sticky.Cookie.Name = cookie.GetName(config.Sticky.Cookie.Name, serviceName)
 	}
 
-	return lb, nil
+	balancer := wrr.New(config.Sticky)
+	for _, service := range config.Services {
+		serviceHandler, err := m.BuildHTTP(ctx, service.Name, responseModifier)
+		if err != nil {
+			return nil, err
+		}
+
+		balancer.AddService(service.Name, serviceHandler, service.Weight)
+	}
+	return balancer, nil
 }
 
 func (m *Manager) getLoadBalancerServiceHandler(
 	ctx context.Context,
 	serviceName string,
-	service *dynamic.LoadBalancerService,
+	service *dynamic.ServersLoadBalancer,
 	responseModifier func(*http.Response) error,
 ) (http.Handler, error) {
 	fwd, err := buildProxy(service.PassHostHeader, service.ResponseForwarding, m.defaultRoundTripper, m.bufferPool, responseModifier)
@@ -193,16 +226,16 @@ func buildHealthCheckOptions(ctx context.Context, lb healthcheck.BalancerHandler
 	}
 }
 
-func (m *Manager) getLoadBalancer(ctx context.Context, serviceName string, service *dynamic.LoadBalancerService, fwd http.Handler) (healthcheck.BalancerHandler, error) {
+func (m *Manager) getLoadBalancer(ctx context.Context, serviceName string, service *dynamic.ServersLoadBalancer, fwd http.Handler) (healthcheck.BalancerHandler, error) {
 	logger := log.FromContext(ctx)
 	logger.Debug("Creating load-balancer")
 
 	var options []roundrobin.LBOption
 
 	var cookieName string
-	if stickiness := service.Stickiness; stickiness != nil {
-		cookieName = cookie.GetName(stickiness.CookieName, serviceName)
-		opts := roundrobin.CookieOptions{HTTPOnly: stickiness.HTTPOnlyCookie, Secure: stickiness.SecureCookie}
+	if service.Sticky != nil && service.Sticky.Cookie != nil {
+		cookieName = cookie.GetName(service.Sticky.Cookie.Name, serviceName)
+		opts := roundrobin.CookieOptions{HTTPOnly: service.Sticky.Cookie.HTTPOnly, Secure: service.Sticky.Cookie.Secure}
 		options = append(options, roundrobin.EnableStickySession(roundrobin.NewStickySessionWithOptions(cookieName, opts)))
 		logger.Debugf("Sticky session cookie name: %v", cookieName)
 	}


Test patch:
diff --git a/integration/rest_test.go b/integration/rest_test.go
index 3aac50fd0..692542c9f 100644
--- a/integration/rest_test.go
+++ b/integration/rest_test.go
@@ -51,7 +51,7 @@ func (s *RestSuite) TestSimpleConfiguration(c *check.C) {
 					},
 					Services: map[string]*dynamic.Service{
 						"service1": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://" + s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress + ":80",
diff --git a/integration/simple_test.go b/integration/simple_test.go
index a004ffd19..7105f2482 100644
--- a/integration/simple_test.go
+++ b/integration/simple_test.go
@@ -4,6 +4,7 @@ import (
 	"bytes"
 	"encoding/json"
 	"fmt"
+	"io/ioutil"
 	"net/http"
 	"net/http/httptest"
 	"os"
@@ -563,7 +564,7 @@ func (s *SimpleSuite) TestServiceConfigErrors(c *check.C) {
 	c.Assert(err, checker.IsNil)
 	defer cmd.Process.Kill()
 
-	err = try.GetRequest("http://127.0.0.1:8080/api/http/services", 1000*time.Millisecond, try.BodyContains(`["the service \"service1@file\" doesn't have any load balancer"]`))
+	err = try.GetRequest("http://127.0.0.1:8080/api/http/services", 1000*time.Millisecond, try.BodyContains(`["the service \"service1@file\" does not have any type defined"]`))
 	c.Assert(err, checker.IsNil)
 
 	err = try.GetRequest("http://127.0.0.1:8080/api/http/services/service1@file", 1000*time.Millisecond, try.BodyContains(`"status":"disabled"`))
@@ -572,3 +573,101 @@ func (s *SimpleSuite) TestServiceConfigErrors(c *check.C) {
 	err = try.GetRequest("http://127.0.0.1:8080/api/http/services/service2@file", 1000*time.Millisecond, try.BodyContains(`"status":"enabled"`))
 	c.Assert(err, checker.IsNil)
 }
+
+func (s *SimpleSuite) TestWRR(c *check.C) {
+	s.createComposeProject(c, "base")
+	s.composeProject.Start(c)
+
+	server1 := s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress
+	server2 := s.composeProject.Container(c, "whoami2").NetworkSettings.IPAddress
+
+	file := s.adaptFile(c, "fixtures/wrr.toml", struct {
+		Server1 string
+		Server2 string
+	}{Server1: "http://" + server1, Server2: "http://" + server2})
+	defer os.Remove(file)
+
+	cmd, output := s.traefikCmd(withConfigFile(file))
+	defer output(c)
+
+	err := cmd.Start()
+	c.Assert(err, checker.IsNil)
+	defer cmd.Process.Kill()
+
+	err = try.GetRequest("http://127.0.0.1:8080/api/http/services", 1000*time.Millisecond, try.BodyContains("service1", "service2"))
+	c.Assert(err, checker.IsNil)
+
+	repartition := map[string]int{}
+	for i := 0; i < 4; i++ {
+		req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/whoami", nil)
+		c.Assert(err, checker.IsNil)
+
+		response, err := http.DefaultClient.Do(req)
+		c.Assert(err, checker.IsNil)
+		c.Assert(response.StatusCode, checker.Equals, http.StatusOK)
+
+		body, err := ioutil.ReadAll(response.Body)
+		c.Assert(err, checker.IsNil)
+
+		if strings.Contains(string(body), server1) {
+			repartition[server1]++
+		}
+		if strings.Contains(string(body), server2) {
+			repartition[server2]++
+		}
+	}
+
+	c.Assert(repartition[server1], checker.Equals, 3)
+	c.Assert(repartition[server2], checker.Equals, 1)
+}
+
+func (s *SimpleSuite) TestWRRSticky(c *check.C) {
+	s.createComposeProject(c, "base")
+	s.composeProject.Start(c)
+
+	server1 := s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress
+	server2 := s.composeProject.Container(c, "whoami2").NetworkSettings.IPAddress
+
+	file := s.adaptFile(c, "fixtures/wrr_sticky.toml", struct {
+		Server1 string
+		Server2 string
+	}{Server1: "http://" + server1, Server2: "http://" + server2})
+	defer os.Remove(file)
+
+	cmd, output := s.traefikCmd(withConfigFile(file))
+	defer output(c)
+
+	err := cmd.Start()
+	c.Assert(err, checker.IsNil)
+	defer cmd.Process.Kill()
+
+	err = try.GetRequest("http://127.0.0.1:8080/api/http/services", 1000*time.Millisecond, try.BodyContains("service1", "service2"))
+	c.Assert(err, checker.IsNil)
+
+	repartition := map[string]int{}
+	req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/whoami", nil)
+	c.Assert(err, checker.IsNil)
+	for i := 0; i < 4; i++ {
+
+		response, err := http.DefaultClient.Do(req)
+		c.Assert(err, checker.IsNil)
+		c.Assert(response.StatusCode, checker.Equals, http.StatusOK)
+
+		for _, cookie := range response.Cookies() {
+			req.AddCookie(cookie)
+		}
+
+		body, err := ioutil.ReadAll(response.Body)
+		c.Assert(err, checker.IsNil)
+
+		if strings.Contains(string(body), server1) {
+			repartition[server1]++
+		}
+		if strings.Contains(string(body), server2) {
+			repartition[server2]++
+		}
+	}
+
+	c.Assert(repartition[server1], checker.Equals, 4)
+	c.Assert(repartition[server2], checker.Equals, 0)
+}
diff --git a/pkg/api/handler_http_test.go b/pkg/api/handler_http_test.go
index ea89966a1..57524e675 100644
--- a/pkg/api/handler_http_test.go
+++ b/pkg/api/handler_http_test.go
@@ -203,7 +203,7 @@ func TestHandler_HTTP(t *testing.T) {
 					"bar@myprovider": func() *runtime.ServiceInfo {
 						si := &runtime.ServiceInfo{
 							Service: &dynamic.Service{
-								LoadBalancer: &dynamic.LoadBalancerService{
+								LoadBalancer: &dynamic.ServersLoadBalancer{
 									Servers: []dynamic.Server{
 										{
 											URL: "http://127.0.0.1",
@@ -219,7 +219,7 @@ func TestHandler_HTTP(t *testing.T) {
 					"baz@myprovider": func() *runtime.ServiceInfo {
 						si := &runtime.ServiceInfo{
 							Service: &dynamic.Service{
-								LoadBalancer: &dynamic.LoadBalancerService{
+								LoadBalancer: &dynamic.ServersLoadBalancer{
 									Servers: []dynamic.Server{
 										{
 											URL: "http://127.0.0.2",
@@ -248,7 +248,7 @@ func TestHandler_HTTP(t *testing.T) {
 					"bar@myprovider": func() *runtime.ServiceInfo {
 						si := &runtime.ServiceInfo{
 							Service: &dynamic.Service{
-								LoadBalancer: &dynamic.LoadBalancerService{
+								LoadBalancer: &dynamic.ServersLoadBalancer{
 									Servers: []dynamic.Server{
 										{
 											URL: "http://127.0.0.1",
@@ -264,7 +264,7 @@ func TestHandler_HTTP(t *testing.T) {
 					"baz@myprovider": func() *runtime.ServiceInfo {
 						si := &runtime.ServiceInfo{
 							Service: &dynamic.Service{
-								LoadBalancer: &dynamic.LoadBalancerService{
+								LoadBalancer: &dynamic.ServersLoadBalancer{
 									Servers: []dynamic.Server{
 										{
 											URL: "http://127.0.0.2",
@@ -280,7 +280,7 @@ func TestHandler_HTTP(t *testing.T) {
 					"test@myprovider": func() *runtime.ServiceInfo {
 						si := &runtime.ServiceInfo{
 							Service: &dynamic.Service{
-								LoadBalancer: &dynamic.LoadBalancerService{
+								LoadBalancer: &dynamic.ServersLoadBalancer{
 									Servers: []dynamic.Server{
 										{
 											URL: "http://127.0.0.3",
@@ -309,7 +309,7 @@ func TestHandler_HTTP(t *testing.T) {
 					"bar@myprovider": func() *runtime.ServiceInfo {
 						si := &runtime.ServiceInfo{
 							Service: &dynamic.Service{
-								LoadBalancer: &dynamic.LoadBalancerService{
+								LoadBalancer: &dynamic.ServersLoadBalancer{
 									Servers: []dynamic.Server{
 										{
 											URL: "http://127.0.0.1",
@@ -337,7 +337,7 @@ func TestHandler_HTTP(t *testing.T) {
 					"bar@myprovider": func() *runtime.ServiceInfo {
 						si := &runtime.ServiceInfo{
 							Service: &dynamic.Service{
-								LoadBalancer: &dynamic.LoadBalancerService{
+								LoadBalancer: &dynamic.ServersLoadBalancer{
 									Servers: []dynamic.Server{
 										{
 											URL: "http://127.0.0.1",
diff --git a/pkg/api/handler_overview_test.go b/pkg/api/handler_overview_test.go
index c259de2a7..e8eaa7c92 100644
--- a/pkg/api/handler_overview_test.go
+++ b/pkg/api/handler_overview_test.go
@@ -55,7 +55,7 @@ func TestHandler_Overview(t *testing.T) {
 				Services: map[string]*runtime.ServiceInfo{
 					"foo-service@myprovider": {
 						Service: &dynamic.Service{
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{{URL: "http://127.0.0.1"}},
 							},
 						},
@@ -63,7 +63,7 @@ func TestHandler_Overview(t *testing.T) {
 					},
 					"bar-service@myprovider": {
 						Service: &dynamic.Service{
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{{URL: "http://127.0.0.1"}},
 							},
 						},
@@ -71,7 +71,7 @@ func TestHandler_Overview(t *testing.T) {
 					},
 					"fii-service@myprovider": {
 						Service: &dynamic.Service{
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{{URL: "http://127.0.0.1"}},
 							},
 						},
diff --git a/pkg/api/handler_test.go b/pkg/api/handler_test.go
index 2971f0354..7b506cd6c 100644
--- a/pkg/api/handler_test.go
+++ b/pkg/api/handler_test.go
@@ -37,7 +37,7 @@ func TestHandler_RawData(t *testing.T) {
 				Services: map[string]*runtime.ServiceInfo{
 					"foo-service@myprovider": {
 						Service: &dynamic.Service{
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://127.0.0.1",
diff --git a/pkg/config/label/label_test.go b/pkg/config/label/label_test.go
index edeca179e..0edc8bc31 100644
--- a/pkg/config/label/label_test.go
+++ b/pkg/config/label/label_test.go
@@ -142,8 +142,8 @@ func TestDecodeConfiguration(t *testing.T) {
 		"traefik.http.services.Service0.loadbalancer.responseforwarding.flushinterval": "foobar",
 		"traefik.http.services.Service0.loadbalancer.server.scheme":                    "foobar",
 		"traefik.http.services.Service0.loadbalancer.server.port":                      "8080",
-		"traefik.http.services.Service0.loadbalancer.stickiness.cookiename":            "foobar",
-		"traefik.http.services.Service0.loadbalancer.stickiness.securecookie":          "true",
+		"traefik.http.services.Service0.loadbalancer.sticky.cookie.name":               "foobar",
+		"traefik.http.services.Service0.loadbalancer.sticky.cookie.secure":             "true",
 		"traefik.http.services.Service1.loadbalancer.healthcheck.headers.name0":        "foobar",
 		"traefik.http.services.Service1.loadbalancer.healthcheck.headers.name1":        "foobar",
 		"traefik.http.services.Service1.loadbalancer.healthcheck.hostname":             "foobar",
@@ -156,8 +156,8 @@ func TestDecodeConfiguration(t *testing.T) {
 		"traefik.http.services.Service1.loadbalancer.responseforwarding.flushinterval": "foobar",
 		"traefik.http.services.Service1.loadbalancer.server.scheme":                    "foobar",
 		"traefik.http.services.Service1.loadbalancer.server.port":                      "8080",
-		"traefik.http.services.Service1.loadbalancer.stickiness":                       "false",
-		"traefik.http.services.Service1.loadbalancer.stickiness.cookiename":            "fui",
+		"traefik.http.services.Service1.loadbalancer.sticky":                           "false",
+		"traefik.http.services.Service1.loadbalancer.sticky.cookie.name":               "fui",
 		"traefik.tcp.routers.Router0.rule":                                             "foobar",
 		"traefik.tcp.routers.Router0.entrypoints":                                      "foobar, fiibar",
 		"traefik.tcp.routers.Router0.service":                                          "foobar",
@@ -510,11 +510,13 @@ func TestDecodeConfiguration(t *testing.T) {
 			},
 			Services: map[string]*dynamic.Service{
 				"Service0": {
-					LoadBalancer: &dynamic.LoadBalancerService{
-						Stickiness: &dynamic.Stickiness{
-							CookieName:     "foobar",
-							SecureCookie:   true,
-							HTTPOnlyCookie: false,
+					LoadBalancer: &dynamic.ServersLoadBalancer{
+						Sticky: &dynamic.Sticky{
+							Cookie: &dynamic.Cookie{
+								Name:     "foobar",
+								Secure:   true,
+								HTTPOnly: false,
+							},
 						},
 						Servers: []dynamic.Server{
 							{
@@ -541,7 +543,7 @@ func TestDecodeConfiguration(t *testing.T) {
 					},
 				},
 				"Service1": {
-					LoadBalancer: &dynamic.LoadBalancerService{
+					LoadBalancer: &dynamic.ServersLoadBalancer{
 						Servers: []dynamic.Server{
 							{
 								Scheme: "foobar",
@@ -908,10 +910,12 @@ func TestEncodeConfiguration(t *testing.T) {
 			},
 			Services: map[string]*dynamic.Service{
 				"Service0": {
-					LoadBalancer: &dynamic.LoadBalancerService{
-						Stickiness: &dynamic.Stickiness{
-							CookieName:     "foobar",
-							HTTPOnlyCookie: true,
+					LoadBalancer: &dynamic.ServersLoadBalancer{
+						Sticky: &dynamic.Sticky{
+							Cookie: &dynamic.Cookie{
+								Name:     "foobar",
+								HTTPOnly: true,
+							},
 						},
 						Servers: []dynamic.Server{
 							{
@@ -938,7 +942,7 @@ func TestEncodeConfiguration(t *testing.T) {
 					},
 				},
 				"Service1": {
-					LoadBalancer: &dynamic.LoadBalancerService{
+					LoadBalancer: &dynamic.ServersLoadBalancer{
 						Servers: []dynamic.Server{
 							{
 								Scheme: "foobar",
@@ -1101,9 +1105,9 @@ func TestEncodeConfiguration(t *testing.T) {
 		"traefik.HTTP.Services.Service0.LoadBalancer.ResponseForwarding.FlushInterval": "foobar",
 		"traefik.HTTP.Services.Service0.LoadBalancer.server.Port":                      "8080",
 		"traefik.HTTP.Services.Service0.LoadBalancer.server.Scheme":                    "foobar",
-		"traefik.HTTP.Services.Service0.LoadBalancer.Stickiness.CookieName":            "foobar",
-		"traefik.HTTP.Services.Service0.LoadBalancer.Stickiness.HTTPOnlyCookie":        "true",
-		"traefik.HTTP.Services.Service0.LoadBalancer.Stickiness.SecureCookie":          "false",
+		"traefik.HTTP.Services.Service0.LoadBalancer.Sticky.Cookie.Name":               "foobar",
+		"traefik.HTTP.Services.Service0.LoadBalancer.Sticky.Cookie.HTTPOnly":           "true",
+		"traefik.HTTP.Services.Service0.LoadBalancer.Sticky.Cookie.Secure":             "false",
 		"traefik.HTTP.Services.Service1.LoadBalancer.HealthCheck.Headers.name0":        "foobar",
 		"traefik.HTTP.Services.Service1.LoadBalancer.HealthCheck.Headers.name1":        "foobar",
 		"traefik.HTTP.Services.Service1.LoadBalancer.HealthCheck.Hostname":             "foobar",
diff --git a/pkg/config/runtime/runtime_test.go b/pkg/config/runtime/runtime_test.go
index d220abe84..b2cbc6238 100644
--- a/pkg/config/runtime/runtime_test.go
+++ b/pkg/config/runtime/runtime_test.go
@@ -43,7 +43,7 @@ func TestPopulateUsedBy(t *testing.T) {
 				Services: map[string]*runtime.ServiceInfo{
 					"foo-service@myprovider": {
 						Service: &dynamic.Service{
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{URL: "http://127.0.0.1:8085"},
 									{URL: "http://127.0.0.1:8086"},
@@ -75,7 +75,7 @@ func TestPopulateUsedBy(t *testing.T) {
 				Services: map[string]*runtime.ServiceInfo{
 					"foo-service@myprovider": {
 						Service: &dynamic.Service{
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{URL: "http://127.0.0.1"},
 								},
@@ -149,7 +149,7 @@ func TestPopulateUsedBy(t *testing.T) {
 				Services: map[string]*runtime.ServiceInfo{
 					"foo-service@myprovider": {
 						Service: &dynamic.Service{
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://127.0.0.1:8085",
@@ -167,7 +167,7 @@ func TestPopulateUsedBy(t *testing.T) {
 					},
 					"bar-service@myprovider": {
 						Service: &dynamic.Service{
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://127.0.0.1:8087",
@@ -222,7 +222,7 @@ func TestPopulateUsedBy(t *testing.T) {
 				Services: map[string]*runtime.ServiceInfo{
 					"foo-service@myprovider": {
 						Service: &dynamic.Service{
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://127.0.0.1",
@@ -293,7 +293,7 @@ func TestPopulateUsedBy(t *testing.T) {
 				Services: map[string]*runtime.ServiceInfo{
 					"foo-service@myprovider": {
 						Service: &dynamic.Service{
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://127.0.0.1",
@@ -337,7 +337,7 @@ func TestPopulateUsedBy(t *testing.T) {
 				Services: map[string]*runtime.ServiceInfo{
 					"foo-service@myprovider": {
 						Service: &dynamic.Service{
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://127.0.0.1",
@@ -389,7 +389,7 @@ func TestPopulateUsedBy(t *testing.T) {
 				Services: map[string]*runtime.ServiceInfo{
 					"foo-service@myprovider": {
 						Service: &dynamic.Service{
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://127.0.0.1",
diff --git a/pkg/provider/docker/config_test.go b/pkg/provider/docker/config_test.go
index c7a996593..61636b474 100644
--- a/pkg/provider/docker/config_test.go
+++ b/pkg/provider/docker/config_test.go
@@ -56,7 +56,7 @@ func TestDefaultRule(t *testing.T) {
 					Middlewares: map[string]*dynamic.Middleware{},
 					Services: map[string]*dynamic.Service{
 						"Test": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://127.0.0.1:80",
@@ -105,7 +105,7 @@ func TestDefaultRule(t *testing.T) {
 					Middlewares: map[string]*dynamic.Middleware{},
 					Services: map[string]*dynamic.Service{
 						"Test": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://127.0.0.1:80",
@@ -156,7 +156,7 @@ func TestDefaultRule(t *testing.T) {
 					Middlewares: map[string]*dynamic.Middleware{},
 					Services: map[string]*dynamic.Service{
 						"Test": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://127.0.0.1:80",
@@ -200,7 +200,7 @@ func TestDefaultRule(t *testing.T) {
 					Middlewares: map[string]*dynamic.Middleware{},
 					Services: map[string]*dynamic.Service{
 						"Test": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://127.0.0.1:80",
@@ -244,7 +244,7 @@ func TestDefaultRule(t *testing.T) {
 					Middlewares: map[string]*dynamic.Middleware{},
 					Services: map[string]*dynamic.Service{
 						"Test": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://127.0.0.1:80",
@@ -293,7 +293,7 @@ func TestDefaultRule(t *testing.T) {
 					Middlewares: map[string]*dynamic.Middleware{},
 					Services: map[string]*dynamic.Service{
 						"Test": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://127.0.0.1:80",
@@ -376,7 +376,7 @@ func Test_buildConfiguration(t *testing.T) {
 					Middlewares: map[string]*dynamic.Middleware{},
 					Services: map[string]*dynamic.Service{
 						"Test": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://127.0.0.1:80",
@@ -444,7 +444,7 @@ func Test_buildConfiguration(t *testing.T) {
 					Middlewares: map[string]*dynamic.Middleware{},
 					Services: map[string]*dynamic.Service{
 						"Test": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://127.0.0.1:80",
@@ -454,7 +454,7 @@ func Test_buildConfiguration(t *testing.T) {
 							},
 						},
 						"Test2": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://127.0.0.2:80",
@@ -520,7 +520,7 @@ func Test_buildConfiguration(t *testing.T) {
 					Middlewares: map[string]*dynamic.Middleware{},
 					Services: map[string]*dynamic.Service{
 						"Test": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://127.0.0.1:80",
@@ -573,7 +573,7 @@ func Test_buildConfiguration(t *testing.T) {
 					Middlewares: map[string]*dynamic.Middleware{},
 					Services: map[string]*dynamic.Service{
 						"Service1": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://127.0.0.1:80",
@@ -625,7 +625,7 @@ func Test_buildConfiguration(t *testing.T) {
 					Middlewares: map[string]*dynamic.Middleware{},
 					Services: map[string]*dynamic.Service{
 						"Service1": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://127.0.0.1:80",
@@ -669,7 +669,7 @@ func Test_buildConfiguration(t *testing.T) {
 					Middlewares: map[string]*dynamic.Middleware{},
 					Services: map[string]*dynamic.Service{
 						"Test": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://127.0.0.1:80",
@@ -726,7 +726,7 @@ func Test_buildConfiguration(t *testing.T) {
 					Middlewares: map[string]*dynamic.Middleware{},
 					Services: map[string]*dynamic.Service{
 						"Service1": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://127.0.0.1:80",
@@ -773,7 +773,7 @@ func Test_buildConfiguration(t *testing.T) {
 					Middlewares: map[string]*dynamic.Middleware{},
 					Services: map[string]*dynamic.Service{
 						"Service1": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://127.0.0.1:80",
@@ -783,7 +783,7 @@ func Test_buildConfiguration(t *testing.T) {
 							},
 						},
 						"Service2": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://127.0.0.1:80",
@@ -990,7 +990,7 @@ func Test_buildConfiguration(t *testing.T) {
 					Middlewares: map[string]*dynamic.Middleware{},
 					Services: map[string]*dynamic.Service{
 						"Service1": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://127.0.0.1:80",
@@ -1042,7 +1042,7 @@ func Test_buildConfiguration(t *testing.T) {
 					},
 					Services: map[string]*dynamic.Service{
 						"Test": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://127.0.0.1:80",
@@ -1127,7 +1127,7 @@ func Test_buildConfiguration(t *testing.T) {
 					},
 					Services: map[string]*dynamic.Service{
 						"Test": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://127.0.0.1:80",
@@ -1200,7 +1200,7 @@ func Test_buildConfiguration(t *testing.T) {
 					Middlewares: map[string]*dynamic.Middleware{},
 					Services: map[string]*dynamic.Service{
 						"Test": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://127.0.0.1:80",
@@ -1292,7 +1292,7 @@ func Test_buildConfiguration(t *testing.T) {
 					Middlewares: map[string]*dynamic.Middleware{},
 					Services: map[string]*dynamic.Service{
 						"Test": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://127.0.0.1:80",
@@ -1363,7 +1363,7 @@ func Test_buildConfiguration(t *testing.T) {
 					Middlewares: map[string]*dynamic.Middleware{},
 					Services: map[string]*dynamic.Service{
 						"Test": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://127.0.0.1:80",
@@ -1450,7 +1450,7 @@ func Test_buildConfiguration(t *testing.T) {
 					Middlewares: map[string]*dynamic.Middleware{},
 					Services: map[string]*dynamic.Service{
 						"Test": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://127.0.0.1:80",
@@ -1526,7 +1526,7 @@ func Test_buildConfiguration(t *testing.T) {
 					Middlewares: map[string]*dynamic.Middleware{},
 					Services: map[string]*dynamic.Service{
 						"Test": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://127.0.0.1:80",
@@ -1592,7 +1592,7 @@ func Test_buildConfiguration(t *testing.T) {
 					Middlewares: map[string]*dynamic.Middleware{},
 					Services: map[string]*dynamic.Service{
 						"Test": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://127.0.0.1:80",
@@ -1602,7 +1602,7 @@ func Test_buildConfiguration(t *testing.T) {
 							},
 						},
 						"Test2": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://127.0.0.2:80",
@@ -1652,7 +1652,7 @@ func Test_buildConfiguration(t *testing.T) {
 					Middlewares: map[string]*dynamic.Middleware{},
 					Services: map[string]*dynamic.Service{
 						"Test": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://127.0.0.1:80",
@@ -1703,7 +1703,7 @@ func Test_buildConfiguration(t *testing.T) {
 					Middlewares: map[string]*dynamic.Middleware{},
 					Services: map[string]*dynamic.Service{
 						"Service1": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "h2c://127.0.0.1:8080",
@@ -1749,7 +1749,7 @@ func Test_buildConfiguration(t *testing.T) {
 					Middlewares: map[string]*dynamic.Middleware{},
 					Services: map[string]*dynamic.Service{
 						"Service1": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://127.0.0.1:80",
@@ -1759,7 +1759,7 @@ func Test_buildConfiguration(t *testing.T) {
 							},
 						},
 						"Service2": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://127.0.0.1:8080",
@@ -1974,7 +1974,7 @@ func Test_buildConfiguration(t *testing.T) {
 					Middlewares: map[string]*dynamic.Middleware{},
 					Services: map[string]*dynamic.Service{
 						"Test": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://127.0.0.1:80",
@@ -2035,7 +2035,7 @@ func Test_buildConfiguration(t *testing.T) {
 					},
 					Services: map[string]*dynamic.Service{
 						"Test": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://127.0.0.1:80",
@@ -2278,7 +2278,7 @@ func Test_buildConfiguration(t *testing.T) {
 					Middlewares: map[string]*dynamic.Middleware{},
 					Services: map[string]*dynamic.Service{
 						"Service1": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://127.0.0.1:80",
diff --git a/pkg/provider/kubernetes/crd/kubernetes_test.go b/pkg/provider/kubernetes/crd/kubernetes_test.go
index 75e7f122b..cb9d87408 100644
--- a/pkg/provider/kubernetes/crd/kubernetes_test.go
+++ b/pkg/provider/kubernetes/crd/kubernetes_test.go
@@ -12,6 +12,8 @@ import (
 
 var _ provider.Provider = (*Provider)(nil)
 
+func Int(v int) *int { return &v }
+
 func TestLoadIngressRouteTCPs(t *testing.T) {
 	testCases := []struct {
 		desc         string
@@ -671,7 +673,7 @@ func TestLoadIngressRoutes(t *testing.T) {
 					Middlewares: map[string]*dynamic.Middleware{},
 					Services: map[string]*dynamic.Service{
 						"default/test.route-6b204d94623b3df4370c": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://10.10.0.1:80",
@@ -720,7 +722,7 @@ func TestLoadIngressRoutes(t *testing.T) {
 					},
 					Services: map[string]*dynamic.Service{
 						"default/test2.route-23c7f4c450289ee29016": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://10.10.0.1:80",
@@ -770,7 +772,7 @@ func TestLoadIngressRoutes(t *testing.T) {
 					},
 					Services: map[string]*dynamic.Service{
 						"default/test2.route-23c7f4c450289ee29016": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://10.10.0.1:80",
@@ -812,7 +814,7 @@ func TestLoadIngressRoutes(t *testing.T) {
 					Middlewares: map[string]*dynamic.Middleware{},
 					Services: map[string]*dynamic.Service{
 						"default/test.route-6b204d94623b3df4370c": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://10.10.0.1:80",
@@ -825,7 +827,7 @@ func TestLoadIngressRoutes(t *testing.T) {
 							},
 						},
 						"default/test.route-77c62dfe9517144aeeaa": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://10.10.0.1:80",
@@ -843,7 +845,7 @@ func TestLoadIngressRoutes(t *testing.T) {
 			},
 		},
 		{
-			desc:  "One ingress Route with two different services, their servers will merge",
+			desc:  "One ingress Route with two different services",
 			paths: []string{"services.yml", "with_two_services.yml"},
 			expected: &dynamic.Configuration{
 				TLS: &dynamic.TLSConfiguration{},
@@ -863,7 +865,85 @@ func TestLoadIngressRoutes(t *testing.T) {
 					Middlewares: map[string]*dynamic.Middleware{},
 					Services: map[string]*dynamic.Service{
 						"default/test.route-77c62dfe9517144aeeaa": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							Weighted: &dynamic.WeightedRoundRobin{
+								Services: []dynamic.WRRService{
+									{
+										Name:   "default/test.route-77c62dfe9517144aeeaa-whoami-80",
+										Weight: func(i int) *int { return &i }(1),
+									},
+									{
+										Name:   "default/test.route-77c62dfe9517144aeeaa-whoami2-8080",
+										Weight: func(i int) *int { return &i }(1),
+									},
+								},
+							},
+						},
+						"default/test.route-77c62dfe9517144aeeaa-whoami-80": {
+							LoadBalancer: &dynamic.ServersLoadBalancer{
+								Servers: []dynamic.Server{
+									{
+										URL: "http://10.10.0.1:80",
+									},
+									{
+										URL: "http://10.10.0.2:80",
+									},
+								},
+								PassHostHeader: true,
+							},
+						},
+						"default/test.route-77c62dfe9517144aeeaa-whoami2-8080": {
+							LoadBalancer: &dynamic.ServersLoadBalancer{
+								Servers: []dynamic.Server{
+									{
+										URL: "http://10.10.0.3:8080",
+									},
+									{
+										URL: "http://10.10.0.4:8080",
+									},
+								},
+								PassHostHeader: true,
+							},
+						},
+					},
+				},
+			},
+		},
+		{
+			desc:  "One ingress Route with two different services, with weights",
+			paths: []string{"services.yml", "with_two_services_weight.yml"},
+			expected: &dynamic.Configuration{
+				TLS: &dynamic.TLSConfiguration{},
+				TCP: &dynamic.TCPConfiguration{
+					Routers:  map[string]*dynamic.TCPRouter{},
+					Services: map[string]*dynamic.TCPService{},
+				},
+				HTTP: &dynamic.HTTPConfiguration{
+					Routers: map[string]*dynamic.Router{
+						"default/test.route-77c62dfe9517144aeeaa": {
+							EntryPoints: []string{"web"},
+							Service:     "default/test.route-77c62dfe9517144aeeaa",
+							Rule:        "Host(`foo.com`) && PathPrefix(`/foo`)",
+							Priority:    12,
+						},
+					},
+					Middlewares: map[string]*dynamic.Middleware{},
+					Services: map[string]*dynamic.Service{
+						"default/test.route-77c62dfe9517144aeeaa": {
+							Weighted: &dynamic.WeightedRoundRobin{
+								Services: []dynamic.WRRService{
+									{
+										Name:   "default/test.route-77c62dfe9517144aeeaa-whoami-80",
+										Weight: Int(10),
+									},
+									{
+										Name:   "default/test.route-77c62dfe9517144aeeaa-whoami2-8080",
+										Weight: Int(0),
+									},
+								},
+							},
+						},
+						"default/test.route-77c62dfe9517144aeeaa-whoami-80": {
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://10.10.0.1:80",
@@ -871,6 +951,13 @@ func TestLoadIngressRoutes(t *testing.T) {
 									{
 										URL: "http://10.10.0.2:80",
 									},
+								},
+								PassHostHeader: true,
+							},
+						},
+						"default/test.route-77c62dfe9517144aeeaa-whoami2-8080": {
+							LoadBalancer: &dynamic.ServersLoadBalancer{
+								Servers: []dynamic.Server{
 									{
 										URL: "http://10.10.0.3:8080",
 									},
@@ -981,7 +1068,7 @@ func TestLoadIngressRoutes(t *testing.T) {
 					Middlewares: map[string]*dynamic.Middleware{},
 					Services: map[string]*dynamic.Service{
 						"default/test.route-6b204d94623b3df4370c": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://10.10.0.1:80",
@@ -1039,7 +1126,7 @@ func TestLoadIngressRoutes(t *testing.T) {
 					Middlewares: map[string]*dynamic.Middleware{},
 					Services: map[string]*dynamic.Service{
 						"default/test.route-6b204d94623b3df4370c": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://10.10.0.1:80",
@@ -1097,7 +1184,7 @@ func TestLoadIngressRoutes(t *testing.T) {
 					Middlewares: map[string]*dynamic.Middleware{},
 					Services: map[string]*dynamic.Service{
 						"default/test.route-6b204d94623b3df4370c": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://10.10.0.1:80",
@@ -1154,7 +1241,7 @@ func TestLoadIngressRoutes(t *testing.T) {
 					Middlewares: map[string]*dynamic.Middleware{},
 					Services: map[string]*dynamic.Service{
 						"default/test.route-6b204d94623b3df4370c": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://10.10.0.1:80",
@@ -1200,7 +1287,7 @@ func TestLoadIngressRoutes(t *testing.T) {
 					Middlewares: map[string]*dynamic.Middleware{},
 					Services: map[string]*dynamic.Service{
 						"default/test.route-6b204d94623b3df4370c": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://10.10.0.1:80",
@@ -1246,7 +1333,7 @@ func TestLoadIngressRoutes(t *testing.T) {
 					Middlewares: map[string]*dynamic.Middleware{},
 					Services: map[string]*dynamic.Service{
 						"default/test.route-6b204d94623b3df4370c": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://10.10.0.1:80",
@@ -1284,7 +1371,7 @@ func TestLoadIngressRoutes(t *testing.T) {
 					Middlewares: map[string]*dynamic.Middleware{},
 					Services: map[string]*dynamic.Service{
 						"default/test.route-6b204d94623b3df4370c": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://10.10.0.1:80",
@@ -1321,7 +1408,7 @@ func TestLoadIngressRoutes(t *testing.T) {
 					Middlewares: map[string]*dynamic.Middleware{},
 					Services: map[string]*dynamic.Service{
 						"default/test.route-6b204d94623b3df4370c": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "https://10.10.0.5:8443",
@@ -1358,7 +1445,7 @@ func TestLoadIngressRoutes(t *testing.T) {
 					Middlewares: map[string]*dynamic.Middleware{},
 					Services: map[string]*dynamic.Service{
 						"default/test.route-6b204d94623b3df4370c": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "https://10.10.0.7:8443",
diff --git a/pkg/provider/kubernetes/ingress/kubernetes_test.go b/pkg/provider/kubernetes/ingress/kubernetes_test.go
index d5ef16a2e..bc20a25b1 100644
--- a/pkg/provider/kubernetes/ingress/kubernetes_test.go
+++ b/pkg/provider/kubernetes/ingress/kubernetes_test.go
@@ -50,7 +50,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 					},
 					Services: map[string]*dynamic.Service{
 						"testing/service1/80": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								PassHostHeader: true,
 								Servers: []dynamic.Server{
 									{
@@ -84,7 +84,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 					},
 					Services: map[string]*dynamic.Service{
 						"testing/service1/80": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								PassHostHeader: true,
 								Servers: []dynamic.Server{
 									{
@@ -118,7 +118,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 					},
 					Services: map[string]*dynamic.Service{
 						"testing/service1/80": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								PassHostHeader: true,
 								Servers: []dynamic.Server{
 									{
@@ -148,7 +148,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 					},
 					Services: map[string]*dynamic.Service{
 						"testing/service1/80": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								PassHostHeader: true,
 								Servers: []dynamic.Server{
 									{
@@ -177,7 +177,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 					},
 					Services: map[string]*dynamic.Service{
 						"testing/example-com/80": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								PassHostHeader: true,
 								Servers: []dynamic.Server{
 									{
@@ -208,7 +208,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 					},
 					Services: map[string]*dynamic.Service{
 						"testing/service1/80": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								PassHostHeader: true,
 								Servers: []dynamic.Server{
 									{
@@ -242,7 +242,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 					},
 					Services: map[string]*dynamic.Service{
 						"testing/service1/80": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								PassHostHeader: true,
 								Servers: []dynamic.Server{
 									{
@@ -276,7 +276,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 					},
 					Services: map[string]*dynamic.Service{
 						"testing/service1/80": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								PassHostHeader: true,
 								Servers: []dynamic.Server{
 									{
@@ -317,7 +317,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 					},
 					Services: map[string]*dynamic.Service{
 						"testing/service1/80": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								PassHostHeader: true,
 								Servers: []dynamic.Server{
 									{
@@ -362,7 +362,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 					},
 					Services: map[string]*dynamic.Service{
 						"testing/service1/80": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								PassHostHeader: true,
 								Servers: []dynamic.Server{
 									{
@@ -375,7 +375,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 							},
 						},
 						"testing/service2/8082": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								PassHostHeader: true,
 								Servers: []dynamic.Server{
 									{
@@ -428,7 +428,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 					},
 					Services: map[string]*dynamic.Service{
 						"default-backend": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								PassHostHeader: true,
 								Servers: []dynamic.Server{
 									{
@@ -458,7 +458,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 					},
 					Services: map[string]*dynamic.Service{
 						"testing/service1/80": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								PassHostHeader: true,
 								Servers: []dynamic.Server{
 									{
@@ -488,7 +488,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 					},
 					Services: map[string]*dynamic.Service{
 						"testing/service1/tchouk": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								PassHostHeader: true,
 								Servers: []dynamic.Server{
 									{
@@ -518,7 +518,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 					},
 					Services: map[string]*dynamic.Service{
 						"testing/service1/tchouk": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								PassHostHeader: true,
 								Servers: []dynamic.Server{
 									{
@@ -552,7 +552,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 					},
 					Services: map[string]*dynamic.Service{
 						"testing/service1/tchouk": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								PassHostHeader: true,
 								Servers: []dynamic.Server{
 									{
@@ -565,7 +565,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 							},
 						},
 						"testing/service1/carotte": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								PassHostHeader: true,
 								Servers: []dynamic.Server{
 									{
@@ -599,7 +599,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 					},
 					Services: map[string]*dynamic.Service{
 						"testing/service1/tchouk": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								PassHostHeader: true,
 								Servers: []dynamic.Server{
 									{
@@ -612,7 +612,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 							},
 						},
 						"toto/service1/tchouk": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								PassHostHeader: true,
 								Servers: []dynamic.Server{
 									{
@@ -664,7 +664,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 					},
 					Services: map[string]*dynamic.Service{
 						"testing/service1/8080": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								PassHostHeader: true,
 								Servers: []dynamic.Server{
 									{
@@ -696,7 +696,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 					},
 					Services: map[string]*dynamic.Service{
 						"testing/example-com/80": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								PassHostHeader: true,
 								Servers: []dynamic.Server{
 									{
@@ -733,7 +733,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 					},
 					Services: map[string]*dynamic.Service{
 						"testing/service1/443": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								PassHostHeader: true,
 								Servers: []dynamic.Server{
 									{
@@ -763,7 +763,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 					},
 					Services: map[string]*dynamic.Service{
 						"testing/service1/8443": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								PassHostHeader: true,
 								Servers: []dynamic.Server{
 									{
@@ -794,7 +794,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 					},
 					Services: map[string]*dynamic.Service{
 						"testing/service1/8443": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								PassHostHeader: true,
 								Servers: []dynamic.Server{
 									{
@@ -825,7 +825,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 					},
 					Services: map[string]*dynamic.Service{
 						"default-backend": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								PassHostHeader: true,
 								Servers: []dynamic.Server{
 									{
@@ -855,7 +855,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 					},
 					Services: map[string]*dynamic.Service{
 						"testing/service1/80": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								PassHostHeader: true,
 								Servers: []dynamic.Server{
 									{
diff --git a/pkg/provider/marathon/config_test.go b/pkg/provider/marathon/config_test.go
index 8d9504e35..8bc35e958 100644
--- a/pkg/provider/marathon/config_test.go
+++ b/pkg/provider/marathon/config_test.go
@@ -56,7 +56,7 @@ func TestBuildConfiguration(t *testing.T) {
 					},
 					Middlewares: map[string]*dynamic.Middleware{},
 					Services: map[string]*dynamic.Service{
-						"app": {LoadBalancer: &dynamic.LoadBalancerService{
+						"app": {LoadBalancer: &dynamic.ServersLoadBalancer{
 							Servers: []dynamic.Server{
 								{
 									URL: "http://localhost:80",
@@ -110,7 +110,7 @@ func TestBuildConfiguration(t *testing.T) {
 					},
 					Middlewares: map[string]*dynamic.Middleware{},
 					Services: map[string]*dynamic.Service{
-						"app": {LoadBalancer: &dynamic.LoadBalancerService{
+						"app": {LoadBalancer: &dynamic.ServersLoadBalancer{
 							Servers: []dynamic.Server{
 								{
 									URL: "http://localhost:80",
@@ -156,7 +156,7 @@ func TestBuildConfiguration(t *testing.T) {
 						},
 					},
 					Services: map[string]*dynamic.Service{
-						"app": {LoadBalancer: &dynamic.LoadBalancerService{
+						"app": {LoadBalancer: &dynamic.ServersLoadBalancer{
 							Servers: []dynamic.Server{
 								{
 									URL: "http://localhost:80",
@@ -200,7 +200,7 @@ func TestBuildConfiguration(t *testing.T) {
 					},
 					Middlewares: map[string]*dynamic.Middleware{},
 					Services: map[string]*dynamic.Service{
-						"Service1": {LoadBalancer: &dynamic.LoadBalancerService{
+						"Service1": {LoadBalancer: &dynamic.ServersLoadBalancer{
 							Servers: []dynamic.Server{
 								{
 									URL: "http://localhost:8080",
@@ -249,7 +249,7 @@ func TestBuildConfiguration(t *testing.T) {
 					},
 					Middlewares: map[string]*dynamic.Middleware{},
 					Services: map[string]*dynamic.Service{
-						"Service1": {LoadBalancer: &dynamic.LoadBalancerService{
+						"Service1": {LoadBalancer: &dynamic.ServersLoadBalancer{
 							Servers: []dynamic.Server{
 								{
 									URL: "http://localhost:8080",
@@ -300,7 +300,7 @@ func TestBuildConfiguration(t *testing.T) {
 					},
 					Middlewares: map[string]*dynamic.Middleware{},
 					Services: map[string]*dynamic.Service{
-						"foo": {LoadBalancer: &dynamic.LoadBalancerService{
+						"foo": {LoadBalancer: &dynamic.ServersLoadBalancer{
 							Servers: []dynamic.Server{
 								{
 									URL: "http://localhost:8080",
@@ -308,7 +308,7 @@ func TestBuildConfiguration(t *testing.T) {
 							},
 							PassHostHeader: true,
 						}},
-						"bar": {LoadBalancer: &dynamic.LoadBalancerService{
+						"bar": {LoadBalancer: &dynamic.ServersLoadBalancer{
 							Servers: []dynamic.Server{
 								{
 									URL: "http://localhost:8081",
@@ -343,7 +343,7 @@ func TestBuildConfiguration(t *testing.T) {
 					Middlewares: map[string]*dynamic.Middleware{},
 					Services: map[string]*dynamic.Service{
 						"app": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://localhost:80",
@@ -382,7 +382,7 @@ func TestBuildConfiguration(t *testing.T) {
 					},
 					Middlewares: map[string]*dynamic.Middleware{},
 					Services: map[string]*dynamic.Service{
-						"Service1": {LoadBalancer: &dynamic.LoadBalancerService{
+						"Service1": {LoadBalancer: &dynamic.ServersLoadBalancer{
 							Servers: []dynamic.Server{
 								{
 									URL: "http://localhost:80",
@@ -420,7 +420,7 @@ func TestBuildConfiguration(t *testing.T) {
 					Middlewares: map[string]*dynamic.Middleware{},
 					Services: map[string]*dynamic.Service{
 						"Service1": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://localhost:80",
@@ -451,7 +451,7 @@ func TestBuildConfiguration(t *testing.T) {
 					Middlewares: map[string]*dynamic.Middleware{},
 					Services: map[string]*dynamic.Service{
 						"app": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://localhost:80",
@@ -495,7 +495,7 @@ func TestBuildConfiguration(t *testing.T) {
 					Middlewares: map[string]*dynamic.Middleware{},
 					Services: map[string]*dynamic.Service{
 						"Service1": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://localhost:80",
@@ -529,7 +529,7 @@ func TestBuildConfiguration(t *testing.T) {
 					Middlewares: map[string]*dynamic.Middleware{},
 					Services: map[string]*dynamic.Service{
 						"Service1": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://localhost:80",
@@ -539,7 +539,7 @@ func TestBuildConfiguration(t *testing.T) {
 							},
 						},
 						"Service2": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://localhost:80",
@@ -629,7 +629,7 @@ func TestBuildConfiguration(t *testing.T) {
 					},
 					Services: map[string]*dynamic.Service{
 						"app": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://localhost:80",
@@ -639,7 +639,7 @@ func TestBuildConfiguration(t *testing.T) {
 							},
 						},
 						"app2": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://localhost:80",
@@ -686,7 +686,7 @@ func TestBuildConfiguration(t *testing.T) {
 					Middlewares: map[string]*dynamic.Middleware{},
 					Services: map[string]*dynamic.Service{
 						"app": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://localhost:80",
@@ -696,7 +696,7 @@ func TestBuildConfiguration(t *testing.T) {
 							},
 						},
 						"app2": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://localhost:80",
@@ -734,7 +734,7 @@ func TestBuildConfiguration(t *testing.T) {
 					Middlewares: map[string]*dynamic.Middleware{},
 					Services: map[string]*dynamic.Service{
 						"app": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://localhost:80",
@@ -744,7 +744,7 @@ func TestBuildConfiguration(t *testing.T) {
 							},
 						},
 						"app2": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://localhost:80",
@@ -789,7 +789,7 @@ func TestBuildConfiguration(t *testing.T) {
 					Middlewares: map[string]*dynamic.Middleware{},
 					Services: map[string]*dynamic.Service{
 						"Service1": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://localhost:80",
@@ -830,7 +830,7 @@ func TestBuildConfiguration(t *testing.T) {
 					Middlewares: map[string]*dynamic.Middleware{},
 					Services: map[string]*dynamic.Service{
 						"app": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://localhost:80",
@@ -840,7 +840,7 @@ func TestBuildConfiguration(t *testing.T) {
 							},
 						},
 						"app2": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://localhost:80",
@@ -877,7 +877,7 @@ func TestBuildConfiguration(t *testing.T) {
 					Middlewares: map[string]*dynamic.Middleware{},
 					Services: map[string]*dynamic.Service{
 						"app": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://localhost:80",
@@ -915,7 +915,7 @@ func TestBuildConfiguration(t *testing.T) {
 					Middlewares: map[string]*dynamic.Middleware{},
 					Services: map[string]*dynamic.Service{
 						"Service1": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "h2c://localhost:90",
@@ -948,7 +948,7 @@ func TestBuildConfiguration(t *testing.T) {
 					Middlewares: map[string]*dynamic.Middleware{},
 					Services: map[string]*dynamic.Service{
 						"Service1": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://localhost:80",
@@ -958,7 +958,7 @@ func TestBuildConfiguration(t *testing.T) {
 							},
 						},
 						"Service2": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://localhost:8080",
@@ -1123,7 +1123,7 @@ func TestBuildConfiguration(t *testing.T) {
 					Middlewares: map[string]*dynamic.Middleware{},
 					Services: map[string]*dynamic.Service{
 						"app": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://localhost:80",
@@ -1161,7 +1161,7 @@ func TestBuildConfiguration(t *testing.T) {
 					Middlewares: map[string]*dynamic.Middleware{},
 					Services: map[string]*dynamic.Service{
 						"app": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://localhost:80",
@@ -1198,7 +1198,7 @@ func TestBuildConfiguration(t *testing.T) {
 					Middlewares: map[string]*dynamic.Middleware{},
 					Services: map[string]*dynamic.Service{
 						"a_b_app": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://localhost:80",
@@ -1362,7 +1362,7 @@ func TestBuildConfiguration(t *testing.T) {
 					Middlewares: map[string]*dynamic.Middleware{},
 					Services: map[string]*dynamic.Service{
 						"bar": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://localhost:80",
diff --git a/pkg/provider/rancher/config_test.go b/pkg/provider/rancher/config_test.go
index cc40a74c6..31fb29ddc 100644
--- a/pkg/provider/rancher/config_test.go
+++ b/pkg/provider/rancher/config_test.go
@@ -43,7 +43,7 @@ func Test_buildConfiguration(t *testing.T) {
 					Middlewares: map[string]*dynamic.Middleware{},
 					Services: map[string]*dynamic.Service{
 						"Test": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://127.0.0.1:80",
@@ -95,7 +95,7 @@ func Test_buildConfiguration(t *testing.T) {
 					Middlewares: map[string]*dynamic.Middleware{},
 					Services: map[string]*dynamic.Service{
 						"Test1": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://127.0.0.1:80",
@@ -105,7 +105,7 @@ func Test_buildConfiguration(t *testing.T) {
 							},
 						},
 						"Test2": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://127.0.0.2:80",
@@ -157,7 +157,7 @@ func Test_buildConfiguration(t *testing.T) {
 					Middlewares: map[string]*dynamic.Middleware{},
 					Services: map[string]*dynamic.Service{
 						"Test1": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://127.0.0.1:80",
@@ -170,7 +170,7 @@ func Test_buildConfiguration(t *testing.T) {
 							},
 						},
 						"Test2": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://128.0.0.1:80",
@@ -214,7 +214,7 @@ func Test_buildConfiguration(t *testing.T) {
 					Middlewares: map[string]*dynamic.Middleware{},
 					Services: map[string]*dynamic.Service{
 						"Service1": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://127.0.0.1:80",
@@ -302,7 +302,7 @@ func Test_buildConfiguration(t *testing.T) {
 					Middlewares: map[string]*dynamic.Middleware{},
 					Services: map[string]*dynamic.Service{
 						"Test": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://127.0.0.1:80",
@@ -372,7 +372,7 @@ func Test_buildConfiguration(t *testing.T) {
 					Middlewares: map[string]*dynamic.Middleware{},
 					Services: map[string]*dynamic.Service{
 						"Test": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://127.0.0.1:80",
@@ -425,7 +425,7 @@ func Test_buildConfiguration(t *testing.T) {
 					},
 					Services: map[string]*dynamic.Service{
 						"Test": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://127.0.0.1:80",
@@ -467,7 +467,7 @@ func Test_buildConfiguration(t *testing.T) {
 					Middlewares: map[string]*dynamic.Middleware{},
 					Services: map[string]*dynamic.Service{
 						"Test": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://127.0.0.1:80",
@@ -652,7 +652,7 @@ func Test_buildConfiguration(t *testing.T) {
 					Middlewares: map[string]*dynamic.Middleware{},
 					Services: map[string]*dynamic.Service{
 						"Service1": {
-							LoadBalancer: &dynamic.LoadBalancerService{
+							LoadBalancer: &dynamic.ServersLoadBalancer{
 								Servers: []dynamic.Server{
 									{
 										URL: "http://127.0.0.1:80",
diff --git a/pkg/server/router/router_test.go b/pkg/server/router/router_test.go
index 02b8606e5..66b11487d 100644
--- a/pkg/server/router/router_test.go
+++ b/pkg/server/router/router_test.go
@@ -48,7 +48,7 @@ func TestRouterManager_Get(t *testing.T) {
 			},
 			serviceConfig: map[string]*dynamic.Service{
 				"foo-service": {
-					LoadBalancer: &dynamic.LoadBalancerService{
+					LoadBalancer: &dynamic.ServersLoadBalancer{
 						Servers: []dynamic.Server{
 							{
 								URL: server.URL,
@@ -85,7 +85,7 @@ func TestRouterManager_Get(t *testing.T) {
 			},
 			serviceConfig: map[string]*dynamic.Service{
 				"foo-service": {
-					LoadBalancer: &dynamic.LoadBalancerService{
+					LoadBalancer: &dynamic.ServersLoadBalancer{
 						Servers: []dynamic.Server{
 							{
 								URL: server.URL,
@@ -108,7 +108,7 @@ func TestRouterManager_Get(t *testing.T) {
 			},
 			serviceConfig: map[string]*dynamic.Service{
 				"foo-service": {
-					LoadBalancer: &dynamic.LoadBalancerService{
+					LoadBalancer: &dynamic.ServersLoadBalancer{
 						Servers: []dynamic.Server{
 							{
 								URL: server.URL,
@@ -132,7 +132,7 @@ func TestRouterManager_Get(t *testing.T) {
 			},
 			serviceConfig: map[string]*dynamic.Service{
 				"foo-service": {
-					LoadBalancer: &dynamic.LoadBalancerService{
+					LoadBalancer: &dynamic.ServersLoadBalancer{
 						Servers: []dynamic.Server{
 							{
 								URL: server.URL,
@@ -173,7 +173,7 @@ func TestRouterManager_Get(t *testing.T) {
 			},
 			serviceConfig: map[string]*dynamic.Service{
 				"foo-service": {
-					LoadBalancer: &dynamic.LoadBalancerService{
+					LoadBalancer: &dynamic.ServersLoadBalancer{
 						Servers: []dynamic.Server{
 							{
 								URL: server.URL,
@@ -213,7 +213,7 @@ func TestRouterManager_Get(t *testing.T) {
 			},
 			serviceConfig: map[string]*dynamic.Service{
 				"foo-service@provider-1": {
-					LoadBalancer: &dynamic.LoadBalancerService{
+					LoadBalancer: &dynamic.ServersLoadBalancer{
 						Servers: []dynamic.Server{
 							{
 								URL: server.URL,
@@ -236,7 +236,7 @@ func TestRouterManager_Get(t *testing.T) {
 			},
 			serviceConfig: map[string]*dynamic.Service{
 				"foo-service@provider-2": {
-					LoadBalancer: &dynamic.LoadBalancerService{
+					LoadBalancer: &dynamic.ServersLoadBalancer{
 						Servers: []dynamic.Server{
 							{
 								URL: server.URL,
@@ -260,7 +260,7 @@ func TestRouterManager_Get(t *testing.T) {
 			},
 			serviceConfig: map[string]*dynamic.Service{
 				"foo-service@provider-1": {
-					LoadBalancer: &dynamic.LoadBalancerService{
+					LoadBalancer: &dynamic.ServersLoadBalancer{
 						Servers: []dynamic.Server{
 							{
 								URL: server.URL,
@@ -355,7 +355,7 @@ func TestAccessLog(t *testing.T) {
 			},
 			serviceConfig: map[string]*dynamic.Service{
 				"foo-service": {
-					LoadBalancer: &dynamic.LoadBalancerService{
+					LoadBalancer: &dynamic.ServersLoadBalancer{
 						Servers: []dynamic.Server{
 							{
 								URL: server.URL,
@@ -383,7 +383,7 @@ func TestAccessLog(t *testing.T) {
 			},
 			serviceConfig: map[string]*dynamic.Service{
 				"foo-service": {
-					LoadBalancer: &dynamic.LoadBalancerService{
+					LoadBalancer: &dynamic.ServersLoadBalancer{
 						Servers: []dynamic.Server{
 							{
 								URL: server.URL,
@@ -448,7 +448,7 @@ func TestRuntimeConfiguration(t *testing.T) {
 			desc: "No error",
 			serviceConfig: map[string]*dynamic.Service{
 				"foo-service": {
-					LoadBalancer: &dynamic.LoadBalancerService{
+					LoadBalancer: &dynamic.ServersLoadBalancer{
 						Servers: []dynamic.Server{
 							{
 								URL: "http://127.0.0.1:8085",
@@ -482,7 +482,7 @@ func TestRuntimeConfiguration(t *testing.T) {
 			desc: "One router with wrong rule",
 			serviceConfig: map[string]*dynamic.Service{
 				"foo-service": {
-					LoadBalancer: &dynamic.LoadBalancerService{
+					LoadBalancer: &dynamic.ServersLoadBalancer{
 						Servers: []dynamic.Server{
 							{
 								URL: "http://127.0.0.1",
@@ -509,7 +509,7 @@ func TestRuntimeConfiguration(t *testing.T) {
 			desc: "All router with wrong rule",
 			serviceConfig: map[string]*dynamic.Service{
 				"foo-service": {
-					LoadBalancer: &dynamic.LoadBalancerService{
+					LoadBalancer: &dynamic.ServersLoadBalancer{
 						Servers: []dynamic.Server{
 							{
 								URL: "http://127.0.0.1",
@@ -536,7 +536,7 @@ func TestRuntimeConfiguration(t *testing.T) {
 			desc: "Router with unknown service",
 			serviceConfig: map[string]*dynamic.Service{
 				"foo-service": {
-					LoadBalancer: &dynamic.LoadBalancerService{
+					LoadBalancer: &dynamic.ServersLoadBalancer{
 						Servers: []dynamic.Server{
 							{
 								URL: "http://127.0.0.1",
@@ -579,7 +579,7 @@ func TestRuntimeConfiguration(t *testing.T) {
 			desc: "Router with middleware",
 			serviceConfig: map[string]*dynamic.Service{
 				"foo-service": {
-					LoadBalancer: &dynamic.LoadBalancerService{
+					LoadBalancer: &dynamic.ServersLoadBalancer{
 						Servers: []dynamic.Server{
 							{
 								URL: "http://127.0.0.1",
@@ -619,7 +619,7 @@ func TestRuntimeConfiguration(t *testing.T) {
 			desc: "Router with unknown middleware",
 			serviceConfig: map[string]*dynamic.Service{
 				"foo-service": {
-					LoadBalancer: &dynamic.LoadBalancerService{
+					LoadBalancer: &dynamic.ServersLoadBalancer{
 						Servers: []dynamic.Server{
 							{
 								URL: "http://127.0.0.1",
@@ -650,7 +650,7 @@ func TestRuntimeConfiguration(t *testing.T) {
 			desc: "Router with broken middleware",
 			serviceConfig: map[string]*dynamic.Service{
 				"foo-service": {
-					LoadBalancer: &dynamic.LoadBalancerService{
+					LoadBalancer: &dynamic.ServersLoadBalancer{
 						Servers: []dynamic.Server{
 							{
 								URL: "http://127.0.0.1",
@@ -749,7 +749,7 @@ func BenchmarkRouterServe(b *testing.B) {
 	}
 	serviceConfig := map[string]*dynamic.Service{
 		"foo-service": {
-			LoadBalancer: &dynamic.LoadBalancerService{
+			LoadBalancer: &dynamic.ServersLoadBalancer{
 				Servers: []dynamic.Server{
 					{
 						URL: server.URL,
@@ -793,7 +793,7 @@ func BenchmarkService(b *testing.B) {
 
 	serviceConfig := map[string]*dynamic.Service{
 		"foo-service": {
-			LoadBalancer: &dynamic.LoadBalancerService{
+			LoadBalancer: &dynamic.ServersLoadBalancer{
 				Servers: []dynamic.Server{
 					{
 						URL: "tchouck",
diff --git a/pkg/server/server_test.go b/pkg/server/server_test.go
index 9f0913742..868e282be 100644
--- a/pkg/server/server_test.go
+++ b/pkg/server/server_test.go
@@ -199,7 +199,7 @@ func TestServerResponseEmptyBackend(t *testing.T) {
 						th.WithRule(routeRule)),
 					),
 					th.WithLoadBalancerServices(th.WithService("bar",
-						th.WithStickiness("test")),
+						th.WithSticky("test")),
 					),
 				)
 			},
@@ -229,7 +229,7 @@ func TestServerResponseEmptyBackend(t *testing.T) {
 						th.WithRule(routeRule)),
 					),
 					th.WithLoadBalancerServices(th.WithService("bar",
-						th.WithStickiness("test")),
+						th.WithSticky("test")),
 					),
 				)
 			},
diff --git a/pkg/server/service/loadbalancer/wrr/wrr_test.go b/pkg/server/service/loadbalancer/wrr/wrr_test.go
new file mode 100644
index 000000000..b2eb65e5a
--- /dev/null
+++ b/pkg/server/service/loadbalancer/wrr/wrr_test.go
@@ -0,0 +1,115 @@
+package wrr
+
+import (
+	"net/http"
+	"net/http/httptest"
+	"testing"
+
+	"github.com/containous/traefik/v2/pkg/config/dynamic"
+	"github.com/stretchr/testify/assert"
+)
+
+func Int(v int) *int { return &v }
+
+type responseRecorder struct {
+	*httptest.ResponseRecorder
+	save map[string]int
+}
+
+func (r *responseRecorder) WriteHeader(statusCode int) {
+	r.save[r.Header().Get("server")]++
+	r.ResponseRecorder.WriteHeader(statusCode)
+
+}
+
+func TestBalancer(t *testing.T) {
+	balancer := New(nil)
+
+	balancer.AddService("first", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
+		rw.Header().Set("server", "first")
+		rw.WriteHeader(http.StatusOK)
+	}), Int(3))
+
+	balancer.AddService("second", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
+		rw.Header().Set("server", "second")
+		rw.WriteHeader(http.StatusOK)
+	}), Int(1))
+
+	recorder := &responseRecorder{ResponseRecorder: httptest.NewRecorder(), save: map[string]int{}}
+	for i := 0; i < 4; i++ {
+		balancer.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/", nil))
+	}
+
+	assert.Equal(t, 3, recorder.save["first"])
+	assert.Equal(t, 1, recorder.save["second"])
+}
+
+func TestBalancerNoService(t *testing.T) {
+	balancer := New(nil)
+
+	recorder := httptest.NewRecorder()
+	balancer.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/", nil))
+
+	assert.Equal(t, http.StatusInternalServerError, recorder.Result().StatusCode)
+}
+
+func TestBalancerOneServerZeroWeight(t *testing.T) {
+	balancer := New(nil)
+
+	balancer.AddService("first", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
+		rw.Header().Set("server", "first")
+		rw.WriteHeader(http.StatusOK)
+	}), Int(1))
+
+	balancer.AddService("second", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {}), Int(0))
+
+	recorder := &responseRecorder{ResponseRecorder: httptest.NewRecorder(), save: map[string]int{}}
+	for i := 0; i < 3; i++ {
+		balancer.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/", nil))
+	}
+
+	assert.Equal(t, 3, recorder.save["first"])
+}
+
+func TestBalancerAllServersZeroWeight(t *testing.T) {
+	balancer := New(nil)
+
+	balancer.AddService("test", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {}), Int(0))
+	balancer.AddService("test2", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {}), Int(0))
+
+	recorder := httptest.NewRecorder()
+	balancer.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/", nil))
+
+	assert.Equal(t, http.StatusInternalServerError, recorder.Result().StatusCode)
+}
+
+func TestSticky(t *testing.T) {
+	balancer := New(&dynamic.Sticky{
+		Cookie: &dynamic.Cookie{Name: "test"},
+	})
+
+	balancer.AddService("first", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
+		rw.Header().Set("server", "first")
+		rw.WriteHeader(http.StatusOK)
+	}), Int(1))
+
+	balancer.AddService("second", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
+		rw.Header().Set("server", "second")
+		rw.WriteHeader(http.StatusOK)
+	}), Int(2))
+
+	recorder := &responseRecorder{ResponseRecorder: httptest.NewRecorder(), save: map[string]int{}}
+
+	req := httptest.NewRequest(http.MethodGet, "/", nil)
+	for i := 0; i < 3; i++ {
+		for _, cookie := range recorder.Result().Cookies() {
+			req.AddCookie(cookie)
+		}
+		recorder.ResponseRecorder = httptest.NewRecorder()
+
+		balancer.ServeHTTP(recorder, req)
+	}
+
+	assert.Equal(t, 0, recorder.save["first"])
+	assert.Equal(t, 3, recorder.save["second"])
+}
diff --git a/pkg/server/service/service_test.go b/pkg/server/service/service_test.go
index a075b5cae..e150d0cc7 100644
--- a/pkg/server/service/service_test.go
+++ b/pkg/server/service/service_test.go
@@ -27,14 +27,14 @@ func TestGetLoadBalancer(t *testing.T) {
 	testCases := []struct {
 		desc        string
 		serviceName string
-		service     *dynamic.LoadBalancerService
+		service     *dynamic.ServersLoadBalancer
 		fwd         http.Handler
 		expectError bool
 	}{
 		{
 			desc:        "Fails when provided an invalid URL",
 			serviceName: "test",
-			service: &dynamic.LoadBalancerService{
+			service: &dynamic.ServersLoadBalancer{
 				Servers: []dynamic.Server{
 					{
 						URL: ":",
@@ -47,15 +47,15 @@ func TestGetLoadBalancer(t *testing.T) {
 		{
 			desc:        "Succeeds when there are no servers",
 			serviceName: "test",
-			service:     &dynamic.LoadBalancerService{},
+			service:     &dynamic.ServersLoadBalancer{},
 			fwd:         &MockForwarder{},
 			expectError: false,
 		},
 		{
-			desc:        "Succeeds when stickiness is set",
+			desc:        "Succeeds when sticky.cookie is set",
 			serviceName: "test",
-			service: &dynamic.LoadBalancerService{
-				Stickiness: &dynamic.Stickiness{},
+			service: &dynamic.ServersLoadBalancer{
+				Sticky: &dynamic.Sticky{Cookie: &dynamic.Cookie{}},
 			},
 			fwd:         &MockForwarder{},
 			expectError: false,
@@ -114,7 +114,7 @@ func TestGetLoadBalancerServiceHandler(t *testing.T) {
 	testCases := []struct {
 		desc             string
 		serviceName      string
-		service          *dynamic.LoadBalancerService
+		service          *dynamic.ServersLoadBalancer
 		responseModifier func(*http.Response) error
 
 		expected []ExpectedResult
@@ -122,7 +122,7 @@ func TestGetLoadBalancerServiceHandler(t *testing.T) {
 		{
 			desc:        "Load balances between the two servers",
 			serviceName: "test",
-			service: &dynamic.LoadBalancerService{
+			service: &dynamic.ServersLoadBalancer{
 				Servers: []dynamic.Server{
 					{
 						URL: server1.URL,
@@ -146,7 +146,7 @@ func TestGetLoadBalancerServiceHandler(t *testing.T) {
 		{
 			desc:        "StatusBadGateway when the server is not reachable",
 			serviceName: "test",
-			service: &dynamic.LoadBalancerService{
+			service: &dynamic.ServersLoadBalancer{
 				Servers: []dynamic.Server{
 					{
 						URL: "http://foo",
@@ -162,7 +162,7 @@ func TestGetLoadBalancerServiceHandler(t *testing.T) {
 		{
 			desc:        "ServiceUnavailable when no servers are available",
 			serviceName: "test",
-			service: &dynamic.LoadBalancerService{
+			service: &dynamic.ServersLoadBalancer{
 				Servers: []dynamic.Server{},
 			},
 			expected: []ExpectedResult{
@@ -172,10 +172,10 @@ func TestGetLoadBalancerServiceHandler(t *testing.T) {
 			},
 		},
 		{
-			desc:        "Always call the same server when stickiness is true",
+			desc:        "Always call the same server when sticky.cookie is true",
 			serviceName: "test",
-			service: &dynamic.LoadBalancerService{
-				Stickiness: &dynamic.Stickiness{},
+			service: &dynamic.ServersLoadBalancer{
+				Sticky: &dynamic.Sticky{Cookie: &dynamic.Cookie{}},
 				Servers: []dynamic.Server{
 					{
 						URL: server1.URL,
@@ -199,8 +199,8 @@ func TestGetLoadBalancerServiceHandler(t *testing.T) {
 		{
 			desc:        "Sticky Cookie's options set correctly",
 			serviceName: "test",
-			service: &dynamic.LoadBalancerService{
-				Stickiness: &dynamic.Stickiness{HTTPOnlyCookie: true, SecureCookie: true},
+			service: &dynamic.ServersLoadBalancer{
+				Sticky: &dynamic.Sticky{Cookie: &dynamic.Cookie{HTTPOnly: true, Secure: true}},
 				Servers: []dynamic.Server{
 					{
 						URL: server1.URL,
@@ -219,8 +219,8 @@ func TestGetLoadBalancerServiceHandler(t *testing.T) {
 		{
 			desc:        "PassHost passes the host instead of the IP",
 			serviceName: "test",
-			service: &dynamic.LoadBalancerService{
-				Stickiness:     &dynamic.Stickiness{},
+			service: &dynamic.ServersLoadBalancer{
+				Sticky:         &dynamic.Sticky{Cookie: &dynamic.Cookie{}},
 				PassHostHeader: true,
 				Servers: []dynamic.Server{
 					{
@@ -238,8 +238,8 @@ func TestGetLoadBalancerServiceHandler(t *testing.T) {
 		{
 			desc:        "PassHost doesn't passe the host instead of the IP",
 			serviceName: "test",
-			service: &dynamic.LoadBalancerService{
-				Stickiness: &dynamic.Stickiness{},
+			service: &dynamic.ServersLoadBalancer{
+				Sticky: &dynamic.Sticky{Cookie: &dynamic.Cookie{}},
 				Servers: []dynamic.Server{
 					{
 						URL: serverPassHostFalse.URL,
@@ -297,7 +297,7 @@ func TestManager_Build(t *testing.T) {
 			configs: map[string]*runtime.ServiceInfo{
 				"serviceName": {
 					Service: &dynamic.Service{
-						LoadBalancer: &dynamic.LoadBalancerService{},
+						LoadBalancer: &dynamic.ServersLoadBalancer{},
 					},
 				},
 			},
@@ -308,7 +308,7 @@ func TestManager_Build(t *testing.T) {
 			configs: map[string]*runtime.ServiceInfo{
 				"serviceName@provider-1": {
 					Service: &dynamic.Service{
-						LoadBalancer: &dynamic.LoadBalancerService{},
+						LoadBalancer: &dynamic.ServersLoadBalancer{},
 					},
 				},
 			},
@@ -319,7 +319,7 @@ func TestManager_Build(t *testing.T) {
 			configs: map[string]*runtime.ServiceInfo{
 				"serviceName@provider-1": {
 					Service: &dynamic.Service{
-						LoadBalancer: &dynamic.LoadBalancerService{},
+						LoadBalancer: &dynamic.ServersLoadBalancer{},
 					},
 				},
 			},
diff --git a/pkg/testhelpers/config.go b/pkg/testhelpers/config.go
index e0f9b807b..5f6c48165 100644
--- a/pkg/testhelpers/config.go
+++ b/pkg/testhelpers/config.go
@@ -50,11 +50,11 @@ func WithServiceName(serviceName string) func(*dynamic.Router) {
 }
 
 // WithLoadBalancerServices is a helper to create a configuration.
-func WithLoadBalancerServices(opts ...func(service *dynamic.LoadBalancerService) string) func(*dynamic.HTTPConfiguration) {
+func WithLoadBalancerServices(opts ...func(service *dynamic.ServersLoadBalancer) string) func(*dynamic.HTTPConfiguration) {
 	return func(c *dynamic.HTTPConfiguration) {
 		c.Services = make(map[string]*dynamic.Service)
 		for _, opt := range opts {
-			b := &dynamic.LoadBalancerService{}
+			b := &dynamic.ServersLoadBalancer{}
 			name := opt(b)
 			c.Services[name] = &dynamic.Service{
 				LoadBalancer: b,
@@ -64,8 +64,8 @@ func WithLoadBalancerServices(opts ...func(service *dynamic.LoadBalancerService)
 }
 
 // WithService is a helper to create a configuration.
-func WithService(name string, opts ...func(*dynamic.LoadBalancerService)) func(*dynamic.LoadBalancerService) string {
-	return func(r *dynamic.LoadBalancerService) string {
+func WithService(name string, opts ...func(*dynamic.ServersLoadBalancer)) func(*dynamic.ServersLoadBalancer) string {
+	return func(r *dynamic.ServersLoadBalancer) string {
 		for _, opt := range opts {
 			opt(r)
 		}
@@ -117,8 +117,8 @@ func WithRule(rule string) func(*dynamic.Router) {
 }
 
 // WithServers is a helper to create a configuration.
-func WithServers(opts ...func(*dynamic.Server)) func(*dynamic.LoadBalancerService) {
-	return func(b *dynamic.LoadBalancerService) {
+func WithServers(opts ...func(*dynamic.Server)) func(*dynamic.ServersLoadBalancer) {
+	return func(b *dynamic.ServersLoadBalancer) {
 		for _, opt := range opts {
 			server := dynamic.Server{}
 			opt(&server)
@@ -137,11 +137,11 @@ func WithServer(url string, opts ...func(*dynamic.Server)) func(*dynamic.Server)
 	}
 }
 
-// WithStickiness is a helper to create a configuration.
-func WithStickiness(cookieName string) func(*dynamic.LoadBalancerService) {
-	return func(b *dynamic.LoadBalancerService) {
-		b.Stickiness = &dynamic.Stickiness{
-			CookieName: cookieName,
+// WithSticky is a helper to create a configuration.
+func WithSticky(cookieName string) func(*dynamic.ServersLoadBalancer) {
+	return func(b *dynamic.ServersLoadBalancer) {
+		b.Sticky = &dynamic.Sticky{
+			Cookie: &dynamic.Cookie{Name: cookieName},
 		}
 	}
 }