Repository: kubernetes-sigs/gateway-api-inference-extension

Issue:
Deprecate inferencepool-resources.yaml manifest
We should only be using the helm charts to manage the inferencepool resources. A quick search pointed to a few places where inferencepool-resources.yaml is still referenced:

1) https://github.com/kubernetes-sigs/gateway-api-inference-extension/blob/main/site-src/api-types/inferencepool.md
2) https://github.com/kubernetes-sigs/gateway-api-inference-extension/blob/main/site-src/implementations/model-servers.md?plain=1
3) https://github.com/kubernetes-sigs/gateway-api-inference-extension/blob/main/hack/release-quickstart.sh#L77
4) https://github.com/kubernetes-sigs/gateway-api-inference-extension/blob/main/site-src/guides/index.md?plain=1#L319
5) https://github.com/kubernetes-sigs/gateway-api-inference-extension/blob/main/site-src/guides/troubleshooting.md?plain=1#L71

Hints from issue comments:
@ahg-g: /assign @rahulgurnani @k8s-ci-robot: @ahg-g: GitHub didn't allow me to assign the following users: rahulgurnani.

Note that only [kubernetes-sigs members](https://github.com/orgs/kubernetes-sigs/people) with read permissions, repo collaborators and people who have commented on this issue/PR can be assigned. Additionally, issues/PRs can only have 10 assignees at the same time.
For more information please see [the contributor guide](https://git.k8s.io/community/contributors/guide/first-contribution.md#issue-assignment-in-github)

<details>

In response to [this](https://github.com/kubernetes-sigs/gateway-api-inference-extension/issues/1480#issuecomment-3276770421):

>/assign @rahulgurnani 


Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository.
</details>@rahulgurnani: /assign

Solution patch:
diff --git a/config/manifests/inferencepool-resources.yaml b/config/manifests/inferencepool-resources.yaml
deleted file mode 100644
index ffe1965..0000000
--- a/config/manifests/inferencepool-resources.yaml
+++ /dev/null
@@ -1,186 +0,0 @@
-# Note: If you change this file, please also change:
-#  - ./test/testdata/inferencepool-e2e.yaml
-#  - ./conformance/resources/manifests/manifests.yaml
-#  - ./site-src/guides/inferencepool-rollout.md
----
-apiVersion: inference.networking.k8s.io/v1
-kind: InferencePool
-metadata:
-  name: vllm-llama3-8b-instruct
-spec:
-  targetPorts:
-    - number: 8000
-  selector:
-    matchLabels:
-      app: vllm-llama3-8b-instruct
-  endpointPickerRef:
-    name: vllm-llama3-8b-instruct-epp
-    kind: Service
-    port:
-      number: 9002
----
-apiVersion: v1
-kind: Service
-metadata:
-  name: vllm-llama3-8b-instruct-epp
-  namespace: default
-spec:
-  selector:
-    app: vllm-llama3-8b-instruct-epp
-  ports:
-    - protocol: TCP
-      port: 9002
-      targetPort: 9002
-      appProtocol: http2
-  type: ClusterIP
----
-apiVersion: v1
-kind: ServiceAccount
-metadata:
-  name: vllm-llama3-8b-instruct-epp
-  namespace: default
----
-apiVersion: apps/v1
-kind: Deployment
-metadata:
-  name: vllm-llama3-8b-instruct-epp
-  namespace: default
-  labels:
-    app: vllm-llama3-8b-instruct-epp
-spec:
-  replicas: 1
-  selector:
-    matchLabels:
-      app: vllm-llama3-8b-instruct-epp
-  template:
-    metadata:
-      labels:
-        app: vllm-llama3-8b-instruct-epp
-    spec:
-      serviceAccountName: vllm-llama3-8b-instruct-epp
-      # Conservatively, this timeout should mirror the longest grace period of the pods within the pool
-      terminationGracePeriodSeconds: 130
-      containers:
-      - name: epp
-        image: us-central1-docker.pkg.dev/k8s-staging-images/gateway-api-inference-extension/epp:main
-        imagePullPolicy: Always
-        args:
-        - --pool-name
-        - "vllm-llama3-8b-instruct"
-        - "--pool-namespace"
-        - "default"
-        - --v
-        - "4"
-        - --zap-encoder
-        - "json"
-        - --grpc-port
-        - "9002"
-        - --grpc-health-port
-        - "9003"
-        - "--config-file"
-        - "/config/default-plugins.yaml"
-        ports:
-        - containerPort: 9002
-        - containerPort: 9003
-        - name: metrics
-          containerPort: 9090
-        livenessProbe:
-          grpc:
-            port: 9003
-            service: inference-extension
-          initialDelaySeconds: 5
-          periodSeconds: 10
-        readinessProbe:
-          grpc:
-            port: 9003
-            service: inference-extension
-          initialDelaySeconds: 5
-          periodSeconds: 10
-        volumeMounts:
-        - name: plugins-config-volume
-          mountPath: "/config"
-      volumes:
-      - name: plugins-config-volume
-        configMap:
-          name: plugins-config
----
-apiVersion: v1
-kind: ConfigMap
-metadata:
-  name: plugins-config
-  namespace: default
-data:
-  default-plugins.yaml: |
-    apiVersion: inference.networking.x-k8s.io/v1alpha1
-    kind: EndpointPickerConfig
-    plugins:
-    - type: queue-scorer
-    - type: kv-cache-utilization-scorer
-    - type: prefix-cache-scorer
-    schedulingProfiles:
-    - name: default
-      plugins:
-      - pluginRef: queue-scorer
-      - pluginRef: kv-cache-utilization-scorer
-      - pluginRef: prefix-cache-scorer
----
-kind: Role
-apiVersion: rbac.authorization.k8s.io/v1
-metadata:
-  name: pod-read
-  namespace: default
-rules:
-- apiGroups: [ "inference.networking.x-k8s.io" ]
-  resources: [ "inferenceobjectives", "inferencepools" ]
-  verbs: [ "get", "watch", "list" ]
-- apiGroups: [ "inference.networking.k8s.io" ]
-  resources: [ "inferencepools" ]
-  verbs: [ "get", "watch", "list" ]
-- apiGroups: [ "" ]
-  resources: [ "pods" ]
-  verbs: [ "get", "watch", "list" ]
----
-kind: RoleBinding
-apiVersion: rbac.authorization.k8s.io/v1
-metadata:
-  name: pod-read-binding
-  namespace: default
-subjects:
-- kind: ServiceAccount
-  name: vllm-llama3-8b-instruct-epp
-  namespace: default
-roleRef:
-  apiGroup: rbac.authorization.k8s.io
-  kind: Role
-  name: pod-read
----
-kind: ClusterRole
-apiVersion: rbac.authorization.k8s.io/v1
-metadata:
-  name: auth-reviewer
-rules:
-- apiGroups:
-  - authentication.k8s.io
-  resources:
-  - tokenreviews
-  verbs:
-  - create
-- apiGroups:
-  - authorization.k8s.io
-  resources:
-  - subjectaccessreviews
-  verbs:
-  - create
----
-kind: ClusterRoleBinding
-apiVersion: rbac.authorization.k8s.io/v1
-metadata:
-  name: auth-reviewer-binding
-subjects:
-- kind: ServiceAccount
-  name: vllm-llama3-8b-instruct-epp
-  namespace: default
-roleRef:
-  apiGroup: rbac.authorization.k8s.io
-  kind: ClusterRole
-  name: auth-reviewer
diff --git a/go.mod b/go.mod
index 9fcbf1b..3b2fe1e 100644
--- a/go.mod
+++ b/go.mod
@@ -10,7 +10,7 @@ require (
 	github.com/google/go-cmp v0.7.0
 	github.com/google/uuid v1.6.0
 	github.com/hashicorp/golang-lru/v2 v2.0.7
-	github.com/onsi/ginkgo/v2 v2.25.1
+	github.com/onsi/ginkgo/v2 v2.25.3
 	github.com/onsi/gomega v1.38.2
 	github.com/prometheus/client_golang v1.23.2
 	github.com/prometheus/client_model v0.6.2
@@ -20,7 +20,7 @@ require (
 	go.uber.org/multierr v1.11.0
 	go.uber.org/zap v1.27.0
 	golang.org/x/sync v0.17.0
-	google.golang.org/grpc v1.75.0
+	google.golang.org/grpc v1.75.1
 	google.golang.org/protobuf v1.36.8
 	k8s.io/api v0.34.0
 	k8s.io/apiextensions-apiserver v0.34.0
diff --git a/go.sum b/go.sum
index 1f9a3bb..4a45989 100644
--- a/go.sum
+++ b/go.sum
@@ -196,8 +196,8 @@ github.com/oklog/ulid/v2 v2.1.1 h1:suPZ4ARWLOJLegGFiZZ1dFAkqzhMjL3J1TzI+5wHz8s=
 github.com/oklog/ulid/v2 v2.1.1/go.mod h1:rcEKHmBBKfef9DhnvX7y1HZBYxjXb0cP5ExxNsTT1QQ=
 github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
 github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
-github.com/onsi/ginkgo/v2 v2.25.1 h1:Fwp6crTREKM+oA6Cz4MsO8RhKQzs2/gOIVOUscMAfZY=
-github.com/onsi/ginkgo/v2 v2.25.1/go.mod h1:ppTWQ1dh9KM/F1XgpeRqelR+zHVwV81DGRSDnFxK7Sk=
+github.com/onsi/ginkgo/v2 v2.25.3 h1:Ty8+Yi/ayDAGtk4XxmmfUy4GabvM+MegeB4cDLRi6nw=
+github.com/onsi/ginkgo/v2 v2.25.3/go.mod h1:43uiyQC4Ed2tkOzLsEYm7hnrb7UJTWHYNsuy3bG/snE=
 github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A=
 github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k=
 github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
@@ -343,8 +343,8 @@ google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7 h1:
 google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7/go.mod h1:kXqgZtrWaf6qS3jZOCnCH7WYfrvFjkC51bM8fz3RsCA=
 google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7 h1:pFyd6EwwL2TqFf8emdthzeX+gZE1ElRq3iM8pui4KBY=
 google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
-google.golang.org/grpc v1.75.0 h1:+TW+dqTd2Biwe6KKfhE5JpiYIBWq865PhKGSXiivqt4=
-google.golang.org/grpc v1.75.0/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ=
+google.golang.org/grpc v1.75.1 h1:/ODCNEuf9VghjgO3rqLcfg8fiOP0nSluljWFlDxELLI=
+google.golang.org/grpc v1.75.1/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ=
 google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc=
 google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
diff --git a/hack/release-quickstart.sh b/hack/release-quickstart.sh
index 22c7051..04b79a3 100755
--- a/hack/release-quickstart.sh
+++ b/hack/release-quickstart.sh
@@ -74,25 +74,21 @@ sed -i.bak "s|kubectl apply -k https://github.com/kubernetes-sigs/gateway-api-in
 # -----------------------------------------------------------------------------
 # Update image references
 # -----------------------------------------------------------------------------
-EPP="config/manifests/inferencepool-resources.yaml"
 #TODO: Put all helm values files into an array to loop over
 EPP_HELM="config/charts/inferencepool/values.yaml"
 BBR_HELM="config/charts/body-based-routing/values.yaml"
 CONFORMANCE_MANIFESTS="conformance/resources/base.yaml"
-echo "Updating ${EPP}, ${EPP_HELM}, ${BBR_HELM}, and ${CONFORMANCE_MANIFESTS} ..."
+echo "Updating ${EPP_HELM}, ${BBR_HELM}, and ${CONFORMANCE_MANIFESTS} ..."
 
 # Update the container tag.
-sed -i.bak -E "s|(us-central1-docker\.pkg\.dev/k8s-staging-images/gateway-api-inference-extension/epp:)[^\"[:space:]]+|\1${RELEASE_TAG}|g" "$EPP"
 sed -i.bak -E "s|(tag: )[^\"[:space:]]+|\1${RELEASE_TAG}|g" "$EPP_HELM"
 sed -i.bak -E "s|(tag: )[^\"[:space:]]+|\1${RELEASE_TAG}|g" "$BBR_HELM"
 sed -i.bak -E "s|(us-central1-docker\.pkg\.dev/k8s-staging-images/gateway-api-inference-extension/epp:)[^\"[:space:]]+|\1${RELEASE_TAG}|g" "$CONFORMANCE_MANIFESTS"
 
 # Update the container image pull policy.
-sed -i.bak '/us-central1-docker.pkg.dev\/k8s-staging-images\/gateway-api-inference-extension\/epp/{n;s/Always/IfNotPresent/;}' "$EPP"
 sed -i.bak '/us-central1-docker.pkg.dev\/k8s-staging-images\/gateway-api-inference-extension\/epp/{n;s/Always/IfNotPresent/;}' "$CONFORMANCE_MANIFESTS"
 
 # Update the container registry.
-sed -i.bak -E "s|us-central1-docker\.pkg\.dev/k8s-staging-images|registry.k8s.io|g" "$EPP"
 sed -i.bak -E "s|us-central1-docker\.pkg\.dev/k8s-staging-images|registry.k8s.io|g" "$EPP_HELM"
 sed -i.bak -E "s|us-central1-docker\.pkg\.dev/k8s-staging-images|registry.k8s.io|g" "$BBR_HELM"
 sed -i.bak -E "s|us-central1-docker\.pkg\.dev/k8s-staging-images|registry.k8s.io|g" "$CONFORMANCE_MANIFESTS"
@@ -139,8 +135,8 @@ sed -i.bak -E "s|us-central1-docker\.pkg\.dev/k8s-staging-images|registry.k8s.io
 # -----------------------------------------------------------------------------
 # Stage the changes
 # -----------------------------------------------------------------------------
-echo "Staging $VERSION_FILE $UPDATED_CRD $README $EPP $EPP_HELM $BBR_HELM $CONFORMANCE_MANIFESTS $VLLM_GPU_DEPLOY $VLLM_CPU_DEPLOY $VLLM_SIM_DEPLOY files..."
-git add $VERSION_FILE $UPDATED_CRD $README $EPP $EPP_HELM $BBR_HELM $CONFORMANCE_MANIFESTS $VLLM_GPU_DEPLOY $VLLM_CPU_DEPLOY $VLLM_SIM_DEPLOY
+echo "Staging $VERSION_FILE $UPDATED_CRD $README $EPP_HELM $BBR_HELM $CONFORMANCE_MANIFESTS $VLLM_GPU_DEPLOY $VLLM_CPU_DEPLOY $VLLM_SIM_DEPLOY files..."
+git add $VERSION_FILE $UPDATED_CRD $README $EPP_HELM $BBR_HELM $CONFORMANCE_MANIFESTS $VLLM_GPU_DEPLOY $VLLM_CPU_DEPLOY $VLLM_SIM_DEPLOY
 
 # -----------------------------------------------------------------------------
 # Cleanup backup files and finish
diff --git a/pkg/epp/flowcontrol/config.go b/pkg/epp/flowcontrol/config.go
new file mode 100644
index 0000000..edc23ab
--- /dev/null
+++ b/pkg/epp/flowcontrol/config.go
@@ -0,0 +1,50 @@
+/*
+Copyright 2025 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package flowcontrol
+
+import (
+	"fmt"
+
+	"sigs.k8s.io/gateway-api-inference-extension/pkg/epp/flowcontrol/controller"
+	"sigs.k8s.io/gateway-api-inference-extension/pkg/epp/flowcontrol/registry"
+)
+
+// Config is the top-level configuration for the entire flow control module.
+// It embeds the configurations for the controller and the registry, providing a single point of entry for validation
+// and initialization.
+type Config struct {
+	Controller controller.Config
+	Registry   registry.Config
+}
+
+// ValidateAndApplyDefaults checks the configuration for validity and populates any empty fields with system defaults.
+// It delegates validation to the underlying controller and registry configurations.
+// It returns a new, validated `Config` object and does not mutate the receiver.
+func (c *Config) ValidateAndApplyDefaults() (*Config, error) {
+	validatedControllerCfg, err := c.Controller.ValidateAndApplyDefaults()
+	if err != nil {
+		return nil, fmt.Errorf("controller config validation failed: %w", err)
+	}
+	validatedRegistryCfg, err := c.Registry.ValidateAndApplyDefaults()
+	if err != nil {
+		return nil, fmt.Errorf("registry config validation failed: %w", err)
+	}
+	return &Config{
+		Controller: *validatedControllerCfg,
+		Registry:   *validatedRegistryCfg,
+	}, nil
+}
diff --git a/pkg/epp/flowcontrol/controller/config.go b/pkg/epp/flowcontrol/controller/config.go
index 17c1429..e542c4d 100644
--- a/pkg/epp/flowcontrol/controller/config.go
+++ b/pkg/epp/flowcontrol/controller/config.go
@@ -53,46 +53,38 @@ type Config struct {
 	EnqueueChannelBufferSize int
 }
 
-// newConfig performs validation and initialization, returning a guaranteed-valid `Config` object.
-// This is the required constructor for creating a new configuration.
-// It does not mutate the input `cfg`.
-func newConfig(cfg Config) (*Config, error) {
-	newCfg := cfg.deepCopy()
-	if err := newCfg.validateAndApplyDefaults(); err != nil {
-		return nil, err
-	}
-	return newCfg, nil
-}
+// ValidateAndApplyDefaults checks the global configuration for validity and then creates a new `Config` object,
+// populating any empty fields with system defaults.
+// It does not mutate the receiver.
+func (c *Config) ValidateAndApplyDefaults() (*Config, error) {
+	cfg := c.deepCopy()
 
-// validateAndApplyDefaults checks the global configuration for validity and then mutates the receiver to populate any
-// empty fields with system defaults.
-func (c *Config) validateAndApplyDefaults() error {
 	// --- Validation ---
-	if c.DefaultRequestTTL < 0 {
-		return fmt.Errorf("DefaultRequestTTL cannot be negative, but got %v", c.DefaultRequestTTL)
+	if cfg.DefaultRequestTTL < 0 {
+		return nil, fmt.Errorf("DefaultRequestTTL cannot be negative, but got %v", cfg.DefaultRequestTTL)
 	}
-	if c.ExpiryCleanupInterval < 0 {
-		return fmt.Errorf("ExpiryCleanupInterval cannot be negative, but got %v", c.ExpiryCleanupInterval)
+	if cfg.ExpiryCleanupInterval < 0 {
+		return nil, fmt.Errorf("ExpiryCleanupInterval cannot be negative, but got %v", cfg.ExpiryCleanupInterval)
 	}
-	if c.ProcessorReconciliationInterval < 0 {
-		return fmt.Errorf("ProcessorReconciliationInterval cannot be negative, but got %v",
-			c.ProcessorReconciliationInterval)
+	if cfg.ProcessorReconciliationInterval < 0 {
+		return nil, fmt.Errorf("ProcessorReconciliationInterval cannot be negative, but got %v",
+			cfg.ProcessorReconciliationInterval)
 	}
-	if c.EnqueueChannelBufferSize < 0 {
-		return fmt.Errorf("EnqueueChannelBufferSize cannot be negative, but got %d", c.EnqueueChannelBufferSize)
+	if cfg.EnqueueChannelBufferSize < 0 {
+		return nil, fmt.Errorf("EnqueueChannelBufferSize cannot be negative, but got %d", cfg.EnqueueChannelBufferSize)
 	}
 
 	// --- Defaulting ---
-	if c.ExpiryCleanupInterval == 0 {
-		c.ExpiryCleanupInterval = defaultExpiryCleanupInterval
+	if cfg.ExpiryCleanupInterval == 0 {
+		cfg.ExpiryCleanupInterval = defaultExpiryCleanupInterval
 	}
-	if c.ProcessorReconciliationInterval == 0 {
-		c.ProcessorReconciliationInterval = defaultProcessorReconciliationInterval
+	if cfg.ProcessorReconciliationInterval == 0 {
+		cfg.ProcessorReconciliationInterval = defaultProcessorReconciliationInterval
 	}
-	if c.EnqueueChannelBufferSize == 0 {
-		c.EnqueueChannelBufferSize = defaultEnqueueChannelBufferSize
+	if cfg.EnqueueChannelBufferSize == 0 {
+		cfg.EnqueueChannelBufferSize = defaultEnqueueChannelBufferSize
 	}
-	return nil
+	return cfg, nil
 }
 
 // deepCopy creates a deep copy of the `Config` object.
diff --git a/pkg/epp/flowcontrol/controller/controller.go b/pkg/epp/flowcontrol/controller/controller.go
index e25eda9..8449159 100644
--- a/pkg/epp/flowcontrol/controller/controller.go
+++ b/pkg/epp/flowcontrol/controller/controller.go
@@ -118,13 +118,8 @@ func NewFlowController(
 	logger logr.Logger,
 	opts ...flowControllerOption,
 ) (*FlowController, error) {
-	validatedConfig, err := newConfig(config)
-	if err != nil {
-		return nil, fmt.Errorf("invalid flow controller configuration: %w", err)
-	}
-
 	fc := &FlowController{
-		config:             *validatedConfig,
+		config:             *config.deepCopy(),
 		registry:           registry,
 		saturationDetector: sd,
 		clock:              clock.RealClock{},
diff --git a/pkg/epp/flowcontrol/registry/config.go b/pkg/epp/flowcontrol/registry/config.go
index f9ad8e6..af345a6 100644
--- a/pkg/epp/flowcontrol/registry/config.go
+++ b/pkg/epp/flowcontrol/registry/config.go
@@ -112,7 +112,7 @@ type Config struct {
 // that operate at this priority level.
 type PriorityBandConfig struct {
 	// Priority is the unique numerical priority level for this band.
-	// Convention: Lower numerical values indicate lower priority.
+	// Convention: Highest numeric value corresponds to highest priority (centered on 0).
 	// Required.
 	Priority int
 
@@ -140,20 +140,6 @@ type PriorityBandConfig struct {
 	MaxBytes uint64
 }
 
-// NewConfig performs validation and initialization, returning a guaranteed-valid `Config` object.
-// This is the required constructor for creating a new configuration. It applies provided functional options (primarily
-// for testing) and does not mutate the input `cfg`.
-func NewConfig(cfg Config, opts ...configOption) (*Config, error) {
-	newCfg := cfg.deepCopy()
-	for _, opt := range opts {
-		opt(newCfg)
-	}
-	if err := newCfg.validateAndApplyDefaults(); err != nil {
-		return nil, err
-	}
-	return newCfg, nil
-}
-
 // =============================================================================
 // Shard-Level Configuration
 // =============================================================================
@@ -205,52 +191,55 @@ func (sc *ShardConfig) getBandConfig(priority int) (*ShardPriorityBandConfig, er
 
 // --- Validation and Defaulting ---
 
-// validateAndApplyDefaults checks the global configuration for validity (including plugin compatibility) and mutates
-// the receiver to populate any empty fields with system defaults. It also initializes internal lookup maps.
-func (c *Config) validateAndApplyDefaults() error {
+// ValidateAndApplyDefaults checks the global configuration for validity and then creates a new `Config` object,
+// populating any empty fields with system defaults.
+// It does not mutate the receiver.
+func (c *Config) ValidateAndApplyDefaults() (*Config, error) {
+	cfg := c.deepCopy()
+
 	// Apply defaults to top-level fields.
-	if c.InitialShardCount <= 0 {
-		c.InitialShardCount = defaultInitialShardCount
+	if cfg.InitialShardCount <= 0 {
+		cfg.InitialShardCount = defaultInitialShardCount
 	}
-	if c.FlowGCTimeout <= 0 {
-		c.FlowGCTimeout = defaultFlowGCTimeout
+	if cfg.FlowGCTimeout <= 0 {
+		cfg.FlowGCTimeout = defaultFlowGCTimeout
 	}
-	if c.EventChannelBufferSize <= 0 {
-		c.EventChannelBufferSize = defaultEventChannelBufferSize
+	if cfg.EventChannelBufferSize <= 0 {
+		cfg.EventChannelBufferSize = defaultEventChannelBufferSize
 	}
 
 	// Ensure the DI factories are initialized for production use if `NewConfig` was called without options.
-	if c.interFlowDispatchPolicyFactory == nil {
-		c.interFlowDispatchPolicyFactory = inter.NewPolicyFromName
+	if cfg.interFlowDispatchPolicyFactory == nil {
+		cfg.interFlowDispatchPolicyFactory = inter.NewPolicyFromName
 	}
-	if c.intraFlowDispatchPolicyFactory == nil {
-		c.intraFlowDispatchPolicyFactory = intra.NewPolicyFromName
+	if cfg.intraFlowDispatchPolicyFactory == nil {
+		cfg.intraFlowDispatchPolicyFactory = intra.NewPolicyFromName
 	}
-	if c.queueFactory == nil {
-		c.queueFactory = queue.NewQueueFromName
+	if cfg.queueFactory == nil {
+		cfg.queueFactory = queue.NewQueueFromName
 	}
 
-	if len(c.PriorityBands) == 0 {
-		return errors.New("config validation failed: at least one priority band must be defined")
+	if len(cfg.PriorityBands) == 0 {
+		return nil, errors.New("config validation failed: at least one priority band must be defined")
 	}
 
 	// Validate and default each priority band.
 	priorities := make(map[int]struct{})
 	priorityNames := make(map[string]struct{})
-	c.priorityBandMap = make(map[int]*PriorityBandConfig, len(c.PriorityBands))
+	cfg.priorityBandMap = make(map[int]*PriorityBandConfig, len(cfg.PriorityBands))
 
-	for i := range c.PriorityBands {
-		band := &c.PriorityBands[i]
+	for i := range cfg.PriorityBands {
+		band := &cfg.PriorityBands[i]
 		if _, exists := priorities[band.Priority]; exists {
-			return fmt.Errorf("config validation failed: duplicate priority level %d found", band.Priority)
+			return nil, fmt.Errorf("config validation failed: duplicate priority level %d found", band.Priority)
 		}
 		priorities[band.Priority] = struct{}{}
 
 		if band.PriorityName == "" {
-			return fmt.Errorf("config validation failed: PriorityName is required for priority band %d", band.Priority)
+			return nil, fmt.Errorf("config validation failed: PriorityName is required for priority band %d", band.Priority)
 		}
 		if _, exists := priorityNames[band.PriorityName]; exists {
-			return fmt.Errorf("config validation failed: duplicate priority name %q found", band.PriorityName)
+			return nil, fmt.Errorf("config validation failed: duplicate priority name %q found", band.PriorityName)
 		}
 		priorityNames[band.PriorityName] = struct{}{}
 
@@ -267,12 +256,12 @@ func (c *Config) validateAndApplyDefaults() error {
 			band.MaxBytes = defaultPriorityBandMaxBytes
 		}
 
-		if err := c.validateBandCompatibility(*band); err != nil {
-			return err
+		if err := cfg.validateBandCompatibility(*band); err != nil {
+			return nil, err
 		}
-		c.priorityBandMap[band.Priority] = band
+		cfg.priorityBandMap[band.Priority] = band
 	}
-	return nil
+	return cfg, nil
 }
 
 // validateBandCompatibility verifies that a band's configured queue type has the necessary capabilities.
@@ -423,6 +412,18 @@ func withQueueFactory(factory queueFactory) configOption {
 	}
 }
 
+// newConfig creates a new validated and defaulted `Config` object.
+// It applies provided test-only functional options before validation and defaulting.
+// It does not mutate the input `cfg`.
+// test-only
+func newConfig(cfg Config, opts ...configOption) (*Config, error) {
+	newCfg := cfg.deepCopy()
+	for _, opt := range opts {
+		opt(newCfg)
+	}
+	return newCfg.ValidateAndApplyDefaults()
+}
+
 // --- Internal Utilities ---
 
 // deepCopy creates a deep copy of the `Config` object.
@@ -436,7 +437,6 @@ func (c *Config) deepCopy() *Config {
 		FlowGCTimeout:                  c.FlowGCTimeout,
 		EventChannelBufferSize:         c.EventChannelBufferSize,
 		PriorityBands:                  make([]PriorityBandConfig, len(c.PriorityBands)),
-		priorityBandMap:                make(map[int]*PriorityBandConfig, len(c.PriorityBands)),
 		interFlowDispatchPolicyFactory: c.interFlowDispatchPolicyFactory,
 		intraFlowDispatchPolicyFactory: c.intraFlowDispatchPolicyFactory,
 		queueFactory:                   c.queueFactory,
@@ -445,11 +445,14 @@ func (c *Config) deepCopy() *Config {
 	// PriorityBandConfig contains only value types, so a slice copy is sufficient for a deep copy.
 	copy(newCfg.PriorityBands, c.PriorityBands)
 
-	// Crucial: We must rebuild the map and take the address of the elements within the new slice (`newCfg.PriorityBands`)
-	// to ensure the map pointers are correct for the newly created `Config` instance.
-	for i := range newCfg.PriorityBands {
-		band := &newCfg.PriorityBands[i]
-		newCfg.priorityBandMap[band.Priority] = band
+	if c.priorityBandMap != nil {
+		newCfg.priorityBandMap = make(map[int]*PriorityBandConfig, len(c.PriorityBands))
+		// Crucial: We must rebuild the map and take the address of the elements within the new slice (`newCfg.PriorityBands`)
+		// to ensure the map pointers are correct for the newly created `Config` instance.
+		for i := range newCfg.PriorityBands {
+			band := &newCfg.PriorityBands[i]
+			newCfg.priorityBandMap[band.Priority] = band
+		}
 	}
 	return newCfg
 }
diff --git a/pkg/epp/flowcontrol/registry/registry.go b/pkg/epp/flowcontrol/registry/registry.go
index 5f29554..3a73ef7 100644
--- a/pkg/epp/flowcontrol/registry/registry.go
+++ b/pkg/epp/flowcontrol/registry/registry.go
@@ -148,17 +148,13 @@ func withClock(clk clock.WithTickerAndDelayedExecution) RegistryOption {
 
 // NewFlowRegistry creates and initializes a new `FlowRegistry` instance.
 func NewFlowRegistry(config Config, logger logr.Logger, opts ...RegistryOption) (*FlowRegistry, error) {
-	validatedConfig, err := NewConfig(config)
-	if err != nil {
-		return nil, fmt.Errorf("master configuration is invalid: %w", err)
-	}
-
+	cfg := config.deepCopy()
 	fr := &FlowRegistry{
-		config:               validatedConfig,
+		config:               cfg,
 		logger:               logger.WithName("flow-registry"),
 		activeShards:         []*registryShard{},
 		drainingShards:       make(map[string]*registryShard),
-		perPriorityBandStats: make(map[int]*bandStats, len(validatedConfig.PriorityBands)),
+		perPriorityBandStats: make(map[int]*bandStats, len(cfg.PriorityBands)),
 	}
 
 	for _, opt := range opts {
@@ -173,7 +169,7 @@ func NewFlowRegistry(config Config, logger logr.Logger, opts ...RegistryOption)
 		fr.perPriorityBandStats[band.Priority] = &bandStats{}
 	}
 
-	if err := fr.updateShardCount(validatedConfig.InitialShardCount); err != nil {
+	if err := fr.updateShardCount(cfg.InitialShardCount); err != nil {
 		return nil, fmt.Errorf("failed to initialize shards: %w", err)
 	}
 	fr.logger.V(logging.DEFAULT).Info("FlowRegistry initialized successfully")
diff --git a/site-src/implementations/model-servers.md b/site-src/implementations/model-servers.md
index da9968f..ed57e12 100644
--- a/site-src/implementations/model-servers.md
+++ b/site-src/implementations/model-servers.md
@@ -19,34 +19,29 @@ vLLM is configured as the default in the [endpoint picker extension](https://git
 
 Triton specific metric names need to be specified when starting the EPP.
 
-### Option 1: Use Helm
+Use `--set inferencePool.modelServerType=triton-tensorrt-llm` to install the `inferencepool` via helm. See the [`inferencepool` helm guide](https://github.com/kubernetes-sigs/gateway-api-inference-extension/blob/main/config/charts/inferencepool/README.md) for more details.
 
-Use `--set inferencePool.modelServerType=triton-tensorrt-llm` to install the [`inferencepool` via helm](https://github.com/kubernetes-sigs/gateway-api-inference-extension/blob/42eb5ff1c5af1275df43ac384df0ddf20da95134/config/charts/inferencepool). See the [`inferencepool` helm guide](https://github.com/kubernetes-sigs/gateway-api-inference-extension/blob/42eb5ff1c5af1275df43ac384df0ddf20da95134/config/charts/inferencepool/README.md) for more details.
+ Add the following to the `flags` in the helm chart as [flags to EPP](https://github.com/kubernetes-sigs/gateway-api-inference-extension/blob/29ea29028496a638b162ff287c62c0087211bbe5/config/charts/inferencepool/values.yaml#L36)
 
-### Option 2: Edit EPP deployment yaml
-
- Add the following to the `args` of the [EPP deployment](https://github.com/kubernetes-sigs/gateway-api-inference-extension/blob/42eb5ff1c5af1275df43ac384df0ddf20da95134/config/manifests/inferencepool-resources.yaml#L32)
-
- ```
-- --total-queued-requests-metric
-- "nv_trt_llm_request_metrics{request_type=waiting}"
-- --kv-cache-usage-percentage-metric
-- "nv_trt_llm_kv_cache_block_metrics{kv_cache_block_type=fraction}"
-- --lora-info-metric
-- "" # Set an empty metric to disable LoRA metric scraping as they are not supported by Triton yet.
+```
+- name=total-queued-requests-metric
+  value="nv_trt_llm_request_metrics{request_type=waiting}"
+- name=kv-cache-usage-percentage-metric
+  value="nv_trt_llm_kv_cache_block_metrics{kv_cache_block_type=fraction}"
+- name=lora-info-metric
+  value="" # Set an empty metric to disable LoRA metric scraping as they are not supported by Triton yet.
 ```
 
 ## SGLang
 
-### Edit EPP deployment yaml
+ Add the following `flags` while deploying using helm charts in the [EPP deployment](https://github.com/kubernetes-sigs/gateway-api-inference-extension/blob/29ea29028496a638b162ff287c62c0087211bbe5/config/charts/inferencepool/values.yaml#L36)
 
- Add the following to the `args` of the [EPP deployment](https://github.com/kubernetes-sigs/gateway-api-inference-extension/blob/42eb5ff1c5af1275df43ac384df0ddf20da95134/config/manifests/inferencepool-resources.yaml#L32)
 
 ```
-- --totalQueuedRequestsMetric
-- "sglang:num_queue_reqs"
-- --kvCacheUsagePercentageMetric
-- "sglang:token_usage"
-- --lora-info-metric
-- "" # Set an empty metric to disable LoRA metric scraping as they are not supported by SGLang yet.
-```
+- name=total-queued-requests-metric
+  value="sglang:num_queue_reqs"
+- name=kv-cache-usage-percentage-metric
+  value="sglang:token_usage"
+- name=lora-info-metric
+  value="" # Set an empty metric to disable LoRA metric scraping as they are not supported by SGLang yet.
+```
\ No newline at end of file


Test patch:
diff --git a/pkg/epp/flowcontrol/config_test.go b/pkg/epp/flowcontrol/config_test.go
new file mode 100644
index 0000000..713abee
--- /dev/null
+++ b/pkg/epp/flowcontrol/config_test.go
@@ -0,0 +1,91 @@
+/*
+Copyright 2025 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package flowcontrol
+
+import (
+	"testing"
+	"time"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+	"sigs.k8s.io/gateway-api-inference-extension/pkg/epp/flowcontrol/controller"
+	"sigs.k8s.io/gateway-api-inference-extension/pkg/epp/flowcontrol/registry"
+)
+
+func TestConfig_ValidateAndApplyDefaults(t *testing.T) {
+	t.Parallel()
+
+	// A minimal valid registry config, which is required for the success case.
+	validRegistryConfig := registry.Config{
+		PriorityBands: []registry.PriorityBandConfig{
+			{Priority: 1, PriorityName: "TestBand"},
+		},
+	}
+
+	testCases := []struct {
+		name          string
+		input         Config
+		expectErr     bool
+		expectedErrIs error
+	}{
+		{
+			name: "ShouldSucceed_WhenSubConfigsAreValid",
+			input: Config{
+				Controller: controller.Config{},
+				Registry:   validRegistryConfig,
+			},
+			expectErr: false,
+		},
+		{
+			name: "ShouldFail_WhenControllerConfigIsInvalid",
+			input: Config{
+				Controller: controller.Config{
+					DefaultRequestTTL: -1 * time.Second,
+				},
+				Registry: validRegistryConfig,
+			},
+			expectErr: true,
+		},
+		{
+			name: "ShouldFail_WhenRegistryConfigIsInvalid",
+			input: Config{
+				Controller: controller.Config{},
+				Registry: registry.Config{
+					PriorityBands: []registry.PriorityBandConfig{},
+				},
+			},
+			expectErr: true,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			t.Parallel()
+			originalInput := tc.input
+			validatedCfg, err := tc.input.ValidateAndApplyDefaults()
+
+			if tc.expectErr {
+				require.Error(t, err, "expected an error but got nil")
+			} else {
+				require.NoError(t, err, "expected no error but got: %v", err)
+				require.NotNil(t, validatedCfg, "validatedCfg should not be nil on success")
+			}
+
+			assert.Equal(t, originalInput, tc.input, "input config should not be mutated")
+		})
+	}
+}
diff --git a/pkg/epp/flowcontrol/controller/config_test.go b/pkg/epp/flowcontrol/controller/config_test.go
index a89db25..710df9f 100644
--- a/pkg/epp/flowcontrol/controller/config_test.go
+++ b/pkg/epp/flowcontrol/controller/config_test.go
@@ -24,7 +24,7 @@ import (
 	"github.com/stretchr/testify/require"
 )
 
-func TestNewConfig(t *testing.T) {
+func TestConfig_ValidateAndApplyDefaults(t *testing.T) {
 	t.Parallel()
 
 	testCases := []struct {
@@ -88,7 +88,7 @@ func TestNewConfig(t *testing.T) {
 		t.Run(tc.name, func(t *testing.T) {
 			t.Parallel()
 			originalInput := tc.input.deepCopy()
-			validatedCfg, err := newConfig(tc.input)
+			validatedCfg, err := tc.input.ValidateAndApplyDefaults()
 
 			if tc.expectErr {
 				require.Error(t, err, "expected an error but got nil")
diff --git a/pkg/epp/flowcontrol/controller/controller_test.go b/pkg/epp/flowcontrol/controller/controller_test.go
index 511bae8..435cdb5 100644
--- a/pkg/epp/flowcontrol/controller/controller_test.go
+++ b/pkg/epp/flowcontrol/controller/controller_test.go
@@ -273,23 +273,6 @@ func newTestRequest(ctx context.Context, key types.FlowKey) *typesmocks.MockFlow
 
 // --- Test Cases ---
 
-func TestNewFlowController(t *testing.T) {
-	t.Parallel()
-
-	t.Run("ErrorOnInvalidConfig", func(t *testing.T) {
-		t.Parallel()
-		invalidCfg := Config{ProcessorReconciliationInterval: -1 * time.Second}
-		_, err := NewFlowController(
-			context.Background(),
-			invalidCfg,
-			&mockRegistryClient{},
-			&mocks.MockSaturationDetector{},
-			logr.Discard(),
-		)
-		require.Error(t, err, "NewFlowController must return an error for invalid configuration")
-	})
-}
-
 func TestFlowController_EnqueueAndWait(t *testing.T) {
 	t.Parallel()
 
@@ -813,6 +796,7 @@ func TestFlowController_Concurrency(t *testing.T) {
 		// Use a generous buffer to prevent flakes in the test due to transient queuing delays.
 		EnqueueChannelBufferSize: numRequests,
 		DefaultRequestTTL:        1 * time.Second,
+		ExpiryCleanupInterval:    100 * time.Millisecond,
 	}, mockRegistry)
 
 	var wg sync.WaitGroup
diff --git a/pkg/epp/flowcontrol/registry/config_test.go b/pkg/epp/flowcontrol/registry/config_test.go
index ea9c19b..47814ae 100644
--- a/pkg/epp/flowcontrol/registry/config_test.go
+++ b/pkg/epp/flowcontrol/registry/config_test.go
@@ -35,7 +35,7 @@ import (
 	"sigs.k8s.io/gateway-api-inference-extension/pkg/epp/flowcontrol/framework/plugins/queue/listqueue"
 )
 
-func TestConfig_NewConfig(t *testing.T) {
+func TestConfig_ValidateAndApplyDefaults(t *testing.T) {
 	t.Parallel()
 
 	testCases := []struct {
@@ -286,20 +286,24 @@ func TestConfig_NewConfig(t *testing.T) {
 	for _, tc := range testCases {
 		t.Run(tc.name, func(t *testing.T) {
 			t.Parallel()
-			originalInputCopy := tc.input.deepCopy()
-			newCfg, err := NewConfig(tc.input, tc.opts...)
+			originalInput := tc.input.deepCopy()
+			validatedCfg, err := newConfig(tc.input, tc.opts...)
+
 			if tc.expectErr {
-				require.Error(t, err, "NewConfig should have returned an error")
+				require.Error(t, err, "expected an error but got nil")
 				if tc.expectedErrIs != nil {
-					assert.ErrorIs(t, err, tc.expectedErrIs, "Error should wrap the expected error type")
+					assert.ErrorIs(t, err, tc.expectedErrIs, "error should wrap the expected error type")
 				}
-				assert.Nil(t, newCfg, "On error, the returned config should be nil")
+				assert.Nil(t, validatedCfg, "validatedCfg should be nil on error")
 			} else {
-				require.NoError(t, err, "NewConfig should not have returned an error")
-				require.NotNil(t, newCfg, "On success, the returned config should not be nil")
+				require.NoError(t, err, "expected no error but got: %v", err)
+				require.NotNil(t, validatedCfg, "validatedCfg should not be nil on success")
 				if tc.assertion != nil {
-					tc.assertion(t, *originalInputCopy, newCfg)
+					tc.assertion(t, *originalInput, validatedCfg)
 				}
+
+				// Ensure the original config is not mutated.
+				assert.Equal(t, *originalInput, tc.input, "input config should not be mutated")
 			}
 		})
 	}
@@ -307,7 +311,7 @@ func TestConfig_NewConfig(t *testing.T) {
 
 func TestConfig_Partition(t *testing.T) {
 	t.Parallel()
-	baseCfg, err := NewConfig(Config{
+	baseCfg, err := newConfig(Config{
 		MaxBytes: 103, // Will not distribute evenly
 		PriorityBands: []PriorityBandConfig{
 			{Priority: 1, PriorityName: "High", MaxBytes: 55}, // Will not distribute evenly
@@ -391,7 +395,7 @@ func TestConfig_Partition(t *testing.T) {
 
 func TestConfig_GetBandConfig(t *testing.T) {
 	t.Parallel()
-	cfg, err := NewConfig(Config{
+	cfg, err := newConfig(Config{
 		PriorityBands: []PriorityBandConfig{
 			{Priority: 10, PriorityName: "High"},
 		},
@@ -427,7 +431,7 @@ func TestConfig_DeepCopy(t *testing.T) {
 		},
 	}
 	// Create a fully initialized "original" config to be the source of the copy.
-	original, err := NewConfig(baseCfg)
+	original, err := newConfig(baseCfg)
 	require.NoError(t, err, "Setup for deep copy should not fail")
 
 	t.Run("ShouldReturnNil_ForNilReceiver", func(t *testing.T) {
@@ -481,7 +485,7 @@ func TestConfig_DeepCopy(t *testing.T) {
 
 func TestShardConfig_GetBandConfig(t *testing.T) {
 	t.Parallel()
-	baseCfg, err := NewConfig(Config{
+	baseCfg, err := newConfig(Config{
 		PriorityBands: []PriorityBandConfig{
 			{Priority: 10, PriorityName: "High"},
 			{Priority: 20, PriorityName: "Low"},
diff --git a/pkg/epp/flowcontrol/registry/registry_test.go b/pkg/epp/flowcontrol/registry/registry_test.go
index b63b151..b5bc322 100644
--- a/pkg/epp/flowcontrol/registry/registry_test.go
+++ b/pkg/epp/flowcontrol/registry/registry_test.go
@@ -73,9 +73,12 @@ func newRegistryTestHarness(t *testing.T, opts harnessOptions) *registryTestHarn
 		config.InitialShardCount = opts.initialShardCount
 	}
 
+	validatedCfg, err := config.ValidateAndApplyDefaults()
+	require.NoError(t, err, "Test setup: validating config should not fail")
+
 	fakeClock := testclock.NewFakeClock(time.Now())
 	registryOpts := []RegistryOption{withClock(fakeClock)}
-	fr, err := NewFlowRegistry(config, logr.Discard(), registryOpts...)
+	fr, err := NewFlowRegistry(*validatedCfg, logr.Discard(), registryOpts...)
 	require.NoError(t, err, "Test setup: NewFlowRegistry should not fail")
 
 	// Start the GC loop in the background.
@@ -132,69 +135,9 @@ func (h *registryTestHarness) openConnectionOnFlow(key types.FlowKey) {
 func TestFlowRegistry_New(t *testing.T) {
 	t.Parallel()
 
-	t.Run("ShouldApplyDefaults_WhenInitialized", func(t *testing.T) {
-		t.Parallel()
-		config := Config{PriorityBands: []PriorityBandConfig{{Priority: highPriority, PriorityName: "DefaultedBand"}}}
-		fr, err := NewFlowRegistry(config, logr.Discard())
-		require.NoError(t, err, "Creating a valid registry with defaults should not fail")
-		assert.Equal(t, defaultInitialShardCount, fr.config.InitialShardCount, "InitialShardCount should be defaulted")
-		assert.Equal(t, defaultFlowGCTimeout, fr.config.FlowGCTimeout, "FlowGCTimeout should be defaulted")
-		assert.Equal(t, defaultEventChannelBufferSize, fr.config.EventChannelBufferSize,
-			"EventChannelBufferSize should be defaulted")
-		assert.Len(t, fr.allShards, defaultInitialShardCount,
-			"Registry should be initialized with the default number of shards")
-		bandConf, err := fr.config.getBandConfig(highPriority)
-		require.NoError(t, err, "Getting the defaulted band config should not fail")
-		assert.Equal(t, defaultPriorityBandMaxBytes, bandConf.MaxBytes, "Priority band MaxBytes should be defaulted")
-	})
-
-	t.Run("ShouldFail_OnInvalidConfiguration", func(t *testing.T) {
-		t.Parallel()
-		testCases := []struct {
-			name            string
-			config          Config
-			expectErrSubStr string
-		}{
-			{
-				name:            "WhenNoPriorityBandsAreDefined",
-				config:          Config{},
-				expectErrSubStr: "at least one priority band must be defined",
-			},
-			{
-				name: "WhenPriorityLevelsAreDuplicated",
-				config: Config{
-					PriorityBands: []PriorityBandConfig{
-						{Priority: highPriority, PriorityName: "A"},
-						{Priority: highPriority, PriorityName: "B"},
-					},
-				},
-				expectErrSubStr: fmt.Sprintf("duplicate priority level %d", highPriority),
-			},
-			{
-				name: "WhenPriorityNamesAreDuplicated",
-				config: Config{
-					PriorityBands: []PriorityBandConfig{
-						{Priority: highPriority, PriorityName: "A"},
-						{Priority: lowPriority, PriorityName: "A"},
-					},
-				},
-				expectErrSubStr: `duplicate priority name "A"`,
-			},
-		}
-
-		for _, tc := range testCases {
-			t.Run(tc.name, func(t *testing.T) {
-				t.Parallel()
-				_, err := NewFlowRegistry(tc.config, logr.Discard())
-				require.Error(t, err, "NewFlowRegistry should fail with an invalid config")
-				assert.Contains(t, err.Error(), tc.expectErrSubStr, "Error message should contain the expected reason")
-			})
-		}
-	})
-
 	t.Run("ShouldFail_WhenInitialShardCreationFails", func(t *testing.T) {
 		t.Parallel()
-		config, err := NewConfig(
+		config, err := newConfig(
 			Config{PriorityBands: []PriorityBandConfig{{Priority: highPriority, PriorityName: "A"}}},
 			withInterFlowDispatchPolicyFactory(func(inter.RegisteredPolicyName) (framework.InterFlowDispatchPolicy, error) {
 				return nil, errors.New("injected factory failure")
@@ -544,14 +487,6 @@ func TestFlowRegistry_UpdateShardCount(t *testing.T) {
 			expectedPartitionedGlobalCapacities: map[uint64]int{25: 4},
 			expectedPartitionedBandCapacities:   map[uint64]int{12: 2, 13: 2},
 		},
-		{
-			name:                                "Succeeds_ScaleUp_FromZero",
-			initialShardCount:                   0,
-			targetShardCount:                    4,
-			expectedActiveCount:                 4,
-			expectedPartitionedGlobalCapacities: map[uint64]int{25: 4},
-			expectedPartitionedBandCapacities:   map[uint64]int{12: 2, 13: 2},
-		},
 		{
 			name:                                "Succeeds_ScaleDown_ToOne",
 			initialShardCount:                   3,
diff --git a/pkg/epp/flowcontrol/registry/shard_test.go b/pkg/epp/flowcontrol/registry/shard_test.go
index afb2fac..23bf813 100644
--- a/pkg/epp/flowcontrol/registry/shard_test.go
+++ b/pkg/epp/flowcontrol/registry/shard_test.go
@@ -59,7 +59,7 @@ type shardTestHarness struct {
 // newShardTestHarness initializes a `shardTestHarness` with a default configuration.
 func newShardTestHarness(t *testing.T) *shardTestHarness {
 	t.Helper()
-	globalConfig, err := NewConfig(Config{
+	globalConfig, err := newConfig(Config{
 		PriorityBands: []PriorityBandConfig{
 			{Priority: highPriority, PriorityName: "High"},
 			{Priority: lowPriority, PriorityName: "Low"},
@@ -146,7 +146,7 @@ func TestShard_New(t *testing.T) {
 
 	t.Run("ShouldFail_WhenInterFlowPolicyFactoryFails", func(t *testing.T) {
 		t.Parallel()
-		shardConfig, _ := NewConfig(Config{PriorityBands: []PriorityBandConfig{
+		shardConfig, _ := newConfig(Config{PriorityBands: []PriorityBandConfig{
 			{Priority: highPriority, PriorityName: "High"},
 		}})
 		failingFactory := func(inter.RegisteredPolicyName) (framework.InterFlowDispatchPolicy, error) {