Repository: envoyproxy/gateway
Pull request #6100 (2025-05-16 19:12:53)

feat: add ownerreference to infra resources when gateway namespace mode
<!--
NOTE: If your PR contains any API changes (changes under `/api`), we recommend you to separate these API changes into
a new PR, and we will review the API part first. It will save you lots of implementation time if the API get accepted.
-->

**What this PR does / why we need it**:
Add ownerreference (gateway) to infra resources when enable GatewayNamespaceMode.
This PR resolves the following issues when enable GatewayNamespaceMode.

- https://github.com/envoyproxy/gateway/issues/3051
- https://github.com/envoyproxy/gateway/issues/4792

**[Not Scope in PR]**
Add ownerreference (gatewayclass) to infra resources when enable merged gateways.

**Which issue(s) this PR fixes**:
<!--
*Automatically closes linked issue when PR is merged.
Usage: `Fixes #<issue number>`, or `Fixes (paste link of issue)`.
-->
Since the issue has not been resolved in all cases, it remains open.

<!--
For any non-trivial changes, you need to provide a brief description of the changes in the release notes.
Please add the description to the release-notes/current.yaml file and include this file in the PR.
-->
Release Notes: Yes


codecov[bot]: ## [Codecov](https://app.codecov.io/gh/envoyproxy/gateway/pull/6100?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=envoyproxy) Report
Attention: Patch coverage is `90.67797%` with `11 lines` in your changes missing coverage. Please review.
> Project coverage is 70.50%. Comparing base [(`309750a`)](https://app.codecov.io/gh/envoyproxy/gateway/commit/309750a6c7157463a3dd86ff8500cb5c58e05bf0?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=envoyproxy) to head [(`895ba05`)](https://app.codecov.io/gh/envoyproxy/gateway/commit/895ba05411b22f70bcf986108504a64ca1008472?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=envoyproxy).

| [Files with missing lines](https://app.codecov.io/gh/envoyproxy/gateway/pull/6100?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=envoyproxy) | Patch % | Lines |
|---|---|---|
| [internal/infrastructure/kubernetes/proxy\_infra.go](https://app.codecov.io/gh/envoyproxy/gateway/pull/6100?src=pr&el=tree&filepath=internal%2Finfrastructure%2Fkubernetes%2Fproxy_infra.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=envoyproxy#diff-aW50ZXJuYWwvaW5mcmFzdHJ1Y3R1cmUva3ViZXJuZXRlcy9wcm94eV9pbmZyYS5nbw==) | 26.66% | [10 Missing and 1 partial :warning: ](https://app.codecov.io/gh/envoyproxy/gateway/pull/6100?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=envoyproxy) |

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##             main    #6100      +/-   ##
==========================================
+ Coverage   70.39%   70.50%   +0.10%     
==========================================
  Files         217      217              
  Lines       36128    36238     +110     
==========================================
+ Hits        25431    25548     +117     
+ Misses       9177     9174       -3     
+ Partials     1520     1516       -4     
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/envoyproxy/gateway/pull/6100?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=envoyproxy).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=envoyproxy).

<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
</details>
kkk777-7: @zirain @rudrakhp 
thank for comment.
I've made the changes, please retake a look.
rudrakhp: /retest
rudrakhp: /retest
kkk777-7: /retest
cnvergence: thanks for adding this @kkk777-7 
kkk777-7: /retest
kkk777-7: I found a case where e2e test fails in my PR.
https://github.com/envoyproxy/gateway/actions/runs/15141960646/job/42573699817?pr=6100

But, Maybe I believe it will be resolved once the following PR is merged into main.
https://github.com/envoyproxy/gateway/pull/6041

>Currently it is not supported to run Gateway Namespace Mode with Merged Gateways deployments.
kkk777-7: > curious about one thing - when a `Gateway` is deleted in `GatewayNamespace` mode, now the API server will delete the `Deployment`, there will be a race b/w the API server and EG to delete the resource, if the API server gets there 1st, will it show up as a err log in EG or does is gracefully handle this case today ?

@arkodg  That's a good point.
Infra struct call `i.Client.DeleteAllOf` when deleting resources.
e.g. https://github.com/envoyproxy/gateway/blob/main/internal/infrastructure/kubernetes/infra_resource.go#L453-L457

Maybe, controller-runtime's `DeleteAllOf` doesn't return an error if the resource isn't found. So I think EG can gracefully handle.

On a related note, InfraClient wraps the `Delete` method to handle errors. So, This can also be handled gracefully.
https://github.com/envoyproxy/gateway/blob/main/internal/infrastructure/kubernetes/infra_client.go#L72-L81 

Patch:
diff --git a/internal/gatewayapi/translator.go b/internal/gatewayapi/translator.go
index 0dd30d3a0..f4bf96d01 100644
--- a/internal/gatewayapi/translator.go
+++ b/internal/gatewayapi/translator.go
@@ -319,6 +319,10 @@ func (t *Translator) InitIRs(gateways []*GatewayContext) (map[string]*ir.Xds, ma
 		gwInfraIR.Proxy.Namespace = t.ControllerNamespace
 		if t.GatewayNamespaceMode {
 			gwInfraIR.Proxy.Namespace = gateway.Namespace
+			gwInfraIR.Proxy.GetProxyMetadata().OwnerReference = &ir.ResourceMetadata{
+				Kind: resource.KindGateway,
+				Name: gateway.Name,
+			}
 		}
 		// save the IR references in the map before the translation starts
 		xdsIR[irKey] = gwXdsIR
diff --git a/internal/infrastructure/kubernetes/proxy/resource_provider.go b/internal/infrastructure/kubernetes/proxy/resource_provider.go
index 17c2ee647..b83b9002b 100644
--- a/internal/infrastructure/kubernetes/proxy/resource_provider.go
+++ b/internal/infrastructure/kubernetes/proxy/resource_provider.go
@@ -6,6 +6,7 @@
 package proxy
 
 import (
+	"context"
 	"fmt"
 	"strconv"
 
@@ -16,11 +17,14 @@ import (
 	policyv1 "k8s.io/api/policy/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/labels"
+	"k8s.io/apimachinery/pkg/types"
 	"k8s.io/apimachinery/pkg/util/intstr"
 	"k8s.io/utils/ptr"
+	gwapiv1 "sigs.k8s.io/gateway-api/apis/v1"
 
 	egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1"
 	"github.com/envoyproxy/gateway/internal/gatewayapi"
+	gwapiresource "github.com/envoyproxy/gateway/internal/gatewayapi/resource"
 	"github.com/envoyproxy/gateway/internal/infrastructure/common"
 	"github.com/envoyproxy/gateway/internal/infrastructure/kubernetes/resource"
 	"github.com/envoyproxy/gateway/internal/ir"
@@ -58,17 +62,37 @@ type ResourceRender struct {
 	ShutdownManager *egv1a1.ShutdownManager
 
 	GatewayNamespaceMode bool
+
+	// ownerReferenceUID store the uid of its owner reference. Key is the kind of owner resource.
+	// - Gateway when enabled GatewayNamespaceMode
+	// - TODO: GatewayClass when enabled merged gateways
+	ownerReferenceUID map[string]types.UID
 }
 
-func NewResourceRender(envoyNamespace, controllerNamespace, dnsDomain string, infra *ir.ProxyInfra, gateway *egv1a1.EnvoyGateway) *ResourceRender {
-	return &ResourceRender{
-		envoyNamespace:       envoyNamespace,
-		controllerNamespace:  controllerNamespace,
-		DNSDomain:            dnsDomain,
-		infra:                infra,
-		ShutdownManager:      gateway.GetEnvoyGatewayProvider().GetEnvoyGatewayKubeProvider().ShutdownManager,
-		GatewayNamespaceMode: gateway.GatewayNamespaceMode(),
+// KubernetesInfraProvider provide information for initializing the proxy resource render.
+type KubernetesInfraProvider interface {
+	GetControllerNamespace() string
+	GetDNSDomain() string
+	GetEnvoyGateway() *egv1a1.EnvoyGateway
+	GetOwnerReferenceUID(ctx context.Context, infra *ir.Infra) (map[string]types.UID, error)
+	GetResourceNamespace(ir *ir.Infra) string
+}
+
+func NewResourceRender(ctx context.Context, kubernetesInfra KubernetesInfraProvider, infra *ir.Infra) (*ResourceRender, error) {
+	ownerReference, err := kubernetesInfra.GetOwnerReferenceUID(ctx, infra)
+	if err != nil {
+		return nil, err
 	}
+
+	return &ResourceRender{
+		envoyNamespace:       kubernetesInfra.GetResourceNamespace(infra),
+		controllerNamespace:  kubernetesInfra.GetControllerNamespace(),
+		DNSDomain:            kubernetesInfra.GetDNSDomain(),
+		infra:                infra.GetProxyInfra(),
+		ShutdownManager:      kubernetesInfra.GetEnvoyGateway().GetEnvoyGatewayProvider().GetEnvoyGatewayKubeProvider().ShutdownManager,
+		GatewayNamespaceMode: kubernetesInfra.GetEnvoyGateway().GatewayNamespaceMode(),
+		ownerReferenceUID:    ownerReference,
+	}, nil
 }
 
 func (r *ResourceRender) Name() string {
@@ -87,6 +111,21 @@ func (r *ResourceRender) LabelSelector() labels.Selector {
 	return labels.SelectorFromSet(r.stableSelector().MatchLabels)
 }
 
+func (r *ResourceRender) OwnerReferences() []metav1.OwnerReference {
+	var ownerReferences []metav1.OwnerReference
+	if r.ownerReferenceUID != nil && r.GatewayNamespaceMode {
+		if uid, ok := r.ownerReferenceUID[gwapiresource.KindGateway]; ok {
+			ownerReferences = append(ownerReferences, metav1.OwnerReference{
+				APIVersion: gwapiv1.GroupVersion.String(),
+				Kind:       r.infra.GetProxyMetadata().OwnerReference.Kind,
+				Name:       r.infra.GetProxyMetadata().OwnerReference.Name,
+				UID:        uid,
+			})
+		}
+	}
+	return ownerReferences
+}
+
 // ServiceAccount returns the expected proxy serviceAccount.
 func (r *ResourceRender) ServiceAccount() (*corev1.ServiceAccount, error) {
 	// Set the labels based on the owning gateway name.
@@ -101,10 +140,11 @@ func (r *ResourceRender) ServiceAccount() (*corev1.ServiceAccount, error) {
 			APIVersion: "v1",
 		},
 		ObjectMeta: metav1.ObjectMeta{
-			Namespace:   r.Namespace(),
-			Name:        r.Name(),
-			Labels:      labels,
-			Annotations: r.infra.GetProxyMetadata().Annotations,
+			Namespace:       r.Namespace(),
+			Name:            r.Name(),
+			Labels:          labels,
+			Annotations:     r.infra.GetProxyMetadata().Annotations,
+			OwnerReferences: r.OwnerReferences(),
 		},
 	}, nil
 }
@@ -208,9 +248,10 @@ func (r *ResourceRender) Service() (*corev1.Service, error) {
 			Kind:       "Service",
 		},
 		ObjectMeta: metav1.ObjectMeta{
-			Namespace:   r.Namespace(),
-			Labels:      svcLabels,
-			Annotations: annotations,
+			Namespace:       r.Namespace(),
+			Labels:          svcLabels,
+			Annotations:     annotations,
+			OwnerReferences: r.OwnerReferences(),
 		},
 		Spec: serviceSpec,
 	}
@@ -253,10 +294,11 @@ func (r *ResourceRender) ConfigMap(cert string) (*corev1.ConfigMap, error) {
 			APIVersion: "v1",
 		},
 		ObjectMeta: metav1.ObjectMeta{
-			Namespace:   r.Namespace(),
-			Name:        r.Name(),
-			Labels:      labels,
-			Annotations: r.infra.GetProxyMetadata().Annotations,
+			Namespace:       r.Namespace(),
+			Name:            r.Name(),
+			Labels:          labels,
+			Annotations:     r.infra.GetProxyMetadata().Annotations,
+			OwnerReferences: r.OwnerReferences(),
 		},
 		Data: data,
 	}, nil
@@ -312,9 +354,10 @@ func (r *ResourceRender) Deployment() (*appsv1.Deployment, error) {
 			APIVersion: "apps/v1",
 		},
 		ObjectMeta: metav1.ObjectMeta{
-			Namespace:   r.Namespace(),
-			Labels:      dpLabels,
-			Annotations: dpAnnotations,
+			Namespace:       r.Namespace(),
+			Labels:          dpLabels,
+			Annotations:     dpAnnotations,
+			OwnerReferences: r.OwnerReferences(),
 		},
 		Spec: appsv1.DeploymentSpec{
 			Replicas: deploymentConfig.Replicas,
@@ -400,9 +443,10 @@ func (r *ResourceRender) DaemonSet() (*appsv1.DaemonSet, error) {
 			APIVersion: "apps/v1",
 		},
 		ObjectMeta: metav1.ObjectMeta{
-			Namespace:   r.Namespace(),
-			Labels:      dsLabels,
-			Annotations: dsAnnotations,
+			Namespace:       r.Namespace(),
+			Labels:          dsLabels,
+			Annotations:     dsAnnotations,
+			OwnerReferences: r.OwnerReferences(),
 		},
 		Spec: appsv1.DaemonSetSpec{
 			// Daemonset's selector is immutable.
@@ -468,8 +512,9 @@ func (r *ResourceRender) PodDisruptionBudget() (*policyv1.PodDisruptionBudget, e
 
 	podDisruptionBudget := &policyv1.PodDisruptionBudget{
 		ObjectMeta: metav1.ObjectMeta{
-			Name:      r.Name(),
-			Namespace: r.Namespace(),
+			Name:            r.Name(),
+			Namespace:       r.Namespace(),
+			OwnerReferences: r.OwnerReferences(),
 		},
 		TypeMeta: metav1.TypeMeta{
 			APIVersion: "policy/v1",
@@ -503,10 +548,11 @@ func (r *ResourceRender) HorizontalPodAutoscaler() (*autoscalingv2.HorizontalPod
 			Kind:       "HorizontalPodAutoscaler",
 		},
 		ObjectMeta: metav1.ObjectMeta{
-			Namespace:   r.Namespace(),
-			Name:        r.Name(),
-			Annotations: r.infra.GetProxyMetadata().Annotations,
-			Labels:      r.infra.GetProxyMetadata().Labels,
+			Namespace:       r.Namespace(),
+			Name:            r.Name(),
+			Annotations:     r.infra.GetProxyMetadata().Annotations,
+			Labels:          r.infra.GetProxyMetadata().Labels,
+			OwnerReferences: r.OwnerReferences(),
 		},
 		Spec: autoscalingv2.HorizontalPodAutoscalerSpec{
 			ScaleTargetRef: autoscalingv2.CrossVersionObjectReference{
diff --git a/internal/infrastructure/kubernetes/proxy_infra.go b/internal/infrastructure/kubernetes/proxy_infra.go
index 525048987..c4d851772 100644
--- a/internal/infrastructure/kubernetes/proxy_infra.go
+++ b/internal/infrastructure/kubernetes/proxy_infra.go
@@ -8,40 +8,87 @@ package kubernetes
 import (
 	"context"
 	"errors"
+	"fmt"
 
+	"k8s.io/apimachinery/pkg/types"
+	gwapiv1 "sigs.k8s.io/gateway-api/apis/v1"
+
+	egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1"
+	"github.com/envoyproxy/gateway/internal/gatewayapi/resource"
 	"github.com/envoyproxy/gateway/internal/infrastructure/kubernetes/proxy"
 	"github.com/envoyproxy/gateway/internal/ir"
 )
 
 // CreateOrUpdateProxyInfra creates the managed kube infra, if it doesn't exist.
-func (i *Infra) CreateOrUpdateProxyInfra(ctx context.Context, infra *ir.Infra) error {
-	if infra == nil {
+func (i *Infra) CreateOrUpdateProxyInfra(ctx context.Context, irInfra *ir.Infra) error {
+	if irInfra == nil {
 		return errors.New("infra ir is nil")
 	}
 
-	if infra.Proxy == nil {
+	if irInfra.Proxy == nil {
 		return errors.New("infra proxy ir is nil")
 	}
 
-	envoyNamespace := i.GetResourceNamespace(infra)
-	r := proxy.NewResourceRender(envoyNamespace, i.ControllerNamespace, i.DNSDomain, infra.GetProxyInfra(), i.EnvoyGateway)
+	r, err := proxy.NewResourceRender(ctx, i, irInfra)
+	if err != nil {
+		return fmt.Errorf("failed to initialize proxy resource render: %w", err)
+	}
 	return i.createOrUpdate(ctx, r)
 }
 
 // DeleteProxyInfra removes the managed kube infra, if it doesn't exist.
-func (i *Infra) DeleteProxyInfra(ctx context.Context, infra *ir.Infra) error {
-	if infra == nil {
+func (i *Infra) DeleteProxyInfra(ctx context.Context, irInfra *ir.Infra) error {
+	if irInfra == nil {
 		return errors.New("infra ir is nil")
 	}
 
-	envoyNamespace := i.GetResourceNamespace(infra)
-	r := proxy.NewResourceRender(envoyNamespace, i.ControllerNamespace, i.DNSDomain, infra.GetProxyInfra(), i.EnvoyGateway)
+	r, err := proxy.NewResourceRender(ctx, i, irInfra)
+	if err != nil {
+		return fmt.Errorf("failed to create proxy resource render: %w", err)
+	}
 	return i.delete(ctx, r)
 }
 
-func (i *Infra) GetResourceNamespace(infra *ir.Infra) string {
+func (i *Infra) GetControllerNamespace() string {
+	return i.ControllerNamespace
+}
+
+func (i *Infra) GetDNSDomain() string {
+	return i.DNSDomain
+}
+
+func (i *Infra) GetEnvoyGateway() *egv1a1.EnvoyGateway {
+	return i.EnvoyGateway
+}
+
+func (i *Infra) GetOwnerReferenceUID(ctx context.Context, irInfra *ir.Infra) (map[string]types.UID, error) {
+	ownerReferenceUID := make(map[string]types.UID)
+
+	if i.EnvoyGateway.GatewayNamespaceMode() {
+		if irInfra.GetProxyInfra().GetProxyMetadata() == nil {
+			return nil, errors.New("infra proxy metadata ir is nil")
+		}
+		if irInfra.GetProxyInfra().GetProxyMetadata().OwnerReference == nil {
+			return nil, errors.New("infra proxy metadata owner reference ir is nil")
+		}
+		key := types.NamespacedName{
+			Namespace: i.GetResourceNamespace(irInfra),
+			Name:      irInfra.GetProxyInfra().GetProxyMetadata().OwnerReference.Name,
+		}
+		gatewayUID, err := i.Client.GetUID(ctx, key, &gwapiv1.Gateway{})
+		if err != nil {
+			return nil, err
+		}
+		ownerReferenceUID[resource.KindGateway] = gatewayUID
+	}
+	// TODO: set GatewayClass UID when enable merged gateways
+
+	return ownerReferenceUID, nil
+}
+
+func (i *Infra) GetResourceNamespace(irInfra *ir.Infra) string {
 	if i.EnvoyGateway.GatewayNamespaceMode() {
-		return infra.Proxy.Namespace
+		return irInfra.Proxy.Namespace
 	}
 	return i.ControllerNamespace
 }
diff --git a/internal/ir/infra.go b/internal/ir/infra.go
index d6ab713c8..f4c95bd33 100644
--- a/internal/ir/infra.go
+++ b/internal/ir/infra.go
@@ -67,6 +67,8 @@ type InfraMetadata struct {
 	// Labels define a map of string keys and values that can be used to organize
 	// and categorize proxy infrastructure objects.
 	Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"`
+	// OwnerReference define the values that be used to set ownerReference for proxy infrastructure objects.
+	OwnerReference *ResourceMetadata `json:"ownerReference,omitempty" yaml:"ownerReference,omitempty"`
 }
 
 // ProxyListener defines the listener configuration of the proxy infrastructure.
diff --git a/internal/ir/zz_generated.deepcopy.go b/internal/ir/zz_generated.deepcopy.go
index 8ee15b167..1b6b74cd6 100644
--- a/internal/ir/zz_generated.deepcopy.go
+++ b/internal/ir/zz_generated.deepcopy.go
@@ -1994,6 +1994,11 @@ func (in *InfraMetadata) DeepCopyInto(out *InfraMetadata) {
 			(*out)[key] = val
 		}
 	}
+	if in.OwnerReference != nil {
+		in, out := &in.OwnerReference, &out.OwnerReference
+		*out = new(ResourceMetadata)
+		(*in).DeepCopyInto(*out)
+	}
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InfraMetadata.
diff --git a/release-notes/current.yaml b/release-notes/current.yaml
index d704edba9..80df73d65 100644
--- a/release-notes/current.yaml
+++ b/release-notes/current.yaml
@@ -13,6 +13,7 @@ new features: |
   Added support for configuring Subject Alternative Names (SANs) for upstream TLS validation via `BackendTLSPolicy.validation.subjectAltNames`.
   Added support for local rate limit header.
   Added XDS metadata for clusters and endpoints from xRoutes and referenced backend resources (Backend, Service, ServiceImport).
+  Added support for setting ownerreference to infra resources when enable gateway namespace mode.
 
 bug fixes: |
   Fixed issue where WASM cache init failure caused routes with WASM-less EnvoyExtensionPolicies to have 500 direct responses.


Test patch:
diff --git a/internal/gatewayapi/testdata/gateway-namespace-mode-infra-httproute.out.yaml b/internal/gatewayapi/testdata/gateway-namespace-mode-infra-httproute.out.yaml
index bce08a638..ecdd8f6df 100644
--- a/internal/gatewayapi/testdata/gateway-namespace-mode-infra-httproute.out.yaml
+++ b/internal/gatewayapi/testdata/gateway-namespace-mode-infra-httproute.out.yaml
@@ -240,6 +240,9 @@ infraIR:
         labels:
           gateway.envoyproxy.io/owning-gateway-name: gateway-1
           gateway.envoyproxy.io/owning-gateway-namespace: default
+        ownerReference:
+          kind: Gateway
+          name: gateway-1
       name: default/gateway-1
       namespace: default
   default/gateway-2:
@@ -256,6 +259,9 @@ infraIR:
         labels:
           gateway.envoyproxy.io/owning-gateway-name: gateway-2
           gateway.envoyproxy.io/owning-gateway-namespace: default
+        ownerReference:
+          kind: Gateway
+          name: gateway-2
       name: default/gateway-2
       namespace: default
   test-ns/gateway-3:
@@ -272,6 +278,9 @@ infraIR:
         labels:
           gateway.envoyproxy.io/owning-gateway-name: gateway-3
           gateway.envoyproxy.io/owning-gateway-namespace: test-ns
+        ownerReference:
+          kind: Gateway
+          name: gateway-3
       name: test-ns/gateway-3
       namespace: test-ns
 xdsIR:
diff --git a/internal/infrastructure/kubernetes/proxy/resource_provider_test.go b/internal/infrastructure/kubernetes/proxy/resource_provider_test.go
index f8165efac..d99fd82be 100644
--- a/internal/infrastructure/kubernetes/proxy/resource_provider_test.go
+++ b/internal/infrastructure/kubernetes/proxy/resource_provider_test.go
@@ -6,6 +6,7 @@
 package proxy
 
 import (
+	"context"
 	"fmt"
 	"os"
 	"sort"
@@ -21,6 +22,7 @@ import (
 	apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
 	"k8s.io/apimachinery/pkg/api/resource"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/types"
 	"k8s.io/apimachinery/pkg/util/intstr"
 	"k8s.io/utils/ptr"
 	"sigs.k8s.io/yaml"
@@ -28,6 +30,7 @@ import (
 	egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1"
 	"github.com/envoyproxy/gateway/internal/envoygateway/config"
 	"github.com/envoyproxy/gateway/internal/gatewayapi"
+	gwapiresource "github.com/envoyproxy/gateway/internal/gatewayapi/resource"
 	"github.com/envoyproxy/gateway/internal/ir"
 	"github.com/envoyproxy/gateway/internal/utils/test"
 )
@@ -39,15 +42,59 @@ const (
 	envoyHTTPSPort = int32(8443)
 )
 
+type fakeKubernetesInfraProvider struct {
+	ControllerNamespace string
+	DNSDomain           string
+	EnvoyGateway        *egv1a1.EnvoyGateway
+}
+
+func newFakeKubernetesInfraProvider(cfg *config.Server) KubernetesInfraProvider {
+	return &fakeKubernetesInfraProvider{
+		ControllerNamespace: cfg.ControllerNamespace,
+		DNSDomain:           cfg.DNSDomain,
+		EnvoyGateway:        cfg.EnvoyGateway,
+	}
+}
+
+func (f *fakeKubernetesInfraProvider) GetControllerNamespace() string {
+	return f.ControllerNamespace
+}
+
+func (f *fakeKubernetesInfraProvider) GetDNSDomain() string {
+	return f.DNSDomain
+}
+
+func (f *fakeKubernetesInfraProvider) GetEnvoyGateway() *egv1a1.EnvoyGateway {
+	return f.EnvoyGateway
+}
+
+func (f *fakeKubernetesInfraProvider) GetOwnerReferenceUID(ctx context.Context, infra *ir.Infra) (map[string]types.UID, error) {
+	return map[string]types.UID{
+		gwapiresource.KindGateway: "test-owner-reference-uid-for-gateway",
+	}, nil
+}
+
+func (f *fakeKubernetesInfraProvider) GetResourceNamespace(infra *ir.Infra) string {
+	if f.EnvoyGateway.GatewayNamespaceMode() {
+		return infra.Proxy.Namespace
+	}
+	return f.ControllerNamespace
+}
+
 func newTestInfra() *ir.Infra {
 	return newTestInfraWithAnnotations(nil)
 }
 
-func newTestInfraWithNamespace(namespace string) *ir.Infra {
-	i := ir.NewInfra()
+func newTestInfraWithNamespaceName(namespace, name, proxyName string) *ir.Infra {
+	i := newTestInfraWithAnnotations(nil)
+	i.Proxy.Name = proxyName
 	i.Proxy.Namespace = namespace
 	i.Proxy.GetProxyMetadata().Labels[gatewayapi.OwningGatewayNamespaceLabel] = namespace
-	i.Proxy.GetProxyMetadata().Labels[gatewayapi.OwningGatewayNameLabel] = i.Proxy.Name
+	i.Proxy.GetProxyMetadata().Labels[gatewayapi.OwningGatewayNameLabel] = name
+	i.Proxy.GetProxyMetadata().OwnerReference = &ir.ResourceMetadata{
+		Kind: "Gateway",
+		Name: name,
+	}
 
 	return i
 }
@@ -568,6 +615,12 @@ func TestDeployment(t *testing.T) {
 				Name: ptr.To("custom-deployment-name"),
 			},
 		},
+		{
+			caseName:             "gateway-namespace-mode",
+			infra:                newTestInfraWithNamespaceName("ns1", "gateway-1", "ns1/gateway-1"),
+			deploy:               nil,
+			gatewayNamespaceMode: true,
+		},
 	}
 	for _, tc := range cases {
 		t.Run(tc.caseName, func(t *testing.T) {
@@ -612,8 +665,20 @@ func TestDeployment(t *testing.T) {
 			if len(tc.extraArgs) > 0 {
 				tc.infra.Proxy.Config.Spec.ExtraArgs = tc.extraArgs
 			}
-			infraNamespace := cfg.ControllerNamespace
-			r := NewResourceRender(infraNamespace, cfg.ControllerNamespace, cfg.DNSDomain, tc.infra.GetProxyInfra(), cfg.EnvoyGateway)
+			if tc.gatewayNamespaceMode {
+				deployType := egv1a1.KubernetesDeployModeType(egv1a1.KubernetesDeployModeTypeGatewayNamespace)
+				cfg.EnvoyGateway.Provider = &egv1a1.EnvoyGatewayProvider{
+					Type: egv1a1.ProviderTypeKubernetes,
+					Kubernetes: &egv1a1.EnvoyGatewayKubernetesProvider{
+						Deploy: &egv1a1.KubernetesDeployMode{
+							Type: &deployType,
+						},
+					},
+				}
+			}
+
+			r, err := NewResourceRender(context.Background(), newFakeKubernetesInfraProvider(cfg), tc.infra)
+			require.NoError(t, err)
 			dp, err := r.Deployment()
 			require.NoError(t, err)
 
@@ -657,15 +722,16 @@ func TestDaemonSet(t *testing.T) {
 	require.NoError(t, err)
 
 	cases := []struct {
-		caseName     string
-		infra        *ir.Infra
-		daemonset    *egv1a1.KubernetesDaemonSetSpec
-		shutdown     *egv1a1.ShutdownConfig
-		proxyLogging map[egv1a1.ProxyLogComponent]egv1a1.LogLevel
-		bootstrap    string
-		telemetry    *egv1a1.ProxyTelemetry
-		concurrency  *int32
-		extraArgs    []string
+		caseName             string
+		infra                *ir.Infra
+		daemonset            *egv1a1.KubernetesDaemonSetSpec
+		shutdown             *egv1a1.ShutdownConfig
+		proxyLogging         map[egv1a1.ProxyLogComponent]egv1a1.LogLevel
+		bootstrap            string
+		telemetry            *egv1a1.ProxyTelemetry
+		concurrency          *int32
+		extraArgs            []string
+		gatewayNamespaceMode bool
 	}{
 		{
 			caseName:  "default",
@@ -998,9 +1064,27 @@ func TestDaemonSet(t *testing.T) {
 				Name: ptr.To("custom-daemonset-name"),
 			},
 		},
+		{
+			caseName:             "gateway-namespace-mode",
+			infra:                newTestInfraWithNamespaceName("ns1", "gateway-1", "ns1/gateway-1"),
+			daemonset:            nil,
+			gatewayNamespaceMode: true,
+		},
 	}
 	for _, tc := range cases {
 		t.Run(tc.caseName, func(t *testing.T) {
+			if tc.gatewayNamespaceMode {
+				deployType := egv1a1.KubernetesDeployModeType(egv1a1.KubernetesDeployModeTypeGatewayNamespace)
+				cfg.EnvoyGateway.Provider = &egv1a1.EnvoyGatewayProvider{
+					Type: egv1a1.ProviderTypeKubernetes,
+					Kubernetes: &egv1a1.EnvoyGatewayKubernetesProvider{
+						Deploy: &egv1a1.KubernetesDeployMode{
+							Type: &deployType,
+						},
+					},
+				}
+			}
+
 			kube := tc.infra.GetProxyInfra().GetProxyConfig().GetEnvoyProxyProvider().GetEnvoyProxyKubeProvider()
 
 			// fill deploument, use daemonset
@@ -1042,7 +1126,8 @@ func TestDaemonSet(t *testing.T) {
 				tc.infra.Proxy.Config.Spec.ExtraArgs = tc.extraArgs
 			}
 
-			r := NewResourceRender(cfg.ControllerNamespace, cfg.ControllerNamespace, cfg.DNSDomain, tc.infra.GetProxyInfra(), cfg.EnvoyGateway)
+			r, err := NewResourceRender(context.Background(), newFakeKubernetesInfraProvider(cfg), tc.infra)
+			require.NoError(t, err)
 			ds, err := r.DaemonSet()
 			require.NoError(t, err)
 
@@ -1087,9 +1172,10 @@ func TestService(t *testing.T) {
 
 	svcType := egv1a1.ServiceTypeClusterIP
 	cases := []struct {
-		caseName string
-		infra    *ir.Infra
-		service  *egv1a1.KubernetesServiceSpec
+		caseName             string
+		infra                *ir.Infra
+		service              *egv1a1.KubernetesServiceSpec
+		gatewayNamespaceMode bool
 	}{
 		{
 			caseName: "default",
@@ -1199,15 +1285,34 @@ func TestService(t *testing.T) {
 			infra:    newTestInfraWithIPFamily(ptr.To(egv1a1.IPv6)),
 			service:  nil,
 		},
+		{
+			caseName:             "gateway-namespace-mode",
+			infra:                newTestInfraWithNamespaceName("ns1", "gateway-1", "ns1/gateway-1"),
+			service:              nil,
+			gatewayNamespaceMode: true,
+		},
 	}
 	for _, tc := range cases {
 		t.Run(tc.caseName, func(t *testing.T) {
+			if tc.gatewayNamespaceMode {
+				deployType := egv1a1.KubernetesDeployModeType(egv1a1.KubernetesDeployModeTypeGatewayNamespace)
+				cfg.EnvoyGateway.Provider = &egv1a1.EnvoyGatewayProvider{
+					Type: egv1a1.ProviderTypeKubernetes,
+					Kubernetes: &egv1a1.EnvoyGatewayKubernetesProvider{
+						Deploy: &egv1a1.KubernetesDeployMode{
+							Type: &deployType,
+						},
+					},
+				}
+			}
+
 			provider := tc.infra.GetProxyInfra().GetProxyConfig().GetEnvoyProxyProvider().GetEnvoyProxyKubeProvider()
 			if tc.service != nil {
 				provider.EnvoyService = tc.service
 			}
 
-			r := NewResourceRender(cfg.ControllerNamespace, cfg.ControllerNamespace, cfg.DNSDomain, tc.infra.GetProxyInfra(), cfg.EnvoyGateway)
+			r, err := NewResourceRender(context.Background(), newFakeKubernetesInfraProvider(cfg), tc.infra)
+			require.NoError(t, err)
 			svc, err := r.Service()
 			require.NoError(t, err)
 
@@ -1233,24 +1338,43 @@ func TestConfigMap(t *testing.T) {
 	cfg, err := config.New(os.Stdout)
 	require.NoError(t, err)
 	cases := []struct {
-		name  string
-		infra *ir.Infra
+		name                 string
+		infra                *ir.Infra
+		gatewayNamespaceMode bool
 	}{
 		{
 			name:  "default",
 			infra: newTestInfra(),
-		}, {
+		},
+		{
 			name: "with-annotations",
 			infra: newTestInfraWithAnnotations(map[string]string{
 				"anno1": "value1",
 				"anno2": "value2",
 			}),
 		},
+		{
+			name:                 "gateway-namespace-mode",
+			infra:                newTestInfraWithNamespaceName("ns1", "gateway-1", "ns1/gateway-1"),
+			gatewayNamespaceMode: true,
+		},
 	}
 
 	for _, tc := range cases {
 		t.Run(tc.name, func(t *testing.T) {
-			r := NewResourceRender(cfg.ControllerNamespace, cfg.ControllerNamespace, cfg.DNSDomain, tc.infra.GetProxyInfra(), cfg.EnvoyGateway)
+			if tc.gatewayNamespaceMode {
+				deployType := egv1a1.KubernetesDeployModeType(egv1a1.KubernetesDeployModeTypeGatewayNamespace)
+				cfg.EnvoyGateway.Provider = &egv1a1.EnvoyGatewayProvider{
+					Type: egv1a1.ProviderTypeKubernetes,
+					Kubernetes: &egv1a1.EnvoyGatewayKubernetesProvider{
+						Deploy: &egv1a1.KubernetesDeployMode{
+							Type: &deployType,
+						},
+					},
+				}
+			}
+			r, err := NewResourceRender(context.Background(), newFakeKubernetesInfraProvider(cfg), tc.infra)
+			require.NoError(t, err)
 			cm, err := r.ConfigMap("")
 			require.NoError(t, err)
 
@@ -1293,14 +1417,13 @@ func TestServiceAccount(t *testing.T) {
 		},
 		{
 			name:                 "gateway-namespace-mode",
-			infra:                newTestInfraWithNamespace("ns1"),
+			infra:                newTestInfraWithNamespaceName("ns1", "gateway-1", "ns1/gateway-1"),
 			gatewayNamespaceMode: true,
 		},
 	}
 
 	for _, tc := range cases {
 		t.Run(tc.name, func(t *testing.T) {
-			ns := cfg.ControllerNamespace
 			if tc.gatewayNamespaceMode {
 				deployType := egv1a1.KubernetesDeployModeType(egv1a1.KubernetesDeployModeTypeGatewayNamespace)
 				cfg.EnvoyGateway.Provider = &egv1a1.EnvoyGatewayProvider{
@@ -1311,9 +1434,9 @@ func TestServiceAccount(t *testing.T) {
 						},
 					},
 				}
-				ns = tc.infra.GetProxyInfra().Namespace
 			}
-			r := NewResourceRender(ns, cfg.ControllerNamespace, cfg.DNSDomain, tc.infra.GetProxyInfra(), cfg.EnvoyGateway)
+			r, err := NewResourceRender(context.Background(), newFakeKubernetesInfraProvider(cfg), tc.infra)
+			require.NoError(t, err)
 			sa, err := r.ServiceAccount()
 			require.NoError(t, err)
 
@@ -1349,10 +1472,11 @@ func TestPDB(t *testing.T) {
 	require.NoError(t, err)
 
 	cases := []struct {
-		caseName string
-		infra    *ir.Infra
-		pdb      *egv1a1.KubernetesPodDisruptionBudgetSpec
-		deploy   *egv1a1.KubernetesDeploymentSpec
+		caseName             string
+		infra                *ir.Infra
+		pdb                  *egv1a1.KubernetesPodDisruptionBudgetSpec
+		deploy               *egv1a1.KubernetesDeploymentSpec
+		gatewayNamespaceMode bool
 	}{
 		{
 			caseName: "default",
@@ -1420,10 +1544,30 @@ func TestPDB(t *testing.T) {
 				},
 			},
 		},
+		{
+			caseName: "gateway-namespace-mode",
+			infra:    newTestInfraWithNamespaceName("ns1", "gateway-1", "ns1/gateway-1"),
+			pdb: &egv1a1.KubernetesPodDisruptionBudgetSpec{
+				MinAvailable: ptr.To(intstr.IntOrString{Type: intstr.Int, IntVal: 1}),
+			},
+			gatewayNamespaceMode: true,
+		},
 	}
 
 	for _, tc := range cases {
 		t.Run(tc.caseName, func(t *testing.T) {
+			if tc.gatewayNamespaceMode {
+				deployType := egv1a1.KubernetesDeployModeType(egv1a1.KubernetesDeployModeTypeGatewayNamespace)
+				cfg.EnvoyGateway.Provider = &egv1a1.EnvoyGatewayProvider{
+					Type: egv1a1.ProviderTypeKubernetes,
+					Kubernetes: &egv1a1.EnvoyGatewayKubernetesProvider{
+						Deploy: &egv1a1.KubernetesDeployMode{
+							Type: &deployType,
+						},
+					},
+				}
+			}
+
 			provider := tc.infra.GetProxyInfra().GetProxyConfig().GetEnvoyProxyProvider()
 			provider.Kubernetes = egv1a1.DefaultEnvoyProxyKubeProvider()
 
@@ -1437,7 +1581,8 @@ func TestPDB(t *testing.T) {
 
 			provider.GetEnvoyProxyKubeProvider()
 
-			r := NewResourceRender(cfg.ControllerNamespace, cfg.ControllerNamespace, cfg.DNSDomain, tc.infra.GetProxyInfra(), cfg.EnvoyGateway)
+			r, err := NewResourceRender(context.Background(), newFakeKubernetesInfraProvider(cfg), tc.infra)
+			require.NoError(t, err)
 
 			pdb, err := r.PodDisruptionBudget()
 			require.NoError(t, err)
@@ -1455,10 +1600,11 @@ func TestHorizontalPodAutoscaler(t *testing.T) {
 	require.NoError(t, err)
 
 	cases := []struct {
-		caseName string
-		infra    *ir.Infra
-		hpa      *egv1a1.KubernetesHorizontalPodAutoscalerSpec
-		deploy   *egv1a1.KubernetesDeploymentSpec
+		caseName             string
+		infra                *ir.Infra
+		hpa                  *egv1a1.KubernetesHorizontalPodAutoscalerSpec
+		deploy               *egv1a1.KubernetesDeploymentSpec
+		gatewayNamespaceMode bool
 	}{
 		{
 			caseName: "default",
@@ -1534,10 +1680,30 @@ func TestHorizontalPodAutoscaler(t *testing.T) {
 				Name: ptr.To("custom-deployment-name"),
 			},
 		},
+		{
+			caseName: "gateway-namespace-mode",
+			infra:    newTestInfraWithNamespaceName("ns1", "gateway-1", "ns1/gateway-1"),
+			hpa: &egv1a1.KubernetesHorizontalPodAutoscalerSpec{
+				MaxReplicas: ptr.To[int32](1),
+			},
+			gatewayNamespaceMode: true,
+		},
 	}
 
 	for _, tc := range cases {
 		t.Run(tc.caseName, func(t *testing.T) {
+			if tc.gatewayNamespaceMode {
+				deployType := egv1a1.KubernetesDeployModeType(egv1a1.KubernetesDeployModeTypeGatewayNamespace)
+				cfg.EnvoyGateway.Provider = &egv1a1.EnvoyGatewayProvider{
+					Type: egv1a1.ProviderTypeKubernetes,
+					Kubernetes: &egv1a1.EnvoyGatewayKubernetesProvider{
+						Deploy: &egv1a1.KubernetesDeployMode{
+							Type: &deployType,
+						},
+					},
+				}
+			}
+
 			provider := tc.infra.GetProxyInfra().GetProxyConfig().GetEnvoyProxyProvider()
 			provider.Kubernetes = egv1a1.DefaultEnvoyProxyKubeProvider()
 
@@ -1549,7 +1715,8 @@ func TestHorizontalPodAutoscaler(t *testing.T) {
 			}
 			provider.GetEnvoyProxyKubeProvider()
 
-			r := NewResourceRender(cfg.ControllerNamespace, cfg.ControllerNamespace, cfg.DNSDomain, tc.infra.GetProxyInfra(), cfg.EnvoyGateway)
+			r, err := NewResourceRender(context.Background(), newFakeKubernetesInfraProvider(cfg), tc.infra)
+			require.NoError(t, err)
 			hpa, err := r.HorizontalPodAutoscaler()
 			require.NoError(t, err)
 
@@ -1695,9 +1862,7 @@ func TestGatewayNamespaceModeMultipleResources(t *testing.T) {
 
 	// Create test infra with multiple namespaces
 	var infraList []*ir.Infra
-	infra1 := newTestInfraWithNamespace("namespace-1")
-	infra1.Proxy.Name = "namespace-1/gateway-1"
-	infra1.Proxy.GetProxyMetadata().Labels[gatewayapi.OwningGatewayNameLabel] = "gateway-1"
+	infra1 := newTestInfraWithNamespaceName("namespace-1", "gateway-1", "namespace-1/gateway-1")
 	// Add HPA config to first infra
 	if infra1.Proxy.Config == nil {
 		infra1.Proxy.Config = &egv1a1.EnvoyProxy{Spec: egv1a1.EnvoyProxySpec{}}
@@ -1714,9 +1879,7 @@ func TestGatewayNamespaceModeMultipleResources(t *testing.T) {
 		MaxReplicas: ptr.To[int32](3),
 	}
 
-	infra2 := newTestInfraWithNamespace("namespace-2")
-	infra2.Proxy.Name = "namespace-2/gateway-2"
-	infra2.Proxy.GetProxyMetadata().Labels[gatewayapi.OwningGatewayNameLabel] = "gateway-2"
+	infra2 := newTestInfraWithNamespaceName("namespace-2", "gateway-2", "namespace-2/gateway-2")
 	// Add HPA config to second infra
 	if infra2.Proxy.Config == nil {
 		infra2.Proxy.Config = &egv1a1.EnvoyProxy{Spec: egv1a1.EnvoyProxySpec{}}
@@ -1741,9 +1904,8 @@ func TestGatewayNamespaceModeMultipleResources(t *testing.T) {
 	hpas := make([]*autoscalingv2.HorizontalPodAutoscaler, 0, len(infraList))
 
 	for _, infra := range infraList {
-		namespace := infra.GetProxyInfra().Namespace
-		r := NewResourceRender(namespace, cfg.ControllerNamespace, cfg.DNSDomain,
-			infra.GetProxyInfra(), cfg.EnvoyGateway)
+		r, err := NewResourceRender(context.Background(), newFakeKubernetesInfraProvider(cfg), infra)
+		require.NoError(t, err)
 
 		dp, err := r.Deployment()
 		require.NoError(t, err)
diff --git a/internal/infrastructure/kubernetes/proxy/testdata/configmap/gateway-namespace-mode.yaml b/internal/infrastructure/kubernetes/proxy/testdata/configmap/gateway-namespace-mode.yaml
new file mode 100644
index 000000000..69cf6c2ad
--- /dev/null
+++ b/internal/infrastructure/kubernetes/proxy/testdata/configmap/gateway-namespace-mode.yaml
@@ -0,0 +1,19 @@
+apiVersion: v1
+kind: ConfigMap
+metadata:
+  labels:
+    app.kubernetes.io/name: envoy
+    app.kubernetes.io/component: proxy
+    app.kubernetes.io/managed-by: envoy-gateway
+    gateway.envoyproxy.io/owning-gateway-name: gateway-1
+    gateway.envoyproxy.io/owning-gateway-namespace: ns1
+  ownerReferences:
+  - apiVersion: gateway.networking.k8s.io/v1
+    kind: Gateway
+    name: gateway-1
+    uid: test-owner-reference-uid-for-gateway
+  name: envoy-ns1-gateway-1-02ae0474
+  namespace: ns1
+data:
+  xds-certificate.json: '{"resources":[{"@type":"type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.Secret","name":"xds_certificate","tls_certificate":{"certificate_chain":{"filename":"/certs/tls.crt"},"private_key":{"filename":"/certs/tls.key"}}}]}'
+  xds-trusted-ca.json: '{"resources":[{"@type":"type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.Secret","name":"xds_trusted_ca","validation_context":{"trusted_ca":{"filename":"/certs/ca.crt"},"match_typed_subject_alt_names":[{"san_type":"DNS","matcher":{"exact":"envoy-gateway"}}]}}]}'
diff --git a/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/gateway-namespace-mode.yaml b/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/gateway-namespace-mode.yaml
new file mode 100644
index 000000000..03c3ff4fc
--- /dev/null
+++ b/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/gateway-namespace-mode.yaml
@@ -0,0 +1,451 @@
+apiVersion: apps/v1
+kind: DaemonSet
+metadata:
+  creationTimestamp: null
+  labels:
+    app.kubernetes.io/component: proxy
+    app.kubernetes.io/managed-by: envoy-gateway
+    app.kubernetes.io/name: envoy
+    gateway.envoyproxy.io/owning-gateway-name: gateway-1
+    gateway.envoyproxy.io/owning-gateway-namespace: ns1
+  name: envoy-ns1-gateway-1-02ae0474
+  namespace: ns1
+  ownerReferences:
+  - apiVersion: gateway.networking.k8s.io/v1
+    kind: Gateway
+    name: gateway-1
+    uid: test-owner-reference-uid-for-gateway
+spec:
+  selector:
+    matchLabels:
+      app.kubernetes.io/component: proxy
+      app.kubernetes.io/managed-by: envoy-gateway
+      app.kubernetes.io/name: envoy
+      gateway.envoyproxy.io/owning-gateway-name: gateway-1
+      gateway.envoyproxy.io/owning-gateway-namespace: ns1
+  template:
+    metadata:
+      annotations:
+        prometheus.io/path: /stats/prometheus
+        prometheus.io/port: "19001"
+        prometheus.io/scrape: "true"
+      creationTimestamp: null
+      labels:
+        app.kubernetes.io/component: proxy
+        app.kubernetes.io/managed-by: envoy-gateway
+        app.kubernetes.io/name: envoy
+        gateway.envoyproxy.io/owning-gateway-name: gateway-1
+        gateway.envoyproxy.io/owning-gateway-namespace: ns1
+    spec:
+      containers:
+      - args:
+        - --service-cluster ns1/gateway-1
+        - --service-node $(ENVOY_POD_NAME)
+        - |
+          --config-yaml admin:
+            access_log:
+            - name: envoy.access_loggers.file
+              typed_config:
+                "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog
+                path: /dev/null
+            address:
+              socket_address:
+                address: 127.0.0.1
+                port_value: 19000
+          cluster_manager:
+            local_cluster_name: local_cluster
+          node:
+            locality:
+              zone: "$(ENVOY_SERVICE_ZONE)"
+          layered_runtime:
+            layers:
+            - name: global_config
+              static_layer:
+                envoy.restart_features.use_eds_cache_for_ads: true
+                re2.max_program_size.error_level: 4294967295
+                re2.max_program_size.warn_level: 1000
+          dynamic_resources:
+            ads_config:
+              api_type: DELTA_GRPC
+              transport_api_version: V3
+              grpc_services:
+              - envoy_grpc:
+                  cluster_name: xds_cluster
+              set_node_on_first_message_only: true
+            lds_config:
+              ads: {}
+              resource_api_version: V3
+            cds_config:
+              ads: {}
+              resource_api_version: V3
+          static_resources:
+            listeners:
+            - name: envoy-gateway-proxy-stats-0.0.0.0-19001
+              address:
+                socket_address:
+                  address: '0.0.0.0'
+                  port_value: 19001
+                  protocol: TCP
+              bypass_overload_manager: true
+              filter_chains:
+              - filters:
+                - name: envoy.filters.network.http_connection_manager
+                  typed_config:
+                    "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
+                    stat_prefix: eg-stats-http
+                    normalize_path: true
+                    route_config:
+                      name: local_route
+                      virtual_hosts:
+                      - name: prometheus_stats
+                        domains:
+                        - "*"
+                        routes:
+                        - match:
+                            path: /stats/prometheus
+                            headers:
+                            - name: ":method"
+                              string_match:
+                                exact: GET
+                          route:
+                            cluster: prometheus_stats
+                    http_filters:
+                    - name: envoy.filters.http.router
+                      typed_config:
+                        "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
+            clusters:
+            - name: prometheus_stats
+              connect_timeout: 0.250s
+              type: STATIC
+              lb_policy: ROUND_ROBIN
+              load_assignment:
+                cluster_name: prometheus_stats
+                endpoints:
+                - lb_endpoints:
+                  - endpoint:
+                      address:
+                        socket_address:
+                          address: 127.0.0.1
+                          port_value: 19000
+            - connect_timeout: 10s
+              lb_policy: ROUND_ROBIN
+              load_assignment:
+                cluster_name: local_cluster
+                endpoints:
+                - lb_endpoints:
+                  - endpoint:
+                      address:
+                        socket_address:
+                          address: 127.0.0.1
+                          port_value: 10080
+                    load_balancing_weight: 1
+                  load_balancing_weight: 1
+                  locality:
+                    zone: "$(ENVOY_SERVICE_ZONE)"
+              name: local_cluster
+              type: STATIC
+            - connect_timeout: 10s
+              load_assignment:
+                cluster_name: xds_cluster
+                endpoints:
+                - load_balancing_weight: 1
+                  lb_endpoints:
+                  - load_balancing_weight: 1
+                    endpoint:
+                      address:
+                        socket_address:
+                          address: envoy-gateway.envoy-gateway-system.svc.cluster.local
+                          port_value: 18000
+              typed_extension_protocol_options:
+                envoy.extensions.upstreams.http.v3.HttpProtocolOptions:
+                  "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions"
+                  explicit_http_config:
+                    http2_protocol_options:
+                      connection_keepalive:
+                        interval: 30s
+                        timeout: 5s
+                  http_filters:
+                  - name: envoy.filters.http.credential_injector
+                    typed_config:
+                      "@type": type.googleapis.com/envoy.extensions.filters.http.credential_injector.v3.CredentialInjector
+                      credential:
+                        name: envoy.http.injected_credentials.generic
+                        typed_config:
+                          "@type": type.googleapis.com/envoy.extensions.http.injected_credentials.generic.v3.Generic
+                          credential:
+                            name: jwt-sa-bearer
+                      overwrite: true
+                  - name: envoy.extensions.filters.http.upstream_codec.v3.UpstreamCodec
+                    typed_config:
+                      "@type": type.googleapis.com/envoy.extensions.filters.http.upstream_codec.v3.UpstreamCodec
+              name: xds_cluster
+              type: STRICT_DNS
+              transport_socket:
+                name: envoy.transport_sockets.tls
+                typed_config:
+                  "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
+                  common_tls_context:
+                    tls_params:
+                      tls_maximum_protocol_version: TLSv1_3
+                    validation_context_sds_secret_config:
+                      name: xds_trusted_ca
+                      sds_config:
+                        path_config_source:
+                          path: /sds/xds-trusted-ca.json
+                        resource_api_version: V3
+            - name: wasm_cluster
+              type: STRICT_DNS
+              connect_timeout: 10s
+              load_assignment:
+                cluster_name: wasm_cluster
+                endpoints:
+                - load_balancing_weight: 1
+                  lb_endpoints:
+                  - load_balancing_weight: 1
+                    endpoint:
+                      address:
+                        socket_address:
+                          address: envoy-gateway
+                          port_value: 18002
+              typed_extension_protocol_options:
+                envoy.extensions.upstreams.http.v3.HttpProtocolOptions:
+                  "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions"
+                  explicit_http_config:
+                    http2_protocol_options: {}
+                  http_filters:
+                  - name: envoy.filters.http.credential_injector
+                    typed_config:
+                      "@type": type.googleapis.com/envoy.extensions.filters.http.credential_injector.v3.CredentialInjector
+                      credential:
+                        name: envoy.http.injected_credentials.generic
+                        typed_config:
+                          "@type": type.googleapis.com/envoy.extensions.http.injected_credentials.generic.v3.Generic
+                          credential:
+                            name: jwt-sa-bearer
+                      overwrite: true
+                  - name: envoy.extensions.filters.http.upstream_codec.v3.UpstreamCodec
+                    typed_config:
+                      "@type": type.googleapis.com/envoy.extensions.filters.http.upstream_codec.v3.UpstreamCodec
+              transport_socket:
+                name: envoy.transport_sockets.tls
+                typed_config:
+                  "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
+                  common_tls_context:
+                    tls_params:
+                      tls_maximum_protocol_version: TLSv1_3
+                    validation_context_sds_secret_config:
+                      name: xds_trusted_ca
+                      sds_config:
+                        path_config_source:
+                          path: /sds/xds-trusted-ca.json
+                        resource_api_version: V3
+            secrets:
+            - name: jwt-sa-bearer
+              generic_secret:
+                secret:
+                  filename: "/var/run/secrets/token/sa-token"
+          overload_manager:
+            refresh_interval: 0.25s
+            resource_monitors:
+            - name: "envoy.resource_monitors.global_downstream_max_connections"
+              typed_config:
+                "@type": type.googleapis.com/envoy.extensions.resource_monitors.downstream_connections.v3.DownstreamConnectionsConfig
+                max_active_downstream_connections: 50000
+        - --log-level warn
+        - --cpuset-threads
+        - --drain-strategy immediate
+        - --component-log-level misc:error
+        - --drain-time-s 60
+        command:
+        - envoy
+        env:
+        - name: ENVOY_GATEWAY_NAMESPACE
+          value: envoy-gateway-system
+        - name: ENVOY_SERVICE_ZONE
+          valueFrom:
+            fieldRef:
+              apiVersion: v1
+              fieldPath: metadata.annotations['topology.kubernetes.io/zone']
+        - name: ENVOY_POD_NAME
+          valueFrom:
+            fieldRef:
+              apiVersion: v1
+              fieldPath: metadata.name
+        image: docker.io/envoyproxy/envoy:distroless-dev
+        imagePullPolicy: IfNotPresent
+        lifecycle:
+          preStop:
+            httpGet:
+              path: /shutdown/ready
+              port: 19002
+              scheme: HTTP
+        livenessProbe:
+          failureThreshold: 3
+          httpGet:
+            path: /ready
+            port: 19003
+            scheme: HTTP
+          periodSeconds: 10
+          successThreshold: 1
+          timeoutSeconds: 1
+        name: envoy
+        ports:
+        - containerPort: 19001
+          name: metrics
+          protocol: TCP
+        - containerPort: 19003
+          name: readiness
+          protocol: TCP
+        readinessProbe:
+          failureThreshold: 1
+          httpGet:
+            path: /ready
+            port: 19003
+            scheme: HTTP
+          periodSeconds: 5
+          successThreshold: 1
+          timeoutSeconds: 1
+        resources:
+          requests:
+            cpu: 100m
+            memory: 512Mi
+        securityContext:
+          allowPrivilegeEscalation: false
+          capabilities:
+            drop:
+            - ALL
+          privileged: false
+          runAsGroup: 65532
+          runAsNonRoot: true
+          runAsUser: 65532
+          seccompProfile:
+            type: RuntimeDefault
+        startupProbe:
+          failureThreshold: 30
+          httpGet:
+            path: /ready
+            port: 19003
+            scheme: HTTP
+          periodSeconds: 10
+          successThreshold: 1
+          timeoutSeconds: 1
+        terminationMessagePath: /dev/termination-log
+        terminationMessagePolicy: File
+        volumeMounts:
+        - mountPath: /certs
+          name: certs
+          readOnly: true
+        - mountPath: /sds
+          name: sds
+        - mountPath: /var/run/secrets/token
+          name: sa-token
+          readOnly: true
+      - args:
+        - envoy
+        - shutdown-manager
+        command:
+        - envoy-gateway
+        env:
+        - name: ENVOY_GATEWAY_NAMESPACE
+          value: envoy-gateway-system
+        - name: ENVOY_SERVICE_ZONE
+          valueFrom:
+            fieldRef:
+              apiVersion: v1
+              fieldPath: metadata.annotations['topology.kubernetes.io/zone']
+        - name: ENVOY_POD_NAME
+          valueFrom:
+            fieldRef:
+              apiVersion: v1
+              fieldPath: metadata.name
+        image: docker.io/envoyproxy/gateway-dev:latest
+        imagePullPolicy: IfNotPresent
+        lifecycle:
+          preStop:
+            exec:
+              command:
+              - envoy-gateway
+              - envoy
+              - shutdown
+        livenessProbe:
+          failureThreshold: 3
+          httpGet:
+            path: /healthz
+            port: 19002
+            scheme: HTTP
+          periodSeconds: 10
+          successThreshold: 1
+          timeoutSeconds: 1
+        name: shutdown-manager
+        readinessProbe:
+          failureThreshold: 3
+          httpGet:
+            path: /healthz
+            port: 19002
+            scheme: HTTP
+          periodSeconds: 10
+          successThreshold: 1
+          timeoutSeconds: 1
+        resources:
+          requests:
+            cpu: 10m
+            memory: 32Mi
+        securityContext:
+          allowPrivilegeEscalation: false
+          capabilities:
+            drop:
+            - ALL
+          privileged: false
+          runAsGroup: 65532
+          runAsNonRoot: true
+          runAsUser: 65532
+          seccompProfile:
+            type: RuntimeDefault
+        startupProbe:
+          failureThreshold: 30
+          httpGet:
+            path: /healthz
+            port: 19002
+            scheme: HTTP
+          periodSeconds: 10
+          successThreshold: 1
+          timeoutSeconds: 1
+        terminationMessagePath: /dev/termination-log
+        terminationMessagePolicy: File
+      dnsPolicy: ClusterFirst
+      restartPolicy: Always
+      schedulerName: default-scheduler
+      serviceAccountName: envoy-ns1-gateway-1-02ae0474
+      terminationGracePeriodSeconds: 360
+      volumes:
+      - name: sa-token
+        projected:
+          defaultMode: 420
+          sources:
+          - serviceAccountToken:
+              audience: envoy-gateway.envoy-gateway-system.svc.cluster.local
+              expirationSeconds: 3600
+              path: sa-token
+      - configMap:
+          defaultMode: 420
+          items:
+          - key: ca.crt
+            path: ca.crt
+          name: envoy-ns1-gateway-1-02ae0474
+          optional: false
+        name: certs
+      - configMap:
+          defaultMode: 420
+          items:
+          - key: xds-trusted-ca.json
+            path: xds-trusted-ca.json
+          name: envoy-ns1-gateway-1-02ae0474
+          optional: false
+        name: sds
+  updateStrategy:
+    type: RollingUpdate
+status:
+  currentNumberScheduled: 0
+  desiredNumberScheduled: 0
+  numberMisscheduled: 0
+  numberReady: 0
diff --git a/internal/infrastructure/kubernetes/proxy/testdata/deployments/gateway-namespace-mode.yaml b/internal/infrastructure/kubernetes/proxy/testdata/deployments/gateway-namespace-mode.yaml
new file mode 100644
index 000000000..edf6fd674
--- /dev/null
+++ b/internal/infrastructure/kubernetes/proxy/testdata/deployments/gateway-namespace-mode.yaml
@@ -0,0 +1,449 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  creationTimestamp: null
+  labels:
+    app.kubernetes.io/component: proxy
+    app.kubernetes.io/managed-by: envoy-gateway
+    app.kubernetes.io/name: envoy
+    gateway.envoyproxy.io/owning-gateway-name: gateway-1
+    gateway.envoyproxy.io/owning-gateway-namespace: ns1
+  name: envoy-ns1-gateway-1-02ae0474
+  namespace: ns1
+  ownerReferences:
+  - apiVersion: gateway.networking.k8s.io/v1
+    kind: Gateway
+    name: gateway-1
+    uid: test-owner-reference-uid-for-gateway
+spec:
+  progressDeadlineSeconds: 600
+  revisionHistoryLimit: 10
+  selector:
+    matchLabels:
+      app.kubernetes.io/component: proxy
+      app.kubernetes.io/managed-by: envoy-gateway
+      app.kubernetes.io/name: envoy
+      gateway.envoyproxy.io/owning-gateway-name: gateway-1
+      gateway.envoyproxy.io/owning-gateway-namespace: ns1
+  strategy:
+    type: RollingUpdate
+  template:
+    metadata:
+      annotations:
+        prometheus.io/path: /stats/prometheus
+        prometheus.io/port: "19001"
+        prometheus.io/scrape: "true"
+      creationTimestamp: null
+      labels:
+        app.kubernetes.io/component: proxy
+        app.kubernetes.io/managed-by: envoy-gateway
+        app.kubernetes.io/name: envoy
+        gateway.envoyproxy.io/owning-gateway-name: gateway-1
+        gateway.envoyproxy.io/owning-gateway-namespace: ns1
+    spec:
+      containers:
+      - args:
+        - --service-cluster ns1/gateway-1
+        - --service-node $(ENVOY_POD_NAME)
+        - |
+          --config-yaml admin:
+            access_log:
+            - name: envoy.access_loggers.file
+              typed_config:
+                "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog
+                path: /dev/null
+            address:
+              socket_address:
+                address: 127.0.0.1
+                port_value: 19000
+          cluster_manager:
+            local_cluster_name: local_cluster
+          node:
+            locality:
+              zone: "$(ENVOY_SERVICE_ZONE)"
+          layered_runtime:
+            layers:
+            - name: global_config
+              static_layer:
+                envoy.restart_features.use_eds_cache_for_ads: true
+                re2.max_program_size.error_level: 4294967295
+                re2.max_program_size.warn_level: 1000
+          dynamic_resources:
+            ads_config:
+              api_type: DELTA_GRPC
+              transport_api_version: V3
+              grpc_services:
+              - envoy_grpc:
+                  cluster_name: xds_cluster
+              set_node_on_first_message_only: true
+            lds_config:
+              ads: {}
+              resource_api_version: V3
+            cds_config:
+              ads: {}
+              resource_api_version: V3
+          static_resources:
+            listeners:
+            - name: envoy-gateway-proxy-stats-0.0.0.0-19001
+              address:
+                socket_address:
+                  address: '0.0.0.0'
+                  port_value: 19001
+                  protocol: TCP
+              bypass_overload_manager: true
+              filter_chains:
+              - filters:
+                - name: envoy.filters.network.http_connection_manager
+                  typed_config:
+                    "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
+                    stat_prefix: eg-stats-http
+                    normalize_path: true
+                    route_config:
+                      name: local_route
+                      virtual_hosts:
+                      - name: prometheus_stats
+                        domains:
+                        - "*"
+                        routes:
+                        - match:
+                            path: /stats/prometheus
+                            headers:
+                            - name: ":method"
+                              string_match:
+                                exact: GET
+                          route:
+                            cluster: prometheus_stats
+                    http_filters:
+                    - name: envoy.filters.http.router
+                      typed_config:
+                        "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
+            clusters:
+            - name: prometheus_stats
+              connect_timeout: 0.250s
+              type: STATIC
+              lb_policy: ROUND_ROBIN
+              load_assignment:
+                cluster_name: prometheus_stats
+                endpoints:
+                - lb_endpoints:
+                  - endpoint:
+                      address:
+                        socket_address:
+                          address: 127.0.0.1
+                          port_value: 19000
+            - connect_timeout: 10s
+              lb_policy: ROUND_ROBIN
+              load_assignment:
+                cluster_name: local_cluster
+                endpoints:
+                - lb_endpoints:
+                  - endpoint:
+                      address:
+                        socket_address:
+                          address: 127.0.0.1
+                          port_value: 10080
+                    load_balancing_weight: 1
+                  load_balancing_weight: 1
+                  locality:
+                    zone: "$(ENVOY_SERVICE_ZONE)"
+              name: local_cluster
+              type: STATIC
+            - connect_timeout: 10s
+              load_assignment:
+                cluster_name: xds_cluster
+                endpoints:
+                - load_balancing_weight: 1
+                  lb_endpoints:
+                  - load_balancing_weight: 1
+                    endpoint:
+                      address:
+                        socket_address:
+                          address: envoy-gateway.envoy-gateway-system.svc.cluster.local
+                          port_value: 18000
+              typed_extension_protocol_options:
+                envoy.extensions.upstreams.http.v3.HttpProtocolOptions:
+                  "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions"
+                  explicit_http_config:
+                    http2_protocol_options:
+                      connection_keepalive:
+                        interval: 30s
+                        timeout: 5s
+                  http_filters:
+                  - name: envoy.filters.http.credential_injector
+                    typed_config:
+                      "@type": type.googleapis.com/envoy.extensions.filters.http.credential_injector.v3.CredentialInjector
+                      credential:
+                        name: envoy.http.injected_credentials.generic
+                        typed_config:
+                          "@type": type.googleapis.com/envoy.extensions.http.injected_credentials.generic.v3.Generic
+                          credential:
+                            name: jwt-sa-bearer
+                      overwrite: true
+                  - name: envoy.extensions.filters.http.upstream_codec.v3.UpstreamCodec
+                    typed_config:
+                      "@type": type.googleapis.com/envoy.extensions.filters.http.upstream_codec.v3.UpstreamCodec
+              name: xds_cluster
+              type: STRICT_DNS
+              transport_socket:
+                name: envoy.transport_sockets.tls
+                typed_config:
+                  "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
+                  common_tls_context:
+                    tls_params:
+                      tls_maximum_protocol_version: TLSv1_3
+                    validation_context_sds_secret_config:
+                      name: xds_trusted_ca
+                      sds_config:
+                        path_config_source:
+                          path: /sds/xds-trusted-ca.json
+                        resource_api_version: V3
+            - name: wasm_cluster
+              type: STRICT_DNS
+              connect_timeout: 10s
+              load_assignment:
+                cluster_name: wasm_cluster
+                endpoints:
+                - load_balancing_weight: 1
+                  lb_endpoints:
+                  - load_balancing_weight: 1
+                    endpoint:
+                      address:
+                        socket_address:
+                          address: envoy-gateway
+                          port_value: 18002
+              typed_extension_protocol_options:
+                envoy.extensions.upstreams.http.v3.HttpProtocolOptions:
+                  "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions"
+                  explicit_http_config:
+                    http2_protocol_options: {}
+                  http_filters:
+                  - name: envoy.filters.http.credential_injector
+                    typed_config:
+                      "@type": type.googleapis.com/envoy.extensions.filters.http.credential_injector.v3.CredentialInjector
+                      credential:
+                        name: envoy.http.injected_credentials.generic
+                        typed_config:
+                          "@type": type.googleapis.com/envoy.extensions.http.injected_credentials.generic.v3.Generic
+                          credential:
+                            name: jwt-sa-bearer
+                      overwrite: true
+                  - name: envoy.extensions.filters.http.upstream_codec.v3.UpstreamCodec
+                    typed_config:
+                      "@type": type.googleapis.com/envoy.extensions.filters.http.upstream_codec.v3.UpstreamCodec
+              transport_socket:
+                name: envoy.transport_sockets.tls
+                typed_config:
+                  "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
+                  common_tls_context:
+                    tls_params:
+                      tls_maximum_protocol_version: TLSv1_3
+                    validation_context_sds_secret_config:
+                      name: xds_trusted_ca
+                      sds_config:
+                        path_config_source:
+                          path: /sds/xds-trusted-ca.json
+                        resource_api_version: V3
+            secrets:
+            - name: jwt-sa-bearer
+              generic_secret:
+                secret:
+                  filename: "/var/run/secrets/token/sa-token"
+          overload_manager:
+            refresh_interval: 0.25s
+            resource_monitors:
+            - name: "envoy.resource_monitors.global_downstream_max_connections"
+              typed_config:
+                "@type": type.googleapis.com/envoy.extensions.resource_monitors.downstream_connections.v3.DownstreamConnectionsConfig
+                max_active_downstream_connections: 50000
+        - --log-level warn
+        - --cpuset-threads
+        - --drain-strategy immediate
+        - --component-log-level misc:error
+        - --drain-time-s 60
+        command:
+        - envoy
+        env:
+        - name: ENVOY_GATEWAY_NAMESPACE
+          value: envoy-gateway-system
+        - name: ENVOY_SERVICE_ZONE
+          valueFrom:
+            fieldRef:
+              apiVersion: v1
+              fieldPath: metadata.annotations['topology.kubernetes.io/zone']
+        - name: ENVOY_POD_NAME
+          valueFrom:
+            fieldRef:
+              apiVersion: v1
+              fieldPath: metadata.name
+        image: docker.io/envoyproxy/envoy:distroless-dev
+        imagePullPolicy: IfNotPresent
+        lifecycle:
+          preStop:
+            httpGet:
+              path: /shutdown/ready
+              port: 19002
+              scheme: HTTP
+        livenessProbe:
+          failureThreshold: 3
+          httpGet:
+            path: /ready
+            port: 19003
+            scheme: HTTP
+          periodSeconds: 10
+          successThreshold: 1
+          timeoutSeconds: 1
+        name: envoy
+        ports:
+        - containerPort: 19001
+          name: metrics
+          protocol: TCP
+        - containerPort: 19003
+          name: readiness
+          protocol: TCP
+        readinessProbe:
+          failureThreshold: 1
+          httpGet:
+            path: /ready
+            port: 19003
+            scheme: HTTP
+          periodSeconds: 5
+          successThreshold: 1
+          timeoutSeconds: 1
+        resources:
+          requests:
+            cpu: 100m
+            memory: 512Mi
+        securityContext:
+          allowPrivilegeEscalation: false
+          capabilities:
+            drop:
+            - ALL
+          privileged: false
+          runAsGroup: 65532
+          runAsNonRoot: true
+          runAsUser: 65532
+          seccompProfile:
+            type: RuntimeDefault
+        startupProbe:
+          failureThreshold: 30
+          httpGet:
+            path: /ready
+            port: 19003
+            scheme: HTTP
+          periodSeconds: 10
+          successThreshold: 1
+          timeoutSeconds: 1
+        terminationMessagePath: /dev/termination-log
+        terminationMessagePolicy: File
+        volumeMounts:
+        - mountPath: /certs
+          name: certs
+          readOnly: true
+        - mountPath: /sds
+          name: sds
+        - mountPath: /var/run/secrets/token
+          name: sa-token
+          readOnly: true
+      - args:
+        - envoy
+        - shutdown-manager
+        command:
+        - envoy-gateway
+        env:
+        - name: ENVOY_GATEWAY_NAMESPACE
+          value: envoy-gateway-system
+        - name: ENVOY_SERVICE_ZONE
+          valueFrom:
+            fieldRef:
+              apiVersion: v1
+              fieldPath: metadata.annotations['topology.kubernetes.io/zone']
+        - name: ENVOY_POD_NAME
+          valueFrom:
+            fieldRef:
+              apiVersion: v1
+              fieldPath: metadata.name
+        image: docker.io/envoyproxy/gateway-dev:latest
+        imagePullPolicy: IfNotPresent
+        lifecycle:
+          preStop:
+            exec:
+              command:
+              - envoy-gateway
+              - envoy
+              - shutdown
+        livenessProbe:
+          failureThreshold: 3
+          httpGet:
+            path: /healthz
+            port: 19002
+            scheme: HTTP
+          periodSeconds: 10
+          successThreshold: 1
+          timeoutSeconds: 1
+        name: shutdown-manager
+        readinessProbe:
+          failureThreshold: 3
+          httpGet:
+            path: /healthz
+            port: 19002
+            scheme: HTTP
+          periodSeconds: 10
+          successThreshold: 1
+          timeoutSeconds: 1
+        resources:
+          requests:
+            cpu: 10m
+            memory: 32Mi
+        securityContext:
+          allowPrivilegeEscalation: false
+          capabilities:
+            drop:
+            - ALL
+          privileged: false
+          runAsGroup: 65532
+          runAsNonRoot: true
+          runAsUser: 65532
+          seccompProfile:
+            type: RuntimeDefault
+        startupProbe:
+          failureThreshold: 30
+          httpGet:
+            path: /healthz
+            port: 19002
+            scheme: HTTP
+          periodSeconds: 10
+          successThreshold: 1
+          timeoutSeconds: 1
+        terminationMessagePath: /dev/termination-log
+        terminationMessagePolicy: File
+      dnsPolicy: ClusterFirst
+      restartPolicy: Always
+      schedulerName: default-scheduler
+      serviceAccountName: envoy-ns1-gateway-1-02ae0474
+      terminationGracePeriodSeconds: 360
+      volumes:
+      - name: sa-token
+        projected:
+          defaultMode: 420
+          sources:
+          - serviceAccountToken:
+              audience: envoy-gateway.envoy-gateway-system.svc.cluster.local
+              expirationSeconds: 3600
+              path: sa-token
+      - configMap:
+          defaultMode: 420
+          items:
+          - key: ca.crt
+            path: ca.crt
+          name: envoy-ns1-gateway-1-02ae0474
+          optional: false
+        name: certs
+      - configMap:
+          defaultMode: 420
+          items:
+          - key: xds-trusted-ca.json
+            path: xds-trusted-ca.json
+          name: envoy-ns1-gateway-1-02ae0474
+          optional: false
+        name: sds
+status: {}
diff --git a/internal/infrastructure/kubernetes/proxy/testdata/gateway-namespace-mode/deployment.yaml b/internal/infrastructure/kubernetes/proxy/testdata/gateway-namespace-mode/deployment.yaml
index 06de348c3..a767126ce 100644
--- a/internal/infrastructure/kubernetes/proxy/testdata/gateway-namespace-mode/deployment.yaml
+++ b/internal/infrastructure/kubernetes/proxy/testdata/gateway-namespace-mode/deployment.yaml
@@ -10,6 +10,11 @@ metadata:
     gateway.envoyproxy.io/owning-gateway-namespace: namespace-1
   name: envoy-namespace-1-gateway-1-e2117e41
   namespace: namespace-1
+  ownerReferences:
+  - apiVersion: gateway.networking.k8s.io/v1
+    kind: Gateway
+    name: gateway-1
+    uid: test-owner-reference-uid-for-gateway
 spec:
   progressDeadlineSeconds: 600
   revisionHistoryLimit: 10
@@ -455,6 +460,11 @@ metadata:
     gateway.envoyproxy.io/owning-gateway-namespace: namespace-2
   name: envoy-namespace-2-gateway-2-107e8cb2
   namespace: namespace-2
+  ownerReferences:
+  - apiVersion: gateway.networking.k8s.io/v1
+    kind: Gateway
+    name: gateway-2
+    uid: test-owner-reference-uid-for-gateway
 spec:
   progressDeadlineSeconds: 600
   revisionHistoryLimit: 10
diff --git a/internal/infrastructure/kubernetes/proxy/testdata/gateway-namespace-mode/hpa.yaml b/internal/infrastructure/kubernetes/proxy/testdata/gateway-namespace-mode/hpa.yaml
index 0724e0c08..2c1be83e0 100644
--- a/internal/infrastructure/kubernetes/proxy/testdata/gateway-namespace-mode/hpa.yaml
+++ b/internal/infrastructure/kubernetes/proxy/testdata/gateway-namespace-mode/hpa.yaml
@@ -7,6 +7,11 @@ metadata:
     gateway.envoyproxy.io/owning-gateway-namespace: namespace-1
   name: envoy-namespace-1-gateway-1-e2117e41
   namespace: namespace-1
+  ownerReferences:
+  - apiVersion: gateway.networking.k8s.io/v1
+    kind: Gateway
+    name: gateway-1
+    uid: test-owner-reference-uid-for-gateway
 spec:
   maxReplicas: 3
   metrics:
@@ -34,6 +39,11 @@ metadata:
     gateway.envoyproxy.io/owning-gateway-namespace: namespace-2
   name: envoy-namespace-2-gateway-2-107e8cb2
   namespace: namespace-2
+  ownerReferences:
+  - apiVersion: gateway.networking.k8s.io/v1
+    kind: Gateway
+    name: gateway-2
+    uid: test-owner-reference-uid-for-gateway
 spec:
   maxReplicas: 3
   metrics:
diff --git a/internal/infrastructure/kubernetes/proxy/testdata/gateway-namespace-mode/service.yaml b/internal/infrastructure/kubernetes/proxy/testdata/gateway-namespace-mode/service.yaml
index 50e94c01d..db0d81507 100644
--- a/internal/infrastructure/kubernetes/proxy/testdata/gateway-namespace-mode/service.yaml
+++ b/internal/infrastructure/kubernetes/proxy/testdata/gateway-namespace-mode/service.yaml
@@ -10,8 +10,22 @@ metadata:
     gateway.envoyproxy.io/owning-gateway-namespace: namespace-1
   name: envoy-namespace-1-gateway-1-e2117e41
   namespace: namespace-1
+  ownerReferences:
+  - apiVersion: gateway.networking.k8s.io/v1
+    kind: Gateway
+    name: gateway-1
+    uid: test-owner-reference-uid-for-gateway
 spec:
   externalTrafficPolicy: Local
+  ports:
+  - name: EnvoyHTTPPort
+    port: 0
+    protocol: TCP
+    targetPort: 8080
+  - name: EnvoyHTTPSPort
+    port: 0
+    protocol: TCP
+    targetPort: 8443
   selector:
     app.kubernetes.io/component: proxy
     app.kubernetes.io/managed-by: envoy-gateway
@@ -35,8 +49,22 @@ metadata:
     gateway.envoyproxy.io/owning-gateway-namespace: namespace-2
   name: envoy-namespace-2-gateway-2-107e8cb2
   namespace: namespace-2
+  ownerReferences:
+  - apiVersion: gateway.networking.k8s.io/v1
+    kind: Gateway
+    name: gateway-2
+    uid: test-owner-reference-uid-for-gateway
 spec:
   externalTrafficPolicy: Local
+  ports:
+  - name: EnvoyHTTPPort
+    port: 0
+    protocol: TCP
+    targetPort: 8080
+  - name: EnvoyHTTPSPort
+    port: 0
+    protocol: TCP
+    targetPort: 8443
   selector:
     app.kubernetes.io/component: proxy
     app.kubernetes.io/managed-by: envoy-gateway
diff --git a/internal/infrastructure/kubernetes/proxy/testdata/gateway-namespace-mode/serviceaccount.yaml b/internal/infrastructure/kubernetes/proxy/testdata/gateway-namespace-mode/serviceaccount.yaml
index 8c8b81a6b..13d5c8e03 100644
--- a/internal/infrastructure/kubernetes/proxy/testdata/gateway-namespace-mode/serviceaccount.yaml
+++ b/internal/infrastructure/kubernetes/proxy/testdata/gateway-namespace-mode/serviceaccount.yaml
@@ -10,6 +10,11 @@ metadata:
     gateway.envoyproxy.io/owning-gateway-namespace: namespace-1
   name: envoy-namespace-1-gateway-1-e2117e41
   namespace: namespace-1
+  ownerReferences:
+  - apiVersion: gateway.networking.k8s.io/v1
+    kind: Gateway
+    name: gateway-1
+    uid: test-owner-reference-uid-for-gateway
 ---
 apiVersion: v1
 kind: ServiceAccount
@@ -23,3 +28,8 @@ metadata:
     gateway.envoyproxy.io/owning-gateway-namespace: namespace-2
   name: envoy-namespace-2-gateway-2-107e8cb2
   namespace: namespace-2
+  ownerReferences:
+  - apiVersion: gateway.networking.k8s.io/v1
+    kind: Gateway
+    name: gateway-2
+    uid: test-owner-reference-uid-for-gateway
diff --git a/internal/infrastructure/kubernetes/proxy/testdata/hpa/gateway-namespace-mode.yaml b/internal/infrastructure/kubernetes/proxy/testdata/hpa/gateway-namespace-mode.yaml
new file mode 100644
index 000000000..b449c891a
--- /dev/null
+++ b/internal/infrastructure/kubernetes/proxy/testdata/hpa/gateway-namespace-mode.yaml
@@ -0,0 +1,26 @@
+apiVersion: autoscaling/v2
+kind: HorizontalPodAutoscaler
+metadata:
+  labels:
+    gateway.envoyproxy.io/owning-gateway-name: gateway-1
+    gateway.envoyproxy.io/owning-gateway-namespace: ns1
+  ownerReferences:
+  - apiVersion: gateway.networking.k8s.io/v1
+    kind: Gateway
+    name: gateway-1
+    uid: test-owner-reference-uid-for-gateway
+  name: envoy-ns1-gateway-1-02ae0474
+  namespace: ns1
+spec:
+  metrics:
+  - resource:
+      name: cpu
+      target:
+        averageUtilization: 80
+        type: Utilization
+    type: Resource
+  maxReplicas: 1
+  scaleTargetRef:
+    apiVersion: apps/v1
+    kind: Deployment
+    name: envoy-ns1-gateway-1-02ae0474
diff --git a/internal/infrastructure/kubernetes/proxy/testdata/pdb/gateway-namespace-mode.yaml b/internal/infrastructure/kubernetes/proxy/testdata/pdb/gateway-namespace-mode.yaml
new file mode 100644
index 000000000..9689d80f7
--- /dev/null
+++ b/internal/infrastructure/kubernetes/proxy/testdata/pdb/gateway-namespace-mode.yaml
@@ -0,0 +1,19 @@
+apiVersion: policy/v1
+kind: PodDisruptionBudget
+metadata:
+  name: envoy-ns1-gateway-1-02ae0474
+  namespace: ns1
+  ownerReferences:
+  - apiVersion: gateway.networking.k8s.io/v1
+    kind: Gateway
+    name: gateway-1
+    uid: test-owner-reference-uid-for-gateway
+spec:
+  minAvailable: 1
+  selector:
+    matchLabels:
+      app.kubernetes.io/component: proxy
+      app.kubernetes.io/managed-by: envoy-gateway
+      app.kubernetes.io/name: envoy
+      gateway.envoyproxy.io/owning-gateway-name: gateway-1
+      gateway.envoyproxy.io/owning-gateway-namespace: ns1
diff --git a/internal/infrastructure/kubernetes/proxy/testdata/serviceaccount/gateway-namespace-mode.yaml b/internal/infrastructure/kubernetes/proxy/testdata/serviceaccount/gateway-namespace-mode.yaml
index 393ada177..484e1f706 100644
--- a/internal/infrastructure/kubernetes/proxy/testdata/serviceaccount/gateway-namespace-mode.yaml
+++ b/internal/infrastructure/kubernetes/proxy/testdata/serviceaccount/gateway-namespace-mode.yaml
@@ -6,7 +6,12 @@ metadata:
     app.kubernetes.io/component: proxy
     app.kubernetes.io/managed-by: envoy-gateway
     app.kubernetes.io/name: envoy
-    gateway.envoyproxy.io/owning-gateway-name: default
+    gateway.envoyproxy.io/owning-gateway-name: gateway-1
     gateway.envoyproxy.io/owning-gateway-namespace: ns1
-  name: envoy-default-37a8eec1
+  name: envoy-ns1-gateway-1-02ae0474
   namespace: ns1
+  ownerReferences:
+  - apiVersion: gateway.networking.k8s.io/v1
+    kind: Gateway
+    name: gateway-1
+    uid: test-owner-reference-uid-for-gateway
diff --git a/internal/infrastructure/kubernetes/proxy/testdata/services/gateway-namespace-mode.yaml b/internal/infrastructure/kubernetes/proxy/testdata/services/gateway-namespace-mode.yaml
new file mode 100644
index 000000000..e063df2a0
--- /dev/null
+++ b/internal/infrastructure/kubernetes/proxy/testdata/services/gateway-namespace-mode.yaml
@@ -0,0 +1,35 @@
+apiVersion: v1
+kind: Service
+metadata:
+  labels:
+    app.kubernetes.io/name: envoy
+    app.kubernetes.io/component: proxy
+    app.kubernetes.io/managed-by: envoy-gateway
+    gateway.envoyproxy.io/owning-gateway-name: gateway-1
+    gateway.envoyproxy.io/owning-gateway-namespace: ns1
+  ownerReferences:
+  - apiVersion: gateway.networking.k8s.io/v1
+    kind: Gateway
+    name: gateway-1
+    uid: test-owner-reference-uid-for-gateway
+  name: envoy-ns1-gateway-1-02ae0474
+  namespace: ns1
+spec:
+  externalTrafficPolicy: Local
+  ports:
+  - name: EnvoyHTTPPort
+    port: 0
+    protocol: TCP
+    targetPort: 8080
+  - name: EnvoyHTTPSPort
+    port: 0
+    protocol: TCP
+    targetPort: 8443
+  selector:
+    app.kubernetes.io/name: envoy
+    app.kubernetes.io/component: proxy
+    app.kubernetes.io/managed-by: envoy-gateway
+    gateway.envoyproxy.io/owning-gateway-name: gateway-1
+    gateway.envoyproxy.io/owning-gateway-namespace: ns1
+  sessionAffinity: None
+  type: LoadBalancer
diff --git a/internal/infrastructure/kubernetes/proxy_configmap_test.go b/internal/infrastructure/kubernetes/proxy_configmap_test.go
index ed7343dbe..660a3cfc6 100644
--- a/internal/infrastructure/kubernetes/proxy_configmap_test.go
+++ b/internal/infrastructure/kubernetes/proxy_configmap_test.go
@@ -10,14 +10,17 @@ import (
 	"os"
 	"testing"
 
+	"github.com/google/go-cmp/cmp"
+	"github.com/google/go-cmp/cmp/cmpopts"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 	corev1 "k8s.io/api/core/v1"
-	apiequality "k8s.io/apimachinery/pkg/api/equality"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/utils/ptr"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 	fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake"
 
+	egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1"
 	"github.com/envoyproxy/gateway/internal/envoygateway"
 	"github.com/envoyproxy/gateway/internal/envoygateway/config"
 	"github.com/envoyproxy/gateway/internal/gatewayapi"
@@ -27,24 +30,35 @@ import (
 )
 
 func TestCreateOrUpdateProxyConfigMap(t *testing.T) {
-	cfg, err := config.New(os.Stdout)
-	require.NoError(t, err)
-
-	infra := ir.NewInfra()
-	infra.Proxy.Name = "test"
-	infra.Proxy.GetProxyMetadata().Labels[gatewayapi.OwningGatewayNamespaceLabel] = "default"
-	infra.Proxy.GetProxyMetadata().Labels[gatewayapi.OwningGatewayNameLabel] = infra.Proxy.Name
-
 	testCases := []struct {
-		name    string
-		current *corev1.ConfigMap
-		expect  *corev1.ConfigMap
+		name                 string
+		ns                   string
+		in                   *ir.Infra
+		gatewayNamespaceMode bool
+		current              *corev1.ConfigMap
+		expect               *corev1.ConfigMap
 	}{
 		{
 			name: "create configmap",
+			ns:   "test",
+			in: &ir.Infra{
+				Proxy: &ir.ProxyInfra{
+					Name: "test",
+					Metadata: &ir.InfraMetadata{
+						Labels: map[string]string{
+							gatewayapi.OwningGatewayNamespaceLabel: "default",
+							gatewayapi.OwningGatewayNameLabel:      "test",
+						},
+					},
+				},
+			},
 			expect: &corev1.ConfigMap{
+				TypeMeta: metav1.TypeMeta{
+					Kind:       "ConfigMap",
+					APIVersion: "v1",
+				},
 				ObjectMeta: metav1.ObjectMeta{
-					Namespace: cfg.ControllerNamespace,
+					Namespace: "test",
 					Name:      "envoy-test-9f86d081",
 					Labels: map[string]string{
 						"app.kubernetes.io/name":               "envoy",
@@ -62,9 +76,21 @@ func TestCreateOrUpdateProxyConfigMap(t *testing.T) {
 		},
 		{
 			name: "update configmap",
+			ns:   "test",
+			in: &ir.Infra{
+				Proxy: &ir.ProxyInfra{
+					Name: "test",
+					Metadata: &ir.InfraMetadata{
+						Labels: map[string]string{
+							gatewayapi.OwningGatewayNamespaceLabel: "default",
+							gatewayapi.OwningGatewayNameLabel:      "test",
+						},
+					},
+				},
+			},
 			current: &corev1.ConfigMap{
 				ObjectMeta: metav1.ObjectMeta{
-					Namespace: cfg.ControllerNamespace,
+					Namespace: "test",
 					Name:      "envoy-test",
 					Labels: map[string]string{
 						"app.kubernetes.io/name":               "envoy",
@@ -77,8 +103,12 @@ func TestCreateOrUpdateProxyConfigMap(t *testing.T) {
 				Data: map[string]string{"foo": "bar"},
 			},
 			expect: &corev1.ConfigMap{
+				TypeMeta: metav1.TypeMeta{
+					Kind:       "ConfigMap",
+					APIVersion: "v1",
+				},
 				ObjectMeta: metav1.ObjectMeta{
-					Namespace: cfg.ControllerNamespace,
+					Namespace: "test",
 					Name:      "envoy-test-9f86d081",
 					Labels: map[string]string{
 						"app.kubernetes.io/name":               "envoy",
@@ -94,10 +124,65 @@ func TestCreateOrUpdateProxyConfigMap(t *testing.T) {
 				},
 			},
 		},
+		{
+			name: "create configmap with gateway namespace mode",
+			ns:   "test",
+			in: &ir.Infra{
+				Proxy: &ir.ProxyInfra{
+					Name:      "ns1/gateway-1",
+					Namespace: "ns1",
+					Metadata: &ir.InfraMetadata{
+						Labels: map[string]string{
+							gatewayapi.OwningGatewayNamespaceLabel: "ns1",
+							gatewayapi.OwningGatewayNameLabel:      "gateway-1",
+						},
+						OwnerReference: &ir.ResourceMetadata{
+							Kind: "Gateway",
+							Name: "gateway-1",
+						},
+					},
+				},
+			},
+			gatewayNamespaceMode: true,
+			expect: &corev1.ConfigMap{
+				TypeMeta: metav1.TypeMeta{
+					Kind:       "ConfigMap",
+					APIVersion: "v1",
+				},
+				ObjectMeta: metav1.ObjectMeta{
+					Namespace: "ns1",
+					Name:      "envoy-ns1-gateway-1-02ae0474",
+					Labels: map[string]string{
+						"app.kubernetes.io/name":               "envoy",
+						"app.kubernetes.io/component":          "proxy",
+						"app.kubernetes.io/managed-by":         "envoy-gateway",
+						gatewayapi.OwningGatewayNamespaceLabel: "ns1",
+						gatewayapi.OwningGatewayNameLabel:      "gateway-1",
+					},
+					OwnerReferences: []metav1.OwnerReference{
+						{
+							APIVersion: "gateway.networking.k8s.io/v1",
+							Kind:       "Gateway",
+							Name:       "gateway-1",
+							UID:        "foo.bar",
+						},
+					},
+				},
+				Data: map[string]string{
+					common.SdsCAFilename:   common.GetSdsCAConfigMapData(proxy.XdsTLSCaFilepath),
+					common.SdsCertFilename: common.GetSdsCertConfigMapData(proxy.XdsTLSCertFilepath, proxy.XdsTLSKeyFilepath),
+				},
+			},
+		},
 	}
 
 	for _, tc := range testCases {
 		t.Run(tc.name, func(t *testing.T) {
+			ctx := context.Background()
+			cfg, err := config.New(os.Stdout)
+			require.NoError(t, err)
+			cfg.ControllerNamespace = tc.ns
+
 			var cli client.Client
 			if tc.current != nil {
 				cli = fakeclient.NewClientBuilder().
@@ -112,8 +197,16 @@ func TestCreateOrUpdateProxyConfigMap(t *testing.T) {
 					Build()
 			}
 			kube := NewInfra(cli, cfg)
-			r := proxy.NewResourceRender(kube.ControllerNamespace, cfg.ControllerNamespace, kube.DNSDomain, infra.GetProxyInfra(), kube.EnvoyGateway)
-			err := kube.createOrUpdateConfigMap(context.Background(), r)
+			if tc.gatewayNamespaceMode {
+				kube.EnvoyGateway.Provider.Kubernetes.Deploy = &egv1a1.KubernetesDeployMode{
+					Type: ptr.To(egv1a1.KubernetesDeployModeType(egv1a1.KubernetesDeployModeTypeGatewayNamespace)),
+				}
+				require.NoError(t, createGatewayForGatewayNamespaceMode(ctx, kube.Client))
+			}
+
+			r, err := proxy.NewResourceRender(ctx, kube, tc.in)
+			require.NoError(t, err)
+			err = kube.createOrUpdateConfigMap(ctx, r)
 			require.NoError(t, err)
 			actual := &corev1.ConfigMap{
 				ObjectMeta: metav1.ObjectMeta{
@@ -121,9 +214,10 @@ func TestCreateOrUpdateProxyConfigMap(t *testing.T) {
 					Name:      tc.expect.Name,
 				},
 			}
-			require.NoError(t, kube.Client.Get(context.Background(), client.ObjectKeyFromObject(actual), actual))
-			require.Equal(t, tc.expect.Data, actual.Data)
-			assert.True(t, apiequality.Semantic.DeepEqual(tc.expect.Labels, actual.Labels))
+			require.NoError(t, kube.Client.Get(ctx, client.ObjectKeyFromObject(actual), actual))
+
+			opts := cmpopts.IgnoreFields(metav1.ObjectMeta{}, "ResourceVersion")
+			assert.True(t, cmp.Equal(tc.expect, actual, opts))
 		})
 	}
 }
@@ -164,20 +258,22 @@ func TestDeleteConfigProxyMap(t *testing.T) {
 
 	for _, tc := range testCases {
 		t.Run(tc.name, func(t *testing.T) {
+			ctx := context.Background()
 			cli := fakeclient.NewClientBuilder().WithScheme(envoygateway.GetScheme()).WithObjects(tc.current).Build()
 			kube := NewInfra(cli, cfg)
 
 			infra.Proxy.GetProxyMetadata().Labels[gatewayapi.OwningGatewayNamespaceLabel] = "default"
 			infra.Proxy.GetProxyMetadata().Labels[gatewayapi.OwningGatewayNameLabel] = infra.Proxy.Name
 
-			r := proxy.NewResourceRender(kube.ControllerNamespace, cfg.ControllerNamespace, kube.DNSDomain, infra.GetProxyInfra(), kube.EnvoyGateway)
+			r, err := proxy.NewResourceRender(ctx, kube, infra)
+			require.NoError(t, err)
 			cm := &corev1.ConfigMap{
 				ObjectMeta: metav1.ObjectMeta{
 					Namespace: kube.ControllerNamespace,
 					Name:      r.Name(),
 				},
 			}
-			err = kube.Client.Delete(context.Background(), cm)
+			err = kube.Client.Delete(ctx, cm)
 			require.NoError(t, err)
 		})
 	}
diff --git a/internal/infrastructure/kubernetes/proxy_daemonset_test.go b/internal/infrastructure/kubernetes/proxy_daemonset_test.go
index 0217d871f..1b060338b 100644
--- a/internal/infrastructure/kubernetes/proxy_daemonset_test.go
+++ b/internal/infrastructure/kubernetes/proxy_daemonset_test.go
@@ -47,13 +47,25 @@ func daemonsetWithSelectorAndLabel(ds *appsv1.DaemonSet, selector *metav1.LabelS
 	return dCopy
 }
 
-func TestCreateOrUpdateProxyDaemonSet(t *testing.T) {
-	cfg, err := config.New(os.Stdout)
-	require.NoError(t, err)
+func daemonsetWithOwnerReferences(ds *appsv1.DaemonSet, ownerReferences []metav1.OwnerReference) *appsv1.DaemonSet {
+	dCopy := ds.DeepCopy()
+	dCopy.OwnerReferences = ownerReferences
+	return dCopy
+}
 
+func setupCreateOrUpdateProxyDaemonSet(gatewayNamespaceMode bool) (*appsv1.DaemonSet, *ir.Infra, *config.Server, error) {
+	ctx := context.Background()
+	cfg, err := config.New(os.Stdout)
+	if err != nil {
+		return nil, nil, nil, err
+	}
 	infra := ir.NewInfra()
 	infra.Proxy.GetProxyMetadata().Labels[gatewayapi.OwningGatewayNamespaceLabel] = "default"
 	infra.Proxy.GetProxyMetadata().Labels[gatewayapi.OwningGatewayNameLabel] = infra.Proxy.Name
+	infra.Proxy.GetProxyMetadata().OwnerReference = &ir.ResourceMetadata{
+		Kind: "Gateway",
+		Name: infra.Proxy.Name,
+	}
 	infra.Proxy.Config = &egv1a1.EnvoyProxy{
 		Spec: egv1a1.EnvoyProxySpec{
 			Provider: &egv1a1.EnvoyProxyProvider{
@@ -67,30 +79,72 @@ func TestCreateOrUpdateProxyDaemonSet(t *testing.T) {
 		},
 	}
 
-	r := proxy.NewResourceRender(cfg.ControllerNamespace, cfg.ControllerNamespace, cfg.DNSDomain, infra.GetProxyInfra(), cfg.EnvoyGateway)
+	cli := fakeclient.NewClientBuilder().
+		WithScheme(envoygateway.GetScheme()).
+		Build()
+	kube := NewInfra(cli, cfg)
+
+	if gatewayNamespaceMode {
+		cfg.EnvoyGateway.Provider.Kubernetes.Deploy = &egv1a1.KubernetesDeployMode{
+			Type: ptr.To(egv1a1.KubernetesDeployModeType(egv1a1.KubernetesDeployModeTypeGatewayNamespace)),
+		}
+		infra.Proxy.Name = "ns1/gateway-1"
+		infra.Proxy.Namespace = "ns1"
+		infra.Proxy.GetProxyMetadata().Labels[gatewayapi.OwningGatewayNamespaceLabel] = "ns1"
+		infra.Proxy.GetProxyMetadata().Labels[gatewayapi.OwningGatewayNameLabel] = "gateway-1"
+		infra.Proxy.GetProxyMetadata().OwnerReference = &ir.ResourceMetadata{
+			Kind: "Gateway",
+			Name: "gateway-1",
+		}
+
+		if err := createGatewayForGatewayNamespaceMode(ctx, kube.Client); err != nil {
+			return nil, nil, nil, err
+		}
+	}
+
+	r, err := proxy.NewResourceRender(ctx, kube, infra)
+	if err != nil {
+		return nil, nil, nil, err
+	}
 	ds, err := r.DaemonSet()
+	if err != nil {
+		return nil, nil, nil, err
+	}
+	return ds, infra, cfg, nil
+}
+
+func TestCreateOrUpdateProxyDaemonSet(t *testing.T) {
+	ds, infra, cfg, err := setupCreateOrUpdateProxyDaemonSet(false)
+	require.NoError(t, err)
+
+	gwDs, gwInfra, gwCfg, err := setupCreateOrUpdateProxyDaemonSet(true)
 	require.NoError(t, err)
 
 	testCases := []struct {
-		name    string
-		in      *ir.Infra
-		current *appsv1.DaemonSet
-		want    *appsv1.DaemonSet
-		wantErr bool
+		name                 string
+		cfg                  *config.Server
+		in                   *ir.Infra
+		gatewayNamespaceMode bool
+		current              *appsv1.DaemonSet
+		want                 *appsv1.DaemonSet
+		wantErr              bool
 	}{
 		{
 			name: "create daemonset",
+			cfg:  cfg,
 			in:   infra,
 			want: ds,
 		},
 		{
 			name:    "daemonset exists",
+			cfg:     cfg,
 			in:      infra,
 			current: ds,
 			want:    ds,
 		},
 		{
 			name: "update daemonset image",
+			cfg:  cfg,
 			in: &ir.Infra{
 				Proxy: &ir.ProxyInfra{
 					Metadata: &ir.InfraMetadata{
@@ -122,6 +176,7 @@ func TestCreateOrUpdateProxyDaemonSet(t *testing.T) {
 		},
 		{
 			name: "update daemonset label",
+			cfg:  cfg,
 			in: &ir.Infra{
 				Proxy: &ir.ProxyInfra{
 					Metadata: &ir.InfraMetadata{
@@ -158,6 +213,7 @@ func TestCreateOrUpdateProxyDaemonSet(t *testing.T) {
 		},
 		{
 			name: "the daemonset originally has a selector and label, and an user add a new label to the custom label config",
+			cfg:  cfg,
 			in: &ir.Infra{
 				Proxy: &ir.ProxyInfra{
 					Metadata: &ir.InfraMetadata{
@@ -193,6 +249,7 @@ func TestCreateOrUpdateProxyDaemonSet(t *testing.T) {
 		},
 		{
 			name: "the daemonset originally has a selector and label, and an user update an existing custom label",
+			cfg:  cfg,
 			in: &ir.Infra{
 				Proxy: &ir.ProxyInfra{
 					Metadata: &ir.InfraMetadata{
@@ -227,10 +284,18 @@ func TestCreateOrUpdateProxyDaemonSet(t *testing.T) {
 			current: daemonsetWithSelectorAndLabel(ds, resource2.GetSelector(map[string]string{"custom-label": "version1"}), map[string]string{"custom-label": "version1"}),
 			wantErr: true,
 		},
+		{
+			name:                 "create daemonset with gateway namespace mode",
+			cfg:                  gwCfg,
+			in:                   gwInfra,
+			gatewayNamespaceMode: true,
+			want:                 daemonsetWithOwnerReferences(gwDs, []metav1.OwnerReference{{APIVersion: "gateway.networking.k8s.io/v1", Kind: "Gateway", Name: "gateway-1", UID: "foo.bar"}}),
+		},
 	}
 
 	for _, tc := range testCases {
 		t.Run(tc.name, func(t *testing.T) {
+			ctx := context.Background()
 			var cli client.Client
 			if tc.current != nil {
 				cli = fakeclient.NewClientBuilder().
@@ -245,9 +310,14 @@ func TestCreateOrUpdateProxyDaemonSet(t *testing.T) {
 					Build()
 			}
 
-			kube := NewInfra(cli, cfg)
-			r := proxy.NewResourceRender(kube.ControllerNamespace, cfg.ControllerNamespace, kube.DNSDomain, tc.in.GetProxyInfra(), cfg.EnvoyGateway)
-			err := kube.createOrUpdateDaemonSet(context.Background(), r)
+			kube := NewInfra(cli, tc.cfg)
+			if tc.gatewayNamespaceMode {
+				require.NoError(t, createGatewayForGatewayNamespaceMode(ctx, kube.Client))
+			}
+
+			r, err := proxy.NewResourceRender(ctx, kube, tc.in)
+			require.NoError(t, err)
+			err = kube.createOrUpdateDaemonSet(ctx, r)
 			if tc.wantErr {
 				require.Error(t, err)
 				return
@@ -256,12 +326,13 @@ func TestCreateOrUpdateProxyDaemonSet(t *testing.T) {
 
 			actual := &appsv1.DaemonSet{
 				ObjectMeta: metav1.ObjectMeta{
-					Namespace: kube.ControllerNamespace,
+					Namespace: kube.GetResourceNamespace(tc.in),
 					Name:      proxy.ExpectedResourceHashedName(tc.in.Proxy.Name),
 				},
 			}
-			require.NoError(t, kube.Client.Get(context.Background(), client.ObjectKeyFromObject(actual), actual))
+			require.NoError(t, kube.Client.Get(ctx, client.ObjectKeyFromObject(actual), actual))
 			require.Equal(t, tc.want.Spec, actual.Spec)
+			require.Equal(t, tc.want.OwnerReferences, actual.OwnerReferences)
 		})
 	}
 }
diff --git a/internal/infrastructure/kubernetes/proxy_deployment_test.go b/internal/infrastructure/kubernetes/proxy_deployment_test.go
index efe72d4c2..b4528cb44 100644
--- a/internal/infrastructure/kubernetes/proxy_deployment_test.go
+++ b/internal/infrastructure/kubernetes/proxy_deployment_test.go
@@ -52,38 +52,92 @@ func deploymentWithSelectorAndLabel(deploy *appsv1.Deployment, selector *metav1.
 	return dCopy
 }
 
-func TestCreateOrUpdateProxyDeployment(t *testing.T) {
-	cfg, err := config.New(os.Stdout)
-	require.NoError(t, err)
+func deploymentWithOwnerReferences(deploy *appsv1.Deployment, ownerReferences []metav1.OwnerReference) *appsv1.Deployment {
+	dCopy := deploy.DeepCopy()
+	dCopy.OwnerReferences = ownerReferences
+	return dCopy
+}
 
+func setupCreateOrUpdateProxyDeployment(gatewayNamespaceMode bool) (*appsv1.Deployment, *ir.Infra, *config.Server, error) {
+	ctx := context.Background()
+	cfg, err := config.New(os.Stdout)
+	if err != nil {
+		return nil, nil, nil, err
+	}
 	infra := ir.NewInfra()
 	infra.Proxy.GetProxyMetadata().Labels[gatewayapi.OwningGatewayNamespaceLabel] = "default"
 	infra.Proxy.GetProxyMetadata().Labels[gatewayapi.OwningGatewayNameLabel] = infra.Proxy.Name
+	infra.Proxy.GetProxyMetadata().OwnerReference = &ir.ResourceMetadata{
+		Kind: "Gateway",
+		Name: infra.Proxy.Name,
+	}
+
+	cli := fakeclient.NewClientBuilder().
+		WithScheme(envoygateway.GetScheme()).
+		Build()
+	kube := NewInfra(cli, cfg)
+
+	if gatewayNamespaceMode {
+		cfg.EnvoyGateway.Provider.Kubernetes.Deploy = &egv1a1.KubernetesDeployMode{
+			Type: ptr.To(egv1a1.KubernetesDeployModeType(egv1a1.KubernetesDeployModeTypeGatewayNamespace)),
+		}
+		infra.Proxy.Name = "ns1/gateway-1"
+		infra.Proxy.Namespace = "ns1"
+		infra.Proxy.GetProxyMetadata().Labels[gatewayapi.OwningGatewayNamespaceLabel] = "ns1"
+		infra.Proxy.GetProxyMetadata().Labels[gatewayapi.OwningGatewayNameLabel] = "gateway-1"
+		infra.Proxy.GetProxyMetadata().OwnerReference = &ir.ResourceMetadata{
+			Kind: "Gateway",
+			Name: "gateway-1",
+		}
+
+		if err := createGatewayForGatewayNamespaceMode(ctx, kube.Client); err != nil {
+			return nil, nil, nil, err
+		}
+	}
 
-	r := proxy.NewResourceRender(cfg.ControllerNamespace, cfg.ControllerNamespace, cfg.DNSDomain, infra.GetProxyInfra(), cfg.EnvoyGateway)
+	r, err := proxy.NewResourceRender(ctx, kube, infra)
+	if err != nil {
+		return nil, nil, nil, err
+	}
 	deploy, err := r.Deployment()
+	if err != nil {
+		return nil, nil, nil, err
+	}
+	return deploy, infra, cfg, nil
+}
+
+func TestCreateOrUpdateProxyDeployment(t *testing.T) {
+	deploy, infra, cfg, err := setupCreateOrUpdateProxyDeployment(false)
+	require.NoError(t, err)
+
+	gwDeploy, gwInfra, gwCfg, err := setupCreateOrUpdateProxyDeployment(true)
 	require.NoError(t, err)
 
 	testCases := []struct {
-		name    string
-		in      *ir.Infra
-		current *appsv1.Deployment
-		want    *appsv1.Deployment
-		wantErr bool
+		name                 string
+		cfg                  *config.Server
+		in                   *ir.Infra
+		gatewayNamespaceMode bool
+		current              *appsv1.Deployment
+		want                 *appsv1.Deployment
+		wantErr              bool
 	}{
 		{
 			name: "create deployment",
+			cfg:  cfg,
 			in:   infra,
 			want: deploy,
 		},
 		{
 			name:    "deployment exists",
+			cfg:     cfg,
 			in:      infra,
 			current: deploy,
 			want:    deploy,
 		},
 		{
 			name: "update deployment image",
+			cfg:  cfg,
 			in: &ir.Infra{
 				Proxy: &ir.ProxyInfra{
 					Metadata: &ir.InfraMetadata{
@@ -115,6 +169,7 @@ func TestCreateOrUpdateProxyDeployment(t *testing.T) {
 		},
 		{
 			name: "update deployment label",
+			cfg:  cfg,
 			in: &ir.Infra{
 				Proxy: &ir.ProxyInfra{
 					Metadata: &ir.InfraMetadata{
@@ -149,6 +204,7 @@ func TestCreateOrUpdateProxyDeployment(t *testing.T) {
 		},
 		{
 			name: "the daemonset originally has a selector and label, and an user add a new label to the custom label config",
+			cfg:  cfg,
 			in: &ir.Infra{
 				Proxy: &ir.ProxyInfra{
 					Metadata: &ir.InfraMetadata{
@@ -186,6 +242,7 @@ func TestCreateOrUpdateProxyDeployment(t *testing.T) {
 		},
 		{
 			name: "the deployment originally has a selector and label, and an user update an existing custom label",
+			cfg:  cfg,
 			in: &ir.Infra{
 				Proxy: &ir.ProxyInfra{
 					Metadata: &ir.InfraMetadata{
@@ -220,10 +277,18 @@ func TestCreateOrUpdateProxyDeployment(t *testing.T) {
 			current: deploymentWithSelectorAndLabel(deploy, resource2.GetSelector(map[string]string{"custom-label": "version1"}), map[string]string{"custom-label": "version1"}),
 			wantErr: true,
 		},
+		{
+			name:                 "create deployment with gateway namespace mode",
+			cfg:                  gwCfg,
+			in:                   gwInfra,
+			gatewayNamespaceMode: true,
+			want:                 deploymentWithOwnerReferences(gwDeploy, []metav1.OwnerReference{{APIVersion: "gateway.networking.k8s.io/v1", Kind: "Gateway", Name: "gateway-1", UID: "foo.bar"}}),
+		},
 	}
 
 	for _, tc := range testCases {
 		t.Run(tc.name, func(t *testing.T) {
+			ctx := context.Background()
 			var cli client.Client
 			if tc.current != nil {
 				cli = fakeclient.NewClientBuilder().
@@ -238,9 +303,14 @@ func TestCreateOrUpdateProxyDeployment(t *testing.T) {
 					Build()
 			}
 
-			kube := NewInfra(cli, cfg)
-			r := proxy.NewResourceRender(kube.ControllerNamespace, cfg.ControllerNamespace, kube.DNSDomain, tc.in.GetProxyInfra(), cfg.EnvoyGateway)
-			err := kube.createOrUpdateDeployment(context.Background(), r)
+			kube := NewInfra(cli, tc.cfg)
+			if tc.gatewayNamespaceMode {
+				require.NoError(t, createGatewayForGatewayNamespaceMode(ctx, kube.Client))
+			}
+
+			r, err := proxy.NewResourceRender(ctx, kube, tc.in)
+			require.NoError(t, err)
+			err = kube.createOrUpdateDeployment(ctx, r)
 			if tc.wantErr {
 				require.Error(t, err)
 				return
@@ -249,12 +319,13 @@ func TestCreateOrUpdateProxyDeployment(t *testing.T) {
 
 			actual := &appsv1.Deployment{
 				ObjectMeta: metav1.ObjectMeta{
-					Namespace: kube.ControllerNamespace,
+					Namespace: kube.GetResourceNamespace(tc.in),
 					Name:      proxy.ExpectedResourceHashedName(tc.in.Proxy.Name),
 				},
 			}
-			require.NoError(t, kube.Client.Get(context.Background(), client.ObjectKeyFromObject(actual), actual))
+			require.NoError(t, kube.Client.Get(ctx, client.ObjectKeyFromObject(actual), actual))
 			require.Equal(t, tc.want.Spec, actual.Spec)
+			require.Equal(t, tc.want.OwnerReferences, actual.OwnerReferences)
 		})
 	}
 }
@@ -280,14 +351,16 @@ func TestDeleteProxyDeployment(t *testing.T) {
 
 	for _, tc := range testCases {
 		t.Run(tc.name, func(t *testing.T) {
+			ctx := context.Background()
 			kube := NewInfra(cli, cfg)
 
 			infra := ir.NewInfra()
 			infra.Proxy.GetProxyMetadata().Labels[gatewayapi.OwningGatewayNamespaceLabel] = "default"
 			infra.Proxy.GetProxyMetadata().Labels[gatewayapi.OwningGatewayNameLabel] = infra.Proxy.Name
-			r := proxy.NewResourceRender(kube.ControllerNamespace, cfg.ControllerNamespace, kube.DNSDomain, infra.GetProxyInfra(), kube.EnvoyGateway)
+			r, err := proxy.NewResourceRender(ctx, kube, infra)
+			require.NoError(t, err)
 
-			err := kube.createOrUpdateDeployment(context.Background(), r)
+			err = kube.createOrUpdateDeployment(ctx, r)
 			require.NoError(t, err)
 			deployment := &appsv1.Deployment{
 				ObjectMeta: metav1.ObjectMeta{
@@ -295,7 +368,7 @@ func TestDeleteProxyDeployment(t *testing.T) {
 					Name:      r.Name(),
 				},
 			}
-			err = kube.Client.Delete(context.Background(), deployment)
+			err = kube.Client.Delete(ctx, deployment)
 			require.NoError(t, err)
 		})
 	}
diff --git a/internal/infrastructure/kubernetes/proxy_infra_test.go b/internal/infrastructure/kubernetes/proxy_infra_test.go
index 36eec1705..d2630a07f 100644
--- a/internal/infrastructure/kubernetes/proxy_infra_test.go
+++ b/internal/infrastructure/kubernetes/proxy_infra_test.go
@@ -223,3 +223,16 @@ func TestDeleteProxyInfra(t *testing.T) {
 		})
 	}
 }
+
+// This function uses setup for GatewayNamespace mode.
+// When enable GatewayNamespace mode, ProxyInfra Get OwnerReference from Gateway.
+func createGatewayForGatewayNamespaceMode(ctx context.Context, client *InfraClient) error {
+	gw := &gwapiv1.Gateway{
+		ObjectMeta: metav1.ObjectMeta{
+			Namespace: "ns1",
+			Name:      "gateway-1",
+			UID:       "foo.bar",
+		},
+	}
+	return client.Create(ctx, gw)
+}
diff --git a/internal/infrastructure/kubernetes/proxy_service_test.go b/internal/infrastructure/kubernetes/proxy_service_test.go
index 296115d4b..bc30ca7b0 100644
--- a/internal/infrastructure/kubernetes/proxy_service_test.go
+++ b/internal/infrastructure/kubernetes/proxy_service_test.go
@@ -27,16 +27,18 @@ func TestDeleteProxyService(t *testing.T) {
 
 	for _, tc := range testCases {
 		t.Run(tc.name, func(t *testing.T) {
+			ctx := context.Background()
 			kube := newTestInfra(t)
 			infra := ir.NewInfra()
 
 			infra.Proxy.GetProxyMetadata().Labels[gatewayapi.OwningGatewayNamespaceLabel] = "default"
 			infra.Proxy.GetProxyMetadata().Labels[gatewayapi.OwningGatewayNameLabel] = infra.Proxy.Name
-			r := proxy.NewResourceRender(kube.ControllerNamespace, kube.ControllerNamespace, kube.DNSDomain, infra.GetProxyInfra(), kube.EnvoyGateway)
-			err := kube.createOrUpdateService(context.Background(), r)
+			r, err := proxy.NewResourceRender(ctx, kube, infra)
+			require.NoError(t, err)
+			err = kube.createOrUpdateService(ctx, r)
 			require.NoError(t, err)
 
-			err = kube.deleteService(context.Background(), r)
+			err = kube.deleteService(ctx, r)
 			require.NoError(t, err)
 		})
 	}
diff --git a/internal/infrastructure/kubernetes/proxy_serviceaccount_test.go b/internal/infrastructure/kubernetes/proxy_serviceaccount_test.go
index 06eb5ef6e..62c1b4963 100644
--- a/internal/infrastructure/kubernetes/proxy_serviceaccount_test.go
+++ b/internal/infrastructure/kubernetes/proxy_serviceaccount_test.go
@@ -16,9 +16,11 @@ import (
 	"github.com/stretchr/testify/require"
 	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/utils/ptr"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 	fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake"
 
+	egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1"
 	"github.com/envoyproxy/gateway/internal/envoygateway"
 	"github.com/envoyproxy/gateway/internal/envoygateway/config"
 	"github.com/envoyproxy/gateway/internal/gatewayapi"
@@ -28,11 +30,12 @@ import (
 
 func TestCreateOrUpdateProxyServiceAccount(t *testing.T) {
 	testCases := []struct {
-		name    string
-		ns      string
-		in      *ir.Infra
-		current *corev1.ServiceAccount
-		want    *corev1.ServiceAccount
+		name                 string
+		ns                   string
+		in                   *ir.Infra
+		gatewayNamespaceMode bool
+		current              *corev1.ServiceAccount
+		want                 *corev1.ServiceAccount
 	}{
 		{
 			name: "create-sa",
@@ -164,10 +167,57 @@ func TestCreateOrUpdateProxyServiceAccount(t *testing.T) {
 				},
 			},
 		},
+		{
+			name: "create-sa-with-gateway-namespace-mode",
+			ns:   "test",
+			in: &ir.Infra{
+				Proxy: &ir.ProxyInfra{
+					Name:      "ns1/gateway-1",
+					Namespace: "ns1",
+					Metadata: &ir.InfraMetadata{
+						Labels: map[string]string{
+							gatewayapi.OwningGatewayNamespaceLabel: "ns1",
+							gatewayapi.OwningGatewayNameLabel:      "gateway-1",
+						},
+						OwnerReference: &ir.ResourceMetadata{
+							Kind: "Gateway",
+							Name: "gateway-1",
+						},
+					},
+				},
+			},
+			gatewayNamespaceMode: true,
+			want: &corev1.ServiceAccount{
+				TypeMeta: metav1.TypeMeta{
+					Kind:       "ServiceAccount",
+					APIVersion: "v1",
+				},
+				ObjectMeta: metav1.ObjectMeta{
+					Namespace: "ns1",
+					Name:      "envoy-ns1-gateway-1-02ae0474",
+					Labels: map[string]string{
+						"app.kubernetes.io/name":               "envoy",
+						"app.kubernetes.io/component":          "proxy",
+						"app.kubernetes.io/managed-by":         "envoy-gateway",
+						gatewayapi.OwningGatewayNamespaceLabel: "ns1",
+						gatewayapi.OwningGatewayNameLabel:      "gateway-1",
+					},
+					OwnerReferences: []metav1.OwnerReference{
+						{
+							APIVersion: "gateway.networking.k8s.io/v1",
+							Kind:       "Gateway",
+							Name:       "gateway-1",
+							UID:        "foo.bar",
+						},
+					},
+				},
+			},
+		},
 	}
 
 	for _, tc := range testCases {
 		t.Run(tc.name, func(t *testing.T) {
+			ctx := context.Background()
 			cfg, err := config.New(os.Stdout)
 			require.NoError(t, err)
 			cfg.ControllerNamespace = tc.ns
@@ -187,18 +237,25 @@ func TestCreateOrUpdateProxyServiceAccount(t *testing.T) {
 			}
 
 			kube := NewInfra(cli, cfg)
+			if tc.gatewayNamespaceMode {
+				kube.EnvoyGateway.Provider.Kubernetes.Deploy = &egv1a1.KubernetesDeployMode{
+					Type: ptr.To(egv1a1.KubernetesDeployModeType(egv1a1.KubernetesDeployModeTypeGatewayNamespace)),
+				}
+				require.NoError(t, createGatewayForGatewayNamespaceMode(ctx, kube.Client))
+			}
 
-			r := proxy.NewResourceRender(kube.ControllerNamespace, cfg.ControllerNamespace, kube.DNSDomain, tc.in.GetProxyInfra(), cfg.EnvoyGateway)
-			err = kube.createOrUpdateServiceAccount(context.Background(), r)
+			r, err := proxy.NewResourceRender(ctx, kube, tc.in)
+			require.NoError(t, err)
+			err = kube.createOrUpdateServiceAccount(ctx, r)
 			require.NoError(t, err)
 
 			actual := &corev1.ServiceAccount{
 				ObjectMeta: metav1.ObjectMeta{
-					Namespace: kube.ControllerNamespace,
+					Namespace: kube.GetResourceNamespace(tc.in),
 					Name:      proxy.ExpectedResourceHashedName(tc.in.Proxy.Name),
 				},
 			}
-			require.NoError(t, kube.Client.Get(context.Background(), client.ObjectKeyFromObject(actual), actual))
+			require.NoError(t, kube.Client.Get(ctx, client.ObjectKeyFromObject(actual), actual))
 
 			opts := cmpopts.IgnoreFields(metav1.ObjectMeta{}, "ResourceVersion")
 			assert.True(t, cmp.Equal(tc.want, actual, opts))
@@ -216,17 +273,19 @@ func TestDeleteProxyServiceAccount(t *testing.T) {
 	}
 	for _, tc := range testCases {
 		t.Run(tc.name, func(t *testing.T) {
+			ctx := context.Background()
 			kube := newTestInfra(t)
 
 			infra := ir.NewInfra()
 			infra.Proxy.GetProxyMetadata().Labels[gatewayapi.OwningGatewayNamespaceLabel] = "default"
 			infra.Proxy.GetProxyMetadata().Labels[gatewayapi.OwningGatewayNameLabel] = infra.Proxy.Name
-			r := proxy.NewResourceRender(kube.ControllerNamespace, kube.ControllerNamespace, kube.DNSDomain, infra.GetProxyInfra(), kube.EnvoyGateway)
+			r, err := proxy.NewResourceRender(ctx, kube, infra)
+			require.NoError(t, err)
 
-			err := kube.createOrUpdateServiceAccount(context.Background(), r)
+			err = kube.createOrUpdateServiceAccount(ctx, r)
 			require.NoError(t, err)
 
-			err = kube.deleteServiceAccount(context.Background(), r)
+			err = kube.deleteServiceAccount(ctx, r)
 			require.NoError(t, err)
 		})
 	}