diff --git a/pkg/config/runtime/runtime.go b/pkg/config/runtime/runtime.go
index c64b22cbe4d4f5013eda30809b45c3faca646936..b59e768b84fa019827edd92da7503b09b5c30b93 100644
--- a/pkg/config/runtime/runtime.go
+++ b/pkg/config/runtime/runtime.go
@@ -1,10 +1,8 @@
 package runtime
 
 import (
-	"context"
 	"sort"
 	"strings"
-	"sync"
 
 	"github.com/containous/traefik/v2/pkg/config/dynamic"
 	"github.com/containous/traefik/v2/pkg/log"
@@ -171,255 +169,6 @@ func contains(entryPoints []string, entryPointName string) bool {
 	return false
 }
 
-// GetRoutersByEntryPoints returns all the http routers by entry points name and routers name
-func (c *Configuration) GetRoutersByEntryPoints(ctx context.Context, entryPoints []string, tls bool) map[string]map[string]*RouterInfo {
-	entryPointsRouters := make(map[string]map[string]*RouterInfo)
-
-	for rtName, rt := range c.Routers {
-		if (tls && rt.TLS == nil) || (!tls && rt.TLS != nil) {
-			continue
-		}
-
-		eps := rt.EntryPoints
-		if len(eps) == 0 {
-			eps = entryPoints
-		}
-		for _, entryPointName := range eps {
-			if !contains(entryPoints, entryPointName) {
-				log.FromContext(log.With(ctx, log.Str(log.EntryPointName, entryPointName))).
-					Errorf("entryPoint %q doesn't exist", entryPointName)
-				continue
-			}
-
-			if _, ok := entryPointsRouters[entryPointName]; !ok {
-				entryPointsRouters[entryPointName] = make(map[string]*RouterInfo)
-			}
-
-			entryPointsRouters[entryPointName][rtName] = rt
-		}
-	}
-
-	return entryPointsRouters
-}
-
-// GetTCPRoutersByEntryPoints returns all the tcp routers by entry points name and routers name
-func (c *Configuration) GetTCPRoutersByEntryPoints(ctx context.Context, entryPoints []string) map[string]map[string]*TCPRouterInfo {
-	entryPointsRouters := make(map[string]map[string]*TCPRouterInfo)
-
-	for rtName, rt := range c.TCPRouters {
-		eps := rt.EntryPoints
-		if len(eps) == 0 {
-			eps = entryPoints
-		}
-
-		for _, entryPointName := range eps {
-			if !contains(entryPoints, entryPointName) {
-				log.FromContext(log.With(ctx, log.Str(log.EntryPointName, entryPointName))).
-					Errorf("entryPoint %q doesn't exist", entryPointName)
-				continue
-			}
-
-			if _, ok := entryPointsRouters[entryPointName]; !ok {
-				entryPointsRouters[entryPointName] = make(map[string]*TCPRouterInfo)
-			}
-
-			entryPointsRouters[entryPointName][rtName] = rt
-		}
-	}
-
-	return entryPointsRouters
-}
-
-// RouterInfo holds information about a currently running HTTP router
-type RouterInfo struct {
-	*dynamic.Router // dynamic configuration
-	// Err contains all the errors that occurred during router's creation.
-	Err []string `json:"error,omitempty"`
-	// Status reports whether the router is disabled, in a warning state, or all good (enabled).
-	// If not in "enabled" state, the reason for it should be in the list of Err.
-	// It is the caller's responsibility to set the initial status.
-	Status string `json:"status,omitempty"`
-}
-
-// AddError adds err to r.Err, if it does not already exist.
-// If critical is set, r is marked as disabled.
-func (r *RouterInfo) AddError(err error, critical bool) {
-	for _, value := range r.Err {
-		if value == err.Error() {
-			return
-		}
-	}
-
-	r.Err = append(r.Err, err.Error())
-	if critical {
-		r.Status = StatusDisabled
-		return
-	}
-
-	// only set it to "warning" if not already in a worse state
-	if r.Status != StatusDisabled {
-		r.Status = StatusWarning
-	}
-}
-
-// TCPRouterInfo holds information about a currently running TCP router
-type TCPRouterInfo struct {
-	*dynamic.TCPRouter          // dynamic configuration
-	Err                []string `json:"error,omitempty"` // initialization error
-	// Status reports whether the router is disabled, in a warning state, or all good (enabled).
-	// If not in "enabled" state, the reason for it should be in the list of Err.
-	// It is the caller's responsibility to set the initial status.
-	Status string `json:"status,omitempty"`
-}
-
-// AddError adds err to r.Err, if it does not already exist.
-// If critical is set, r is marked as disabled.
-func (r *TCPRouterInfo) AddError(err error, critical bool) {
-	for _, value := range r.Err {
-		if value == err.Error() {
-			return
-		}
-	}
-
-	r.Err = append(r.Err, err.Error())
-	if critical {
-		r.Status = StatusDisabled
-		return
-	}
-
-	// only set it to "warning" if not already in a worse state
-	if r.Status != StatusDisabled {
-		r.Status = StatusWarning
-	}
-}
-
-// MiddlewareInfo holds information about a currently running middleware
-type MiddlewareInfo struct {
-	*dynamic.Middleware // dynamic configuration
-	// Err contains all the errors that occurred during service creation.
-	Err    []string `json:"error,omitempty"`
-	Status string   `json:"status,omitempty"`
-	UsedBy []string `json:"usedBy,omitempty"` // list of routers and services using that middleware
-}
-
-// AddError adds err to s.Err, if it does not already exist.
-// If critical is set, m is marked as disabled.
-func (m *MiddlewareInfo) AddError(err error, critical bool) {
-	for _, value := range m.Err {
-		if value == err.Error() {
-			return
-		}
-	}
-
-	m.Err = append(m.Err, err.Error())
-	if critical {
-		m.Status = StatusDisabled
-		return
-	}
-
-	// only set it to "warning" if not already in a worse state
-	if m.Status != StatusDisabled {
-		m.Status = StatusWarning
-	}
-}
-
-// ServiceInfo holds information about a currently running service
-type ServiceInfo struct {
-	*dynamic.Service // dynamic configuration
-	// Err contains all the errors that occurred during service creation.
-	Err []string `json:"error,omitempty"`
-	// Status reports whether the service is disabled, in a warning state, or all good (enabled).
-	// If not in "enabled" state, the reason for it should be in the list of Err.
-	// It is the caller's responsibility to set the initial status.
-	Status string   `json:"status,omitempty"`
-	UsedBy []string `json:"usedBy,omitempty"` // list of routers using that service
-
-	serverStatusMu sync.RWMutex
-	serverStatus   map[string]string // keyed by server URL
-}
-
-// AddError adds err to s.Err, if it does not already exist.
-// If critical is set, s is marked as disabled.
-func (s *ServiceInfo) AddError(err error, critical bool) {
-	for _, value := range s.Err {
-		if value == err.Error() {
-			return
-		}
-	}
-
-	s.Err = append(s.Err, err.Error())
-	if critical {
-		s.Status = StatusDisabled
-		return
-	}
-
-	// only set it to "warning" if not already in a worse state
-	if s.Status != StatusDisabled {
-		s.Status = StatusWarning
-	}
-}
-
-// UpdateServerStatus sets the status of the server in the ServiceInfo.
-// It is the responsibility of the caller to check that s is not nil.
-func (s *ServiceInfo) UpdateServerStatus(server string, status string) {
-	s.serverStatusMu.Lock()
-	defer s.serverStatusMu.Unlock()
-
-	if s.serverStatus == nil {
-		s.serverStatus = make(map[string]string)
-	}
-	s.serverStatus[server] = status
-}
-
-// GetAllStatus returns all the statuses of all the servers in ServiceInfo.
-// It is the responsibility of the caller to check that s is not nil
-func (s *ServiceInfo) GetAllStatus() map[string]string {
-	s.serverStatusMu.RLock()
-	defer s.serverStatusMu.RUnlock()
-
-	if len(s.serverStatus) == 0 {
-		return nil
-	}
-
-	allStatus := make(map[string]string, len(s.serverStatus))
-	for k, v := range s.serverStatus {
-		allStatus[k] = v
-	}
-	return allStatus
-}
-
-// TCPServiceInfo holds information about a currently running TCP service
-type TCPServiceInfo struct {
-	*dynamic.TCPService          // dynamic configuration
-	Err                 []string `json:"error,omitempty"` // initialization error
-	// Status reports whether the service is disabled, in a warning state, or all good (enabled).
-	// If not in "enabled" state, the reason for it should be in the list of Err.
-	// It is the caller's responsibility to set the initial status.
-	Status string   `json:"status,omitempty"`
-	UsedBy []string `json:"usedBy,omitempty"` // list of routers using that service
-}
-
-// AddError adds err to s.Err, if it does not already exist.
-// If critical is set, s is marked as disabled.
-func (s *TCPServiceInfo) AddError(err error, critical bool) {
-	for _, value := range s.Err {
-		if value == err.Error() {
-			return
-		}
-	}
-
-	s.Err = append(s.Err, err.Error())
-	if critical {
-		s.Status = StatusDisabled
-		return
-	}
-
-	// only set it to "warning" if not already in a worse state
-	if s.Status != StatusDisabled {
-		s.Status = StatusWarning
-	}
-}
-
 func getProviderName(elementName string) string {
 	parts := strings.Split(elementName, "@")
 	if len(parts) > 1 {
diff --git a/pkg/config/runtime/runtime_http.go b/pkg/config/runtime/runtime_http.go
new file mode 100644
index 0000000000000000000000000000000000000000..1c6b9e4aa61a1b46ed24ff749f83dd76e6904f40
--- /dev/null
+++ b/pkg/config/runtime/runtime_http.go
@@ -0,0 +1,167 @@
+package runtime
+
+import (
+	"context"
+	"sync"
+
+	"github.com/containous/traefik/v2/pkg/config/dynamic"
+	"github.com/containous/traefik/v2/pkg/log"
+)
+
+// GetRoutersByEntryPoints returns all the http routers by entry points name and routers name
+func (c *Configuration) GetRoutersByEntryPoints(ctx context.Context, entryPoints []string, tls bool) map[string]map[string]*RouterInfo {
+	entryPointsRouters := make(map[string]map[string]*RouterInfo)
+
+	for rtName, rt := range c.Routers {
+		if (tls && rt.TLS == nil) || (!tls && rt.TLS != nil) {
+			continue
+		}
+
+		eps := rt.EntryPoints
+		if len(eps) == 0 {
+			eps = entryPoints
+		}
+		for _, entryPointName := range eps {
+			if !contains(entryPoints, entryPointName) {
+				log.FromContext(log.With(ctx, log.Str(log.EntryPointName, entryPointName))).
+					Errorf("entryPoint %q doesn't exist", entryPointName)
+				continue
+			}
+
+			if _, ok := entryPointsRouters[entryPointName]; !ok {
+				entryPointsRouters[entryPointName] = make(map[string]*RouterInfo)
+			}
+
+			entryPointsRouters[entryPointName][rtName] = rt
+		}
+	}
+
+	return entryPointsRouters
+}
+
+// RouterInfo holds information about a currently running HTTP router
+type RouterInfo struct {
+	*dynamic.Router // dynamic configuration
+	// Err contains all the errors that occurred during router's creation.
+	Err []string `json:"error,omitempty"`
+	// Status reports whether the router is disabled, in a warning state, or all good (enabled).
+	// If not in "enabled" state, the reason for it should be in the list of Err.
+	// It is the caller's responsibility to set the initial status.
+	Status string `json:"status,omitempty"`
+}
+
+// AddError adds err to r.Err, if it does not already exist.
+// If critical is set, r is marked as disabled.
+func (r *RouterInfo) AddError(err error, critical bool) {
+	for _, value := range r.Err {
+		if value == err.Error() {
+			return
+		}
+	}
+
+	r.Err = append(r.Err, err.Error())
+	if critical {
+		r.Status = StatusDisabled
+		return
+	}
+
+	// only set it to "warning" if not already in a worse state
+	if r.Status != StatusDisabled {
+		r.Status = StatusWarning
+	}
+}
+
+// MiddlewareInfo holds information about a currently running middleware
+type MiddlewareInfo struct {
+	*dynamic.Middleware // dynamic configuration
+	// Err contains all the errors that occurred during service creation.
+	Err    []string `json:"error,omitempty"`
+	Status string   `json:"status,omitempty"`
+	UsedBy []string `json:"usedBy,omitempty"` // list of routers and services using that middleware
+}
+
+// AddError adds err to s.Err, if it does not already exist.
+// If critical is set, m is marked as disabled.
+func (m *MiddlewareInfo) AddError(err error, critical bool) {
+	for _, value := range m.Err {
+		if value == err.Error() {
+			return
+		}
+	}
+
+	m.Err = append(m.Err, err.Error())
+	if critical {
+		m.Status = StatusDisabled
+		return
+	}
+
+	// only set it to "warning" if not already in a worse state
+	if m.Status != StatusDisabled {
+		m.Status = StatusWarning
+	}
+}
+
+// ServiceInfo holds information about a currently running service
+type ServiceInfo struct {
+	*dynamic.Service // dynamic configuration
+	// Err contains all the errors that occurred during service creation.
+	Err []string `json:"error,omitempty"`
+	// Status reports whether the service is disabled, in a warning state, or all good (enabled).
+	// If not in "enabled" state, the reason for it should be in the list of Err.
+	// It is the caller's responsibility to set the initial status.
+	Status string   `json:"status,omitempty"`
+	UsedBy []string `json:"usedBy,omitempty"` // list of routers using that service
+
+	serverStatusMu sync.RWMutex
+	serverStatus   map[string]string // keyed by server URL
+}
+
+// AddError adds err to s.Err, if it does not already exist.
+// If critical is set, s is marked as disabled.
+func (s *ServiceInfo) AddError(err error, critical bool) {
+	for _, value := range s.Err {
+		if value == err.Error() {
+			return
+		}
+	}
+
+	s.Err = append(s.Err, err.Error())
+	if critical {
+		s.Status = StatusDisabled
+		return
+	}
+
+	// only set it to "warning" if not already in a worse state
+	if s.Status != StatusDisabled {
+		s.Status = StatusWarning
+	}
+}
+
+// UpdateServerStatus sets the status of the server in the ServiceInfo.
+// It is the responsibility of the caller to check that s is not nil.
+func (s *ServiceInfo) UpdateServerStatus(server string, status string) {
+	s.serverStatusMu.Lock()
+	defer s.serverStatusMu.Unlock()
+
+	if s.serverStatus == nil {
+		s.serverStatus = make(map[string]string)
+	}
+	s.serverStatus[server] = status
+}
+
+// GetAllStatus returns all the statuses of all the servers in ServiceInfo.
+// It is the responsibility of the caller to check that s is not nil
+func (s *ServiceInfo) GetAllStatus() map[string]string {
+	s.serverStatusMu.RLock()
+	defer s.serverStatusMu.RUnlock()
+
+	if len(s.serverStatus) == 0 {
+		return nil
+	}
+
+	allStatus := make(map[string]string, len(s.serverStatus))
+	for k, v := range s.serverStatus {
+		allStatus[k] = v
+	}
+	return allStatus
+}
diff --git a/pkg/config/runtime/runtime_http_test.go b/pkg/config/runtime/runtime_http_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..38102c61d6f3b4c172a26ab9ec4657b9f74f4b2e
--- /dev/null
+++ b/pkg/config/runtime/runtime_http_test.go
@@ -0,0 +1,213 @@
+package runtime
+
+import (
+	"context"
+	"testing"
+
+	"github.com/containous/traefik/v2/pkg/config/dynamic"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestGetRoutersByEntryPoints(t *testing.T) {
+	testCases := []struct {
+		desc        string
+		conf        dynamic.Configuration
+		entryPoints []string
+		expected    map[string]map[string]*RouterInfo
+	}{
+		{
+			desc:        "Empty Configuration without entrypoint",
+			conf:        dynamic.Configuration{},
+			entryPoints: []string{""},
+			expected:    map[string]map[string]*RouterInfo{},
+		},
+		{
+			desc:        "Empty Configuration with unknown entrypoints",
+			conf:        dynamic.Configuration{},
+			entryPoints: []string{"foo"},
+			expected:    map[string]map[string]*RouterInfo{},
+		},
+		{
+			desc: "Valid configuration with an unknown entrypoint",
+			conf: dynamic.Configuration{
+				HTTP: &dynamic.HTTPConfiguration{
+					Routers: map[string]*dynamic.Router{
+						"foo": {
+							EntryPoints: []string{"web"},
+							Service:     "foo-service@myprovider",
+							Rule:        "Host(`bar.foo`)",
+						},
+					},
+				},
+				TCP: &dynamic.TCPConfiguration{
+					Routers: map[string]*dynamic.TCPRouter{
+						"foo": {
+							EntryPoints: []string{"web"},
+							Service:     "foo-service@myprovider",
+							Rule:        "HostSNI(`bar.foo`)",
+						},
+					},
+				},
+			},
+			entryPoints: []string{"foo"},
+			expected:    map[string]map[string]*RouterInfo{},
+		},
+		{
+			desc: "Valid configuration with a known entrypoint",
+			conf: dynamic.Configuration{
+				HTTP: &dynamic.HTTPConfiguration{
+					Routers: map[string]*dynamic.Router{
+						"foo": {
+							EntryPoints: []string{"web"},
+							Service:     "foo-service@myprovider",
+							Rule:        "Host(`bar.foo`)",
+						},
+						"bar": {
+							EntryPoints: []string{"webs"},
+							Service:     "bar-service@myprovider",
+							Rule:        "Host(`foo.bar`)",
+						},
+						"foobar": {
+							EntryPoints: []string{"web", "webs"},
+							Service:     "foobar-service@myprovider",
+							Rule:        "Host(`bar.foobar`)",
+						},
+					},
+				},
+				TCP: &dynamic.TCPConfiguration{
+					Routers: map[string]*dynamic.TCPRouter{
+						"foo": {
+							EntryPoints: []string{"web"},
+							Service:     "foo-service@myprovider",
+							Rule:        "HostSNI(`bar.foo`)",
+						},
+						"bar": {
+							EntryPoints: []string{"webs"},
+							Service:     "bar-service@myprovider",
+							Rule:        "HostSNI(`foo.bar`)",
+						},
+						"foobar": {
+							EntryPoints: []string{"web", "webs"},
+							Service:     "foobar-service@myprovider",
+							Rule:        "HostSNI(`bar.foobar`)",
+						},
+					},
+				},
+			},
+			entryPoints: []string{"web"},
+			expected: map[string]map[string]*RouterInfo{
+				"web": {
+					"foo": {
+						Router: &dynamic.Router{
+							EntryPoints: []string{"web"},
+							Service:     "foo-service@myprovider",
+							Rule:        "Host(`bar.foo`)",
+						},
+						Status: "enabled",
+					},
+					"foobar": {
+						Router: &dynamic.Router{
+							EntryPoints: []string{"web", "webs"},
+							Service:     "foobar-service@myprovider",
+							Rule:        "Host(`bar.foobar`)",
+						},
+						Status: "enabled",
+					},
+				},
+			},
+		},
+		{
+			desc: "Valid configuration with multiple known entrypoints",
+			conf: dynamic.Configuration{
+				HTTP: &dynamic.HTTPConfiguration{
+					Routers: map[string]*dynamic.Router{
+						"foo": {
+							EntryPoints: []string{"web"},
+							Service:     "foo-service@myprovider",
+							Rule:        "Host(`bar.foo`)",
+						},
+						"bar": {
+							EntryPoints: []string{"webs"},
+							Service:     "bar-service@myprovider",
+							Rule:        "Host(`foo.bar`)",
+						},
+						"foobar": {
+							EntryPoints: []string{"web", "webs"},
+							Service:     "foobar-service@myprovider",
+							Rule:        "Host(`bar.foobar`)",
+						},
+					},
+				},
+				TCP: &dynamic.TCPConfiguration{
+					Routers: map[string]*dynamic.TCPRouter{
+						"foo": {
+							EntryPoints: []string{"web"},
+							Service:     "foo-service@myprovider",
+							Rule:        "HostSNI(`bar.foo`)",
+						},
+						"bar": {
+							EntryPoints: []string{"webs"},
+							Service:     "bar-service@myprovider",
+							Rule:        "HostSNI(`foo.bar`)",
+						},
+						"foobar": {
+							EntryPoints: []string{"web", "webs"},
+							Service:     "foobar-service@myprovider",
+							Rule:        "HostSNI(`bar.foobar`)",
+						},
+					},
+				},
+			},
+			entryPoints: []string{"web", "webs"},
+			expected: map[string]map[string]*RouterInfo{
+				"web": {
+					"foo": {
+						Router: &dynamic.Router{
+							EntryPoints: []string{"web"},
+							Service:     "foo-service@myprovider",
+							Rule:        "Host(`bar.foo`)",
+						},
+						Status: "enabled",
+					},
+					"foobar": {
+						Router: &dynamic.Router{
+							EntryPoints: []string{"web", "webs"},
+							Service:     "foobar-service@myprovider",
+							Rule:        "Host(`bar.foobar`)",
+						},
+						Status: "enabled",
+					},
+				},
+				"webs": {
+					"bar": {
+						Router: &dynamic.Router{
+
+							EntryPoints: []string{"webs"},
+							Service:     "bar-service@myprovider",
+							Rule:        "Host(`foo.bar`)",
+						},
+						Status: "enabled",
+					},
+					"foobar": {
+						Router: &dynamic.Router{
+							EntryPoints: []string{"web", "webs"},
+							Service:     "foobar-service@myprovider",
+							Rule:        "Host(`bar.foobar`)",
+						},
+						Status: "enabled",
+					},
+				},
+			},
+		},
+	}
+
+	for _, test := range testCases {
+		test := test
+		t.Run(test.desc, func(t *testing.T) {
+			t.Parallel()
+			runtimeConfig := NewConfig(test.conf)
+			actual := runtimeConfig.GetRoutersByEntryPoints(context.Background(), test.entryPoints, false)
+			assert.Equal(t, test.expected, actual)
+		})
+	}
+}
diff --git a/pkg/config/runtime/runtime_tcp.go b/pkg/config/runtime/runtime_tcp.go
new file mode 100644
index 0000000000000000000000000000000000000000..a99d519f9cfac8845925c4a9fa82bcd89c2c4c9e
--- /dev/null
+++ b/pkg/config/runtime/runtime_tcp.go
@@ -0,0 +1,99 @@
+package runtime
+
+import (
+	"context"
+
+	"github.com/containous/traefik/v2/pkg/config/dynamic"
+	"github.com/containous/traefik/v2/pkg/log"
+)
+
+// GetTCPRoutersByEntryPoints returns all the tcp routers by entry points name and routers name
+func (c *Configuration) GetTCPRoutersByEntryPoints(ctx context.Context, entryPoints []string) map[string]map[string]*TCPRouterInfo {
+	entryPointsRouters := make(map[string]map[string]*TCPRouterInfo)
+
+	for rtName, rt := range c.TCPRouters {
+		eps := rt.EntryPoints
+		if len(eps) == 0 {
+			eps = entryPoints
+		}
+
+		for _, entryPointName := range eps {
+			if !contains(entryPoints, entryPointName) {
+				log.FromContext(log.With(ctx, log.Str(log.EntryPointName, entryPointName))).
+					Errorf("entryPoint %q doesn't exist", entryPointName)
+				continue
+			}
+
+			if _, ok := entryPointsRouters[entryPointName]; !ok {
+				entryPointsRouters[entryPointName] = make(map[string]*TCPRouterInfo)
+			}
+
+			entryPointsRouters[entryPointName][rtName] = rt
+		}
+	}
+
+	return entryPointsRouters
+}
+
+// TCPRouterInfo holds information about a currently running TCP router
+type TCPRouterInfo struct {
+	*dynamic.TCPRouter          // dynamic configuration
+	Err                []string `json:"error,omitempty"` // initialization error
+	// Status reports whether the router is disabled, in a warning state, or all good (enabled).
+	// If not in "enabled" state, the reason for it should be in the list of Err.
+	// It is the caller's responsibility to set the initial status.
+	Status string `json:"status,omitempty"`
+}
+
+// AddError adds err to r.Err, if it does not already exist.
+// If critical is set, r is marked as disabled.
+func (r *TCPRouterInfo) AddError(err error, critical bool) {
+	for _, value := range r.Err {
+		if value == err.Error() {
+			return
+		}
+	}
+
+	r.Err = append(r.Err, err.Error())
+	if critical {
+		r.Status = StatusDisabled
+		return
+	}
+
+	// only set it to "warning" if not already in a worse state
+	if r.Status != StatusDisabled {
+		r.Status = StatusWarning
+	}
+}
+
+// TCPServiceInfo holds information about a currently running TCP service
+type TCPServiceInfo struct {
+	*dynamic.TCPService          // dynamic configuration
+	Err                 []string `json:"error,omitempty"` // initialization error
+	// Status reports whether the service is disabled, in a warning state, or all good (enabled).
+	// If not in "enabled" state, the reason for it should be in the list of Err.
+	// It is the caller's responsibility to set the initial status.
+	Status string   `json:"status,omitempty"`
+	UsedBy []string `json:"usedBy,omitempty"` // list of routers using that service
+}
+
+// AddError adds err to s.Err, if it does not already exist.
+// If critical is set, s is marked as disabled.
+func (s *TCPServiceInfo) AddError(err error, critical bool) {
+	for _, value := range s.Err {
+		if value == err.Error() {
+			return
+		}
+	}
+
+	s.Err = append(s.Err, err.Error())
+	if critical {
+		s.Status = StatusDisabled
+		return
+	}
+
+	// only set it to "warning" if not already in a worse state
+	if s.Status != StatusDisabled {
+		s.Status = StatusWarning
+	}
+}
diff --git a/pkg/config/runtime/runtime_tcp_test.go b/pkg/config/runtime/runtime_tcp_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..9fb3f137e020460032129dbbd50f7a358efea87a
--- /dev/null
+++ b/pkg/config/runtime/runtime_tcp_test.go
@@ -0,0 +1,213 @@
+package runtime
+
+import (
+	"context"
+	"testing"
+
+	"github.com/containous/traefik/v2/pkg/config/dynamic"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestGetTCPRoutersByEntryPoints(t *testing.T) {
+	testCases := []struct {
+		desc        string
+		conf        dynamic.Configuration
+		entryPoints []string
+		expected    map[string]map[string]*TCPRouterInfo
+	}{
+		{
+			desc:        "Empty Configuration without entrypoint",
+			conf:        dynamic.Configuration{},
+			entryPoints: []string{""},
+			expected:    map[string]map[string]*TCPRouterInfo{},
+		},
+		{
+			desc:        "Empty Configuration with unknown entrypoints",
+			conf:        dynamic.Configuration{},
+			entryPoints: []string{"foo"},
+			expected:    map[string]map[string]*TCPRouterInfo{},
+		},
+		{
+			desc: "Valid configuration with an unknown entrypoint",
+			conf: dynamic.Configuration{
+				HTTP: &dynamic.HTTPConfiguration{
+					Routers: map[string]*dynamic.Router{
+						"foo": {
+							EntryPoints: []string{"web"},
+							Service:     "foo-service@myprovider",
+							Rule:        "Host(`bar.foo`)",
+						},
+					},
+				},
+				TCP: &dynamic.TCPConfiguration{
+					Routers: map[string]*dynamic.TCPRouter{
+						"foo": {
+							EntryPoints: []string{"web"},
+							Service:     "foo-service@myprovider",
+							Rule:        "HostSNI(`bar.foo`)",
+						},
+					},
+				},
+			},
+			entryPoints: []string{"foo"},
+			expected:    map[string]map[string]*TCPRouterInfo{},
+		},
+		{
+			desc: "Valid configuration with a known entrypoint",
+			conf: dynamic.Configuration{
+				HTTP: &dynamic.HTTPConfiguration{
+					Routers: map[string]*dynamic.Router{
+						"foo": {
+							EntryPoints: []string{"web"},
+							Service:     "foo-service@myprovider",
+							Rule:        "Host(`bar.foo`)",
+						},
+						"bar": {
+							EntryPoints: []string{"webs"},
+							Service:     "bar-service@myprovider",
+							Rule:        "Host(`foo.bar`)",
+						},
+						"foobar": {
+							EntryPoints: []string{"web", "webs"},
+							Service:     "foobar-service@myprovider",
+							Rule:        "Host(`bar.foobar`)",
+						},
+					},
+				},
+				TCP: &dynamic.TCPConfiguration{
+					Routers: map[string]*dynamic.TCPRouter{
+						"foo": {
+							EntryPoints: []string{"web"},
+							Service:     "foo-service@myprovider",
+							Rule:        "HostSNI(`bar.foo`)",
+						},
+						"bar": {
+							EntryPoints: []string{"webs"},
+							Service:     "bar-service@myprovider",
+							Rule:        "HostSNI(`foo.bar`)",
+						},
+						"foobar": {
+							EntryPoints: []string{"web", "webs"},
+							Service:     "foobar-service@myprovider",
+							Rule:        "HostSNI(`bar.foobar`)",
+						},
+					},
+				},
+			},
+			entryPoints: []string{"web"},
+			expected: map[string]map[string]*TCPRouterInfo{
+				"web": {
+					"foo": {
+						TCPRouter: &dynamic.TCPRouter{
+							EntryPoints: []string{"web"},
+							Service:     "foo-service@myprovider",
+							Rule:        "HostSNI(`bar.foo`)",
+						},
+						Status: "enabled",
+					},
+					"foobar": {
+						TCPRouter: &dynamic.TCPRouter{
+							EntryPoints: []string{"web", "webs"},
+							Service:     "foobar-service@myprovider",
+							Rule:        "HostSNI(`bar.foobar`)",
+						},
+						Status: "enabled",
+					},
+				},
+			},
+		},
+		{
+			desc: "Valid configuration with multiple known entrypoints",
+			conf: dynamic.Configuration{
+				HTTP: &dynamic.HTTPConfiguration{
+					Routers: map[string]*dynamic.Router{
+						"foo": {
+							EntryPoints: []string{"web"},
+							Service:     "foo-service@myprovider",
+							Rule:        "Host(`bar.foo`)",
+						},
+						"bar": {
+							EntryPoints: []string{"webs"},
+							Service:     "bar-service@myprovider",
+							Rule:        "Host(`foo.bar`)",
+						},
+						"foobar": {
+							EntryPoints: []string{"web", "webs"},
+							Service:     "foobar-service@myprovider",
+							Rule:        "Host(`bar.foobar`)",
+						},
+					},
+				},
+				TCP: &dynamic.TCPConfiguration{
+					Routers: map[string]*dynamic.TCPRouter{
+						"foo": {
+							EntryPoints: []string{"web"},
+							Service:     "foo-service@myprovider",
+							Rule:        "HostSNI(`bar.foo`)",
+						},
+						"bar": {
+							EntryPoints: []string{"webs"},
+							Service:     "bar-service@myprovider",
+							Rule:        "HostSNI(`foo.bar`)",
+						},
+						"foobar": {
+							EntryPoints: []string{"web", "webs"},
+							Service:     "foobar-service@myprovider",
+							Rule:        "HostSNI(`bar.foobar`)",
+						},
+					},
+				},
+			},
+			entryPoints: []string{"web", "webs"},
+			expected: map[string]map[string]*TCPRouterInfo{
+				"web": {
+					"foo": {
+						TCPRouter: &dynamic.TCPRouter{
+							EntryPoints: []string{"web"},
+							Service:     "foo-service@myprovider",
+							Rule:        "HostSNI(`bar.foo`)",
+						},
+						Status: "enabled",
+					},
+					"foobar": {
+						TCPRouter: &dynamic.TCPRouter{
+							EntryPoints: []string{"web", "webs"},
+							Service:     "foobar-service@myprovider",
+							Rule:        "HostSNI(`bar.foobar`)",
+						},
+						Status: "enabled",
+					},
+				},
+				"webs": {
+					"bar": {
+						TCPRouter: &dynamic.TCPRouter{
+
+							EntryPoints: []string{"webs"},
+							Service:     "bar-service@myprovider",
+							Rule:        "HostSNI(`foo.bar`)",
+						},
+						Status: "enabled",
+					},
+					"foobar": {
+						TCPRouter: &dynamic.TCPRouter{
+							EntryPoints: []string{"web", "webs"},
+							Service:     "foobar-service@myprovider",
+							Rule:        "HostSNI(`bar.foobar`)",
+						},
+						Status: "enabled",
+					},
+				},
+			},
+		},
+	}
+
+	for _, test := range testCases {
+		test := test
+		t.Run(test.desc, func(t *testing.T) {
+			t.Parallel()
+			runtimeConfig := NewConfig(test.conf)
+			actual := runtimeConfig.GetTCPRoutersByEntryPoints(context.Background(), test.entryPoints)
+			assert.Equal(t, test.expected, actual)
+		})
+	}
+}
diff --git a/pkg/config/runtime/runtime_test.go b/pkg/config/runtime/runtime_test.go
index 5a20af3d93466a2de009d70c3ae9097332120e3c..d220abe84d66fc50a9d478b673c1960112bcbae4 100644
--- a/pkg/config/runtime/runtime_test.go
+++ b/pkg/config/runtime/runtime_test.go
@@ -1,7 +1,6 @@
 package runtime_test
 
 import (
-	"context"
 	"testing"
 
 	"github.com/containous/traefik/v2/pkg/config/dynamic"
@@ -690,411 +689,3 @@ func TestPopulateUsedBy(t *testing.T) {
 	}
 
 }
-
-func TestGetTCPRoutersByEntryPoints(t *testing.T) {
-	testCases := []struct {
-		desc        string
-		conf        dynamic.Configuration
-		entryPoints []string
-		expected    map[string]map[string]*runtime.TCPRouterInfo
-	}{
-		{
-			desc:        "Empty Configuration without entrypoint",
-			conf:        dynamic.Configuration{},
-			entryPoints: []string{""},
-			expected:    map[string]map[string]*runtime.TCPRouterInfo{},
-		},
-		{
-			desc:        "Empty Configuration with unknown entrypoints",
-			conf:        dynamic.Configuration{},
-			entryPoints: []string{"foo"},
-			expected:    map[string]map[string]*runtime.TCPRouterInfo{},
-		},
-		{
-			desc: "Valid configuration with an unknown entrypoint",
-			conf: dynamic.Configuration{
-				HTTP: &dynamic.HTTPConfiguration{
-					Routers: map[string]*dynamic.Router{
-						"foo": {
-							EntryPoints: []string{"web"},
-							Service:     "foo-service@myprovider",
-							Rule:        "Host(`bar.foo`)",
-						},
-					},
-				},
-				TCP: &dynamic.TCPConfiguration{
-					Routers: map[string]*dynamic.TCPRouter{
-						"foo": {
-							EntryPoints: []string{"web"},
-							Service:     "foo-service@myprovider",
-							Rule:        "HostSNI(`bar.foo`)",
-						},
-					},
-				},
-			},
-			entryPoints: []string{"foo"},
-			expected:    map[string]map[string]*runtime.TCPRouterInfo{},
-		},
-		{
-			desc: "Valid configuration with a known entrypoint",
-			conf: dynamic.Configuration{
-				HTTP: &dynamic.HTTPConfiguration{
-					Routers: map[string]*dynamic.Router{
-						"foo": {
-							EntryPoints: []string{"web"},
-							Service:     "foo-service@myprovider",
-							Rule:        "Host(`bar.foo`)",
-						},
-						"bar": {
-							EntryPoints: []string{"webs"},
-							Service:     "bar-service@myprovider",
-							Rule:        "Host(`foo.bar`)",
-						},
-						"foobar": {
-							EntryPoints: []string{"web", "webs"},
-							Service:     "foobar-service@myprovider",
-							Rule:        "Host(`bar.foobar`)",
-						},
-					},
-				},
-				TCP: &dynamic.TCPConfiguration{
-					Routers: map[string]*dynamic.TCPRouter{
-						"foo": {
-							EntryPoints: []string{"web"},
-							Service:     "foo-service@myprovider",
-							Rule:        "HostSNI(`bar.foo`)",
-						},
-						"bar": {
-							EntryPoints: []string{"webs"},
-							Service:     "bar-service@myprovider",
-							Rule:        "HostSNI(`foo.bar`)",
-						},
-						"foobar": {
-							EntryPoints: []string{"web", "webs"},
-							Service:     "foobar-service@myprovider",
-							Rule:        "HostSNI(`bar.foobar`)",
-						},
-					},
-				},
-			},
-			entryPoints: []string{"web"},
-			expected: map[string]map[string]*runtime.TCPRouterInfo{
-				"web": {
-					"foo": {
-						TCPRouter: &dynamic.TCPRouter{
-							EntryPoints: []string{"web"},
-							Service:     "foo-service@myprovider",
-							Rule:        "HostSNI(`bar.foo`)",
-						},
-						Status: "enabled",
-					},
-					"foobar": {
-						TCPRouter: &dynamic.TCPRouter{
-							EntryPoints: []string{"web", "webs"},
-							Service:     "foobar-service@myprovider",
-							Rule:        "HostSNI(`bar.foobar`)",
-						},
-						Status: "enabled",
-					},
-				},
-			},
-		},
-		{
-			desc: "Valid configuration with multiple known entrypoints",
-			conf: dynamic.Configuration{
-				HTTP: &dynamic.HTTPConfiguration{
-					Routers: map[string]*dynamic.Router{
-						"foo": {
-							EntryPoints: []string{"web"},
-							Service:     "foo-service@myprovider",
-							Rule:        "Host(`bar.foo`)",
-						},
-						"bar": {
-							EntryPoints: []string{"webs"},
-							Service:     "bar-service@myprovider",
-							Rule:        "Host(`foo.bar`)",
-						},
-						"foobar": {
-							EntryPoints: []string{"web", "webs"},
-							Service:     "foobar-service@myprovider",
-							Rule:        "Host(`bar.foobar`)",
-						},
-					},
-				},
-				TCP: &dynamic.TCPConfiguration{
-					Routers: map[string]*dynamic.TCPRouter{
-						"foo": {
-							EntryPoints: []string{"web"},
-							Service:     "foo-service@myprovider",
-							Rule:        "HostSNI(`bar.foo`)",
-						},
-						"bar": {
-							EntryPoints: []string{"webs"},
-							Service:     "bar-service@myprovider",
-							Rule:        "HostSNI(`foo.bar`)",
-						},
-						"foobar": {
-							EntryPoints: []string{"web", "webs"},
-							Service:     "foobar-service@myprovider",
-							Rule:        "HostSNI(`bar.foobar`)",
-						},
-					},
-				},
-			},
-			entryPoints: []string{"web", "webs"},
-			expected: map[string]map[string]*runtime.TCPRouterInfo{
-				"web": {
-					"foo": {
-						TCPRouter: &dynamic.TCPRouter{
-							EntryPoints: []string{"web"},
-							Service:     "foo-service@myprovider",
-							Rule:        "HostSNI(`bar.foo`)",
-						},
-						Status: "enabled",
-					},
-					"foobar": {
-						TCPRouter: &dynamic.TCPRouter{
-							EntryPoints: []string{"web", "webs"},
-							Service:     "foobar-service@myprovider",
-							Rule:        "HostSNI(`bar.foobar`)",
-						},
-						Status: "enabled",
-					},
-				},
-				"webs": {
-					"bar": {
-						TCPRouter: &dynamic.TCPRouter{
-
-							EntryPoints: []string{"webs"},
-							Service:     "bar-service@myprovider",
-							Rule:        "HostSNI(`foo.bar`)",
-						},
-						Status: "enabled",
-					},
-					"foobar": {
-						TCPRouter: &dynamic.TCPRouter{
-							EntryPoints: []string{"web", "webs"},
-							Service:     "foobar-service@myprovider",
-							Rule:        "HostSNI(`bar.foobar`)",
-						},
-						Status: "enabled",
-					},
-				},
-			},
-		},
-	}
-
-	for _, test := range testCases {
-		test := test
-		t.Run(test.desc, func(t *testing.T) {
-			t.Parallel()
-			runtimeConfig := runtime.NewConfig(test.conf)
-			actual := runtimeConfig.GetTCPRoutersByEntryPoints(context.Background(), test.entryPoints)
-			assert.Equal(t, test.expected, actual)
-		})
-	}
-}
-
-func TestGetRoutersByEntryPoints(t *testing.T) {
-	testCases := []struct {
-		desc        string
-		conf        dynamic.Configuration
-		entryPoints []string
-		expected    map[string]map[string]*runtime.RouterInfo
-	}{
-		{
-			desc:        "Empty Configuration without entrypoint",
-			conf:        dynamic.Configuration{},
-			entryPoints: []string{""},
-			expected:    map[string]map[string]*runtime.RouterInfo{},
-		},
-		{
-			desc:        "Empty Configuration with unknown entrypoints",
-			conf:        dynamic.Configuration{},
-			entryPoints: []string{"foo"},
-			expected:    map[string]map[string]*runtime.RouterInfo{},
-		},
-		{
-			desc: "Valid configuration with an unknown entrypoint",
-			conf: dynamic.Configuration{
-				HTTP: &dynamic.HTTPConfiguration{
-					Routers: map[string]*dynamic.Router{
-						"foo": {
-							EntryPoints: []string{"web"},
-							Service:     "foo-service@myprovider",
-							Rule:        "Host(`bar.foo`)",
-						},
-					},
-				},
-				TCP: &dynamic.TCPConfiguration{
-					Routers: map[string]*dynamic.TCPRouter{
-						"foo": {
-							EntryPoints: []string{"web"},
-							Service:     "foo-service@myprovider",
-							Rule:        "HostSNI(`bar.foo`)",
-						},
-					},
-				},
-			},
-			entryPoints: []string{"foo"},
-			expected:    map[string]map[string]*runtime.RouterInfo{},
-		},
-		{
-			desc: "Valid configuration with a known entrypoint",
-			conf: dynamic.Configuration{
-				HTTP: &dynamic.HTTPConfiguration{
-					Routers: map[string]*dynamic.Router{
-						"foo": {
-							EntryPoints: []string{"web"},
-							Service:     "foo-service@myprovider",
-							Rule:        "Host(`bar.foo`)",
-						},
-						"bar": {
-							EntryPoints: []string{"webs"},
-							Service:     "bar-service@myprovider",
-							Rule:        "Host(`foo.bar`)",
-						},
-						"foobar": {
-							EntryPoints: []string{"web", "webs"},
-							Service:     "foobar-service@myprovider",
-							Rule:        "Host(`bar.foobar`)",
-						},
-					},
-				},
-				TCP: &dynamic.TCPConfiguration{
-					Routers: map[string]*dynamic.TCPRouter{
-						"foo": {
-							EntryPoints: []string{"web"},
-							Service:     "foo-service@myprovider",
-							Rule:        "HostSNI(`bar.foo`)",
-						},
-						"bar": {
-							EntryPoints: []string{"webs"},
-							Service:     "bar-service@myprovider",
-							Rule:        "HostSNI(`foo.bar`)",
-						},
-						"foobar": {
-							EntryPoints: []string{"web", "webs"},
-							Service:     "foobar-service@myprovider",
-							Rule:        "HostSNI(`bar.foobar`)",
-						},
-					},
-				},
-			},
-			entryPoints: []string{"web"},
-			expected: map[string]map[string]*runtime.RouterInfo{
-				"web": {
-					"foo": {
-						Router: &dynamic.Router{
-							EntryPoints: []string{"web"},
-							Service:     "foo-service@myprovider",
-							Rule:        "Host(`bar.foo`)",
-						},
-						Status: "enabled",
-					},
-					"foobar": {
-						Router: &dynamic.Router{
-							EntryPoints: []string{"web", "webs"},
-							Service:     "foobar-service@myprovider",
-							Rule:        "Host(`bar.foobar`)",
-						},
-						Status: "enabled",
-					},
-				},
-			},
-		},
-		{
-			desc: "Valid configuration with multiple known entrypoints",
-			conf: dynamic.Configuration{
-				HTTP: &dynamic.HTTPConfiguration{
-					Routers: map[string]*dynamic.Router{
-						"foo": {
-							EntryPoints: []string{"web"},
-							Service:     "foo-service@myprovider",
-							Rule:        "Host(`bar.foo`)",
-						},
-						"bar": {
-							EntryPoints: []string{"webs"},
-							Service:     "bar-service@myprovider",
-							Rule:        "Host(`foo.bar`)",
-						},
-						"foobar": {
-							EntryPoints: []string{"web", "webs"},
-							Service:     "foobar-service@myprovider",
-							Rule:        "Host(`bar.foobar`)",
-						},
-					},
-				},
-				TCP: &dynamic.TCPConfiguration{
-					Routers: map[string]*dynamic.TCPRouter{
-						"foo": {
-							EntryPoints: []string{"web"},
-							Service:     "foo-service@myprovider",
-							Rule:        "HostSNI(`bar.foo`)",
-						},
-						"bar": {
-							EntryPoints: []string{"webs"},
-							Service:     "bar-service@myprovider",
-							Rule:        "HostSNI(`foo.bar`)",
-						},
-						"foobar": {
-							EntryPoints: []string{"web", "webs"},
-							Service:     "foobar-service@myprovider",
-							Rule:        "HostSNI(`bar.foobar`)",
-						},
-					},
-				},
-			},
-			entryPoints: []string{"web", "webs"},
-			expected: map[string]map[string]*runtime.RouterInfo{
-				"web": {
-					"foo": {
-						Router: &dynamic.Router{
-							EntryPoints: []string{"web"},
-							Service:     "foo-service@myprovider",
-							Rule:        "Host(`bar.foo`)",
-						},
-						Status: "enabled",
-					},
-					"foobar": {
-						Router: &dynamic.Router{
-							EntryPoints: []string{"web", "webs"},
-							Service:     "foobar-service@myprovider",
-							Rule:        "Host(`bar.foobar`)",
-						},
-						Status: "enabled",
-					},
-				},
-				"webs": {
-					"bar": {
-						Router: &dynamic.Router{
-
-							EntryPoints: []string{"webs"},
-							Service:     "bar-service@myprovider",
-							Rule:        "Host(`foo.bar`)",
-						},
-						Status: "enabled",
-					},
-					"foobar": {
-						Router: &dynamic.Router{
-							EntryPoints: []string{"web", "webs"},
-							Service:     "foobar-service@myprovider",
-							Rule:        "Host(`bar.foobar`)",
-						},
-						Status: "enabled",
-					},
-				},
-			},
-		},
-	}
-
-	for _, test := range testCases {
-		test := test
-		t.Run(test.desc, func(t *testing.T) {
-			t.Parallel()
-			runtimeConfig := runtime.NewConfig(test.conf)
-			actual := runtimeConfig.GetRoutersByEntryPoints(context.Background(), test.entryPoints, false)
-			assert.Equal(t, test.expected, actual)
-		})
-	}
-}