Repository: kumahq/kuma
Pull request #3500 (2021-12-13 17:19:07)

chore(ci): bump patch versions of minikube/k8s
### Summary

SUMMARY_GOES_HERE

### Full changelog

* [Implement ...]
* [Fix ...]

### Issues resolved

Fix #XXX

### Documentation

- [ ] Link to the website [documentation PR](https://github.com/kumahq/kuma-website/pull/XXX)

### Testing

- [ ] Unit tests
- [ ] E2E tests
- [ ] Manual testing on Universal
- [ ] Manual testing on Kubernetes

### Backwards compatibility

- [ ] Update [`UPGRADE.md`](UPGRADE.md) with any steps users will need to take
      when upgrading.
- [ ] Add `backport-to-stable` label if the code is backwards compatible. Otherwise, list breaking changes.



Patch:
diff --git a/.circleci/config.yml b/.circleci/config.yml
index b7f36514f..2b14ec3c5 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -159,9 +159,10 @@ commands:
         name: "Update runc"
         # See https://github.com/rancher/k3d/issues/807 (runc's default version on the instance fails k3d)
         command: |
-          sudo wget -O /usr/bin/runc https://github.com/opencontainers/runc/releases/download/v1.0.2/runc.amd64
-          sudo chown root:root /usr/bin/runc ; sudo chmod o+x /usr/bin/runc
-
+          if [[ `uname -s` == "Linux" ]]; then
+            sudo sh -c 'curl -s --fail --location https://github.com/opencontainers/runc/releases/download/v1.0.3/runc.amd64 > /usr/bin/runc'
+            sudo chown root:root /usr/bin/runc ; sudo chmod o+x /usr/bin/runc
+          fi
 
 executors:
   golang:
@@ -1172,7 +1173,7 @@ workflows:
           - images
           - check
         # custom parameters
-        kubernetes_version: v1.18.16
+        kubernetes_version: v1.18.20
         use_local_kuma_images: true
     - example_minikube:
         <<: *commit_workflow_filters
@@ -1181,7 +1182,7 @@ workflows:
           - images
           - check
         # custom parameters
-        kubernetes_version: v1.20.4
+        kubernetes_version: v1.20.13
         use_local_kuma_images: true
     - e2e:
         <<: *commit_workflow_filters
diff --git a/UPGRADE.md b/UPGRADE.md
index 8bd03ffbd..7dcacfa9f 100644
--- a/UPGRADE.md
+++ b/UPGRADE.md
@@ -8,6 +8,12 @@ does not have any particular instructions.
 
 ## Upcoming release
 
+### Any type
+
+The `kuma.metrics.dataplane.enabled` and `kuma.metrics.zone.enabled` configurations have been removed.
+
+Kuma always generate the corresponding metrics.
+
 ### Kubernetes
 
 Please migrate your `kuma.io/sidecar-injection` annotations to labels.
diff --git a/deployments/charts/kuma/crds/gateway-instances.yaml b/deployments/charts/kuma/crds/gateway-instances.yaml
new file mode 100644
index 000000000..e3c331a58
--- /dev/null
+++ b/deployments/charts/kuma/crds/gateway-instances.yaml
@@ -0,0 +1,243 @@
+
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+  annotations:
+    controller-gen.kubebuilder.io/version: v0.7.0
+  creationTimestamp: null
+  name: gatewayinstances.kuma.io
+spec:
+  group: kuma.io
+  names:
+    kind: GatewayInstance
+    listKind: GatewayInstanceList
+    plural: gatewayinstances
+    singular: gatewayinstance
+  scope: Namespaced
+  versions:
+  - name: v1alpha1
+    schema:
+      openAPIV3Schema:
+        description: GatewayInstance represents a managed instance of a dataplane
+          proxy for a Kuma Gateway.
+        properties:
+          apiVersion:
+            description: 'APIVersion defines the versioned schema of this representation
+              of an object. Servers should convert recognized schemas to the latest
+              internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
+            type: string
+          kind:
+            description: 'Kind is a string value representing the REST resource this
+              object represents. Servers may infer this from the endpoint the client
+              submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
+            type: string
+          metadata:
+            type: object
+          spec:
+            description: GatewayInstanceSpec specifies the options available for a
+              GatewayDataplane.
+            properties:
+              replicas:
+                default: 1
+                description: Replicas is the number of dataplane proxy replicas to
+                  create. For now this is a fixed number, but in the future it could
+                  be automatically scaled based on metrics.
+                format: int32
+                minimum: 1
+                type: integer
+              resources:
+                description: Resources specifies the compute resources for the proxy
+                  container. The default can be set in the control plane config.
+                properties:
+                  limits:
+                    additionalProperties:
+                      anyOf:
+                      - type: integer
+                      - type: string
+                      pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+                      x-kubernetes-int-or-string: true
+                    description: 'Limits describes the maximum amount of compute resources
+                      allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/'
+                    type: object
+                  requests:
+                    additionalProperties:
+                      anyOf:
+                      - type: integer
+                      - type: string
+                      pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+                      x-kubernetes-int-or-string: true
+                    description: 'Requests describes the minimum amount of compute
+                      resources required. If Requests is omitted for a container,
+                      it defaults to Limits if that is explicitly specified, otherwise
+                      to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/'
+                    type: object
+                type: object
+              serviceType:
+                default: LoadBalancer
+                description: ServiceType specifies the type of managed Service that
+                  will be created to expose the dataplane proxies to traffic from
+                  outside the cluster. The ports to expose will be taken from the
+                  matching Gateway resource. If there is no matching Gateway, the
+                  managed Service will be deleted.
+                enum:
+                - LoadBalancer
+                - ClusterIP
+                - NodePort
+                type: string
+              tags:
+                additionalProperties:
+                  type: string
+                description: Tags specifies the Kuma tags that are propagated to the
+                  managed dataplane proxies. These tags should include exactly one
+                  `kuma.io/service` tag, and should match exactly one Gateway resource.
+                type: object
+            type: object
+          status:
+            description: GatewayInstanceStatus holds information about the status
+              of the gateway instance.
+            properties:
+              conditions:
+                description: Conditions is an array of gateway instance conditions.
+                items:
+                  description: "Condition contains details for one aspect of the current
+                    state of this API Resource. --- This struct is intended for direct
+                    use as an array at the field path .status.conditions.  For example,
+                    type FooStatus struct{     // Represents the observations of a
+                    foo's current state.     // Known .status.conditions.type are:
+                    \"Available\", \"Progressing\", and \"Degraded\"     // +patchMergeKey=type
+                    \    // +patchStrategy=merge     // +listType=map     // +listMapKey=type
+                    \    Conditions []metav1.Condition `json:\"conditions,omitempty\"
+                    patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`
+                    \n     // other fields }"
+                  properties:
+                    lastTransitionTime:
+                      description: lastTransitionTime is the last time the condition
+                        transitioned from one status to another. This should be when
+                        the underlying condition changed.  If that is not known, then
+                        using the time when the API field changed is acceptable.
+                      format: date-time
+                      type: string
+                    message:
+                      description: message is a human readable message indicating
+                        details about the transition. This may be an empty string.
+                      maxLength: 32768
+                      type: string
+                    observedGeneration:
+                      description: observedGeneration represents the .metadata.generation
+                        that the condition was set based upon. For instance, if .metadata.generation
+                        is currently 12, but the .status.conditions[x].observedGeneration
+                        is 9, the condition is out of date with respect to the current
+                        state of the instance.
+                      format: int64
+                      minimum: 0
+                      type: integer
+                    reason:
+                      description: reason contains a programmatic identifier indicating
+                        the reason for the condition's last transition. Producers
+                        of specific condition types may define expected values and
+                        meanings for this field, and whether the values are considered
+                        a guaranteed API. The value should be a CamelCase string.
+                        This field may not be empty.
+                      maxLength: 1024
+                      minLength: 1
+                      pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
+                      type: string
+                    status:
+                      description: status of the condition, one of True, False, Unknown.
+                      enum:
+                      - "True"
+                      - "False"
+                      - Unknown
+                      type: string
+                    type:
+                      description: type of condition in CamelCase or in foo.example.com/CamelCase.
+                        --- Many .condition.type values are consistent across resources
+                        like Available, but because arbitrary conditions can be useful
+                        (see .node.status.conditions), the ability to deconflict is
+                        important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt)
+                      maxLength: 316
+                      pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
+                      type: string
+                  required:
+                  - lastTransitionTime
+                  - message
+                  - reason
+                  - status
+                  - type
+                  type: object
+                type: array
+                x-kubernetes-list-map-keys:
+                - type
+                x-kubernetes-list-type: map
+              loadBalancer:
+                description: LoadBalancer contains the current status of the load-balancer,
+                  if one is present.
+                properties:
+                  ingress:
+                    description: Ingress is a list containing ingress points for the
+                      load-balancer. Traffic intended for the service should be sent
+                      to these ingress points.
+                    items:
+                      description: 'LoadBalancerIngress represents the status of a
+                        load-balancer ingress point: traffic intended for the service
+                        should be sent to an ingress point.'
+                      properties:
+                        hostname:
+                          description: Hostname is set for load-balancer ingress points
+                            that are DNS based (typically AWS load-balancers)
+                          type: string
+                        ip:
+                          description: IP is set for load-balancer ingress points
+                            that are IP based (typically GCE or OpenStack load-balancers)
+                          type: string
+                        ports:
+                          description: Ports is a list of records of service ports
+                            If used, every port defined in the service should have
+                            an entry in it
+                          items:
+                            properties:
+                              error:
+                                description: 'Error is to record the problem with
+                                  the service port The format of the error shall comply
+                                  with the following rules: - built-in error values
+                                  shall be specified in this file and those shall
+                                  use   CamelCase names - cloud provider specific
+                                  error values must have names that comply with the   format
+                                  foo.example.com/CamelCase. --- The regex it matches
+                                  is (dns1123SubdomainFmt/)?(qualifiedNameFmt)'
+                                maxLength: 316
+                                pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
+                                type: string
+                              port:
+                                description: Port is the port number of the service
+                                  port of which status is recorded here
+                                format: int32
+                                type: integer
+                              protocol:
+                                default: TCP
+                                description: 'Protocol is the protocol of the service
+                                  port of which status is recorded here The supported
+                                  values are: "TCP", "UDP", "SCTP"'
+                                type: string
+                            required:
+                            - port
+                            - protocol
+                            type: object
+                          type: array
+                          x-kubernetes-list-type: atomic
+                      type: object
+                    type: array
+                type: object
+            type: object
+        type: object
+    served: true
+    storage: true
+    subresources:
+      status: {}
+status:
+  acceptedNames:
+    kind: ""
+    plural: ""
+  conditions: []
+  storedVersions: []
diff --git a/pkg/config/app/kuma-cp/config.go b/pkg/config/app/kuma-cp/config.go
index 7396e7449..930f88099 100644
--- a/pkg/config/app/kuma-cp/config.go
+++ b/pkg/config/app/kuma-cp/config.go
@@ -53,7 +53,6 @@ func (m *Metrics) Validate() error {
 }
 
 type DataplaneMetrics struct {
-	Enabled           bool          `yaml:"enabled" envconfig:"kuma_metrics_dataplane_enabled"`
 	SubscriptionLimit int           `yaml:"subscriptionLimit" envconfig:"kuma_metrics_dataplane_subscription_limit"`
 	IdleTimeout       time.Duration `yaml:"idleTimeout" envconfig:"kuma_metrics_dataplane_idle_timeout"`
 }
@@ -69,7 +68,6 @@ func (d *DataplaneMetrics) Validate() error {
 }
 
 type ZoneMetrics struct {
-	Enabled           bool          `yaml:"enabled" envconfig:"kuma_metrics_zone_enabled"`
 	SubscriptionLimit int           `yaml:"subscriptionLimit" envconfig:"kuma_metrics_zone_subscription_limit"`
 	IdleTimeout       time.Duration `yaml:"idleTimeout" envconfig:"kuma_metrics_zone_idle_timeout"`
 }
@@ -176,12 +174,10 @@ var DefaultConfig = func() Config {
 		},
 		Metrics: &Metrics{
 			Dataplane: &DataplaneMetrics{
-				Enabled:           true,
 				SubscriptionLimit: 2,
 				IdleTimeout:       5 * time.Minute,
 			},
 			Zone: &ZoneMetrics{
-				Enabled:           true,
 				SubscriptionLimit: 10,
 				IdleTimeout:       5 * time.Minute,
 			},
diff --git a/pkg/core/managers/apis/dataplaneinsight/dataplane_insight_manager.go b/pkg/core/managers/apis/dataplaneinsight/dataplane_insight_manager.go
index 304986ce4..552a16eeb 100644
--- a/pkg/core/managers/apis/dataplaneinsight/dataplane_insight_manager.go
+++ b/pkg/core/managers/apis/dataplaneinsight/dataplane_insight_manager.go
@@ -3,6 +3,7 @@ package dataplaneinsight
 import (
 	"context"
 
+	mesh_proto "github.com/kumahq/kuma/api/mesh/v1alpha1"
 	kuma_cp "github.com/kumahq/kuma/pkg/config/app/kuma-cp"
 	"github.com/kumahq/kuma/pkg/core"
 	core_mesh "github.com/kumahq/kuma/pkg/core/resources/apis/mesh"
@@ -46,11 +47,8 @@ func (m *dataplaneInsightManager) Update(ctx context.Context, resource core_mode
 }
 
 func (m *dataplaneInsightManager) limitSubscription(dpInsight *core_mesh.DataplaneInsightResource) {
-	if !m.config.Enabled {
-		dpInsight.Spec.Subscriptions = nil
-		return
-	}
 	if m.config.SubscriptionLimit == 0 {
+		dpInsight.Spec.Subscriptions = []*mesh_proto.DiscoverySubscription{}
 		return
 	}
 	if len(dpInsight.Spec.Subscriptions) <= m.config.SubscriptionLimit {
diff --git a/pkg/core/managers/apis/zoneingressinsight/zone_ingress_insight_manager.go b/pkg/core/managers/apis/zoneingressinsight/zone_ingress_insight_manager.go
index 2c6f603fc..6f349700d 100644
--- a/pkg/core/managers/apis/zoneingressinsight/zone_ingress_insight_manager.go
+++ b/pkg/core/managers/apis/zoneingressinsight/zone_ingress_insight_manager.go
@@ -3,6 +3,7 @@ package zoneingressinsight
 import (
 	"context"
 
+	mesh_proto "github.com/kumahq/kuma/api/mesh/v1alpha1"
 	kuma_cp "github.com/kumahq/kuma/pkg/config/app/kuma-cp"
 	"github.com/kumahq/kuma/pkg/core"
 	"github.com/kumahq/kuma/pkg/core/resources/apis/mesh"
@@ -46,11 +47,8 @@ func (m *zoneIngressInsightManager) Update(ctx context.Context, resource core_mo
 }
 
 func (m *zoneIngressInsightManager) limitSubscription(zoneIngressInsight *mesh.ZoneIngressInsightResource) {
-	if !m.config.Enabled {
-		zoneIngressInsight.Spec.Subscriptions = nil
-		return
-	}
 	if m.config.SubscriptionLimit == 0 {
+		zoneIngressInsight.Spec.Subscriptions = []*mesh_proto.DiscoverySubscription{}
 		return
 	}
 	if len(zoneIngressInsight.Spec.Subscriptions) <= m.config.SubscriptionLimit {
diff --git a/pkg/core/managers/apis/zoneinsight/zone_insight_manager.go b/pkg/core/managers/apis/zoneinsight/zone_insight_manager.go
index fc8acb10f..53ff2a18b 100644
--- a/pkg/core/managers/apis/zoneinsight/zone_insight_manager.go
+++ b/pkg/core/managers/apis/zoneinsight/zone_insight_manager.go
@@ -3,6 +3,7 @@ package zoneinsight
 import (
 	"context"
 
+	system_proto "github.com/kumahq/kuma/api/system/v1alpha1"
 	kuma_cp "github.com/kumahq/kuma/pkg/config/app/kuma-cp"
 	"github.com/kumahq/kuma/pkg/core"
 	"github.com/kumahq/kuma/pkg/core/resources/apis/system"
@@ -46,11 +47,8 @@ func (m *zoneInsightManager) Update(ctx context.Context, resource core_model.Res
 }
 
 func (m *zoneInsightManager) limitSubscription(zoneInsight *system.ZoneInsightResource) {
-	if !m.config.Enabled {
-		zoneInsight.Spec.Subscriptions = nil
-		return
-	}
 	if m.config.SubscriptionLimit == 0 {
+		zoneInsight.Spec.Subscriptions = []*system_proto.KDSSubscription{}
 		return
 	}
 	if len(zoneInsight.Spec.Subscriptions) <= m.config.SubscriptionLimit {
diff --git a/pkg/plugins/resources/k8s/native/Makefile b/pkg/plugins/resources/k8s/native/Makefile
index b5f277324..443344605 100644
--- a/pkg/plugins/resources/k8s/native/Makefile
+++ b/pkg/plugins/resources/k8s/native/Makefile
@@ -1,9 +1,7 @@
 .PHONY: all test install crd/install crd/uninstall crd/upgrade deploy generate controller-gen
 
-# TODO(yskopets): CRD generation is broken since introduction of .proto definitions
-# Produce CRDs that work back to Kubernetes 1.11 (no version conversion)
-#CRD_OPTIONS ?= "crd:trivialVersions=true"
-#CRD_OUTPUT := output:crd:artifacts:config=config/crd/bases
+CRD_OPTIONS ?= "crd:crdVersions=v1"
+CRD_OUTPUT := output:crd:artifacts:config=config/crd/bases
 
 CI_TOOLS_DIR ?= $(HOME)/bin
 GOPATH_DIR := $(shell go env GOPATH | awk -F: '{print $$1}')
@@ -48,7 +46,7 @@ deploy: generate
 	kustomize build config/default | kubectl apply -f -
 
 generate:
-	$(CONTROLLER_GEN) $(CRD_OPTIONS) webhook paths="./api/..." $(CRD_OUTPUT)
+	$(CONTROLLER_GEN) $(CRD_OPTIONS) paths="./api/v1alpha1/gateway_instance.go;./api/v1alpha1/groupversion_info.go" $(CRD_OUTPUT)
 	$(CONTROLLER_GEN) object:headerFile=./hack/boilerplate.go.txt,year=$$(date +%Y) paths=./api/...
 
 help: ## Display this help screen
diff --git a/pkg/plugins/resources/k8s/native/api/v1alpha1/gateway.go b/pkg/plugins/resources/k8s/native/api/v1alpha1/gateway.go
index 93e5d147b..ba869b9f6 100644
--- a/pkg/plugins/resources/k8s/native/api/v1alpha1/gateway.go
+++ b/pkg/plugins/resources/k8s/native/api/v1alpha1/gateway.go
@@ -38,4 +38,6 @@ func init() {
 			Kind:       "GatewayRouteList",
 		},
 	})
+
+	SchemeBuilder.Register(&GatewayInstance{}, &GatewayInstanceList{})
 }
diff --git a/pkg/plugins/resources/k8s/native/api/v1alpha1/gateway_instance.go b/pkg/plugins/resources/k8s/native/api/v1alpha1/gateway_instance.go
new file mode 100644
index 000000000..678ddf385
--- /dev/null
+++ b/pkg/plugins/resources/k8s/native/api/v1alpha1/gateway_instance.go
@@ -0,0 +1,89 @@
+package v1alpha1
+
+import (
+	kube_core "k8s.io/api/core/v1"
+	kube_meta "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+// +kubebuilder:object:root=true
+// +kubebuilder:subresource:status
+
+// GatewayInstance represents a managed instance of a dataplane proxy for a Kuma
+// Gateway.
+type GatewayInstance struct {
+	kube_meta.TypeMeta   `json:",inline"`
+	kube_meta.ObjectMeta `json:"metadata,omitempty"`
+
+	Spec   GatewayInstanceSpec   `json:"spec,omitempty"`
+	Status GatewayInstanceStatus `json:"status,omitempty"`
+}
+
+// +k8s:deepcopy-gen=true
+
+// GatewayInstanceSpec specifies the options available for a GatewayDataplane.
+type GatewayInstanceSpec struct {
+	// Tags specifies the Kuma tags that are propagated to the managed
+	// dataplane proxies. These tags should include exactly one
+	// `kuma.io/service` tag, and should match exactly one Gateway
+	// resource.
+	//
+	// +required
+	// +kubebuilder:validation:MinLen=1
+	Tags map[string]string `json:"tags,omitempty"`
+
+	// Replicas is the number of dataplane proxy replicas to create. For
+	// now this is a fixed number, but in the future it could be
+	// automatically scaled based on metrics.
+	//
+	// +optional
+	// +kubebuilder:default=1
+	// +kubebuilder:validation:Minimum=1
+	Replicas int32 `json:"replicas,omitempty"`
+
+	// ServiceType specifies the type of managed Service that will be
+	// created to expose the dataplane proxies to traffic from outside
+	// the cluster. The ports to expose will be taken from the matching Gateway
+	// resource. If there is no matching Gateway, the managed Service will
+	// be deleted.
+	//
+	// +optional
+	// +kubebuilder:default=LoadBalancer
+	// +kubebuilder:validation:Enum=LoadBalancer;ClusterIP;NodePort
+	ServiceType kube_core.ServiceType `json:"serviceType,omitempty"`
+
+	// Resources specifies the compute resources for the proxy container.
+	// The default can be set in the control plane config.
+	//
+	// +optional
+	Resources *kube_core.ResourceRequirements `json:"resources,omitempty"`
+}
+
+// +k8s:deepcopy-gen=true
+
+// GatewayInstanceStatus holds information about the status of the gateway
+// instance.
+type GatewayInstanceStatus struct {
+	// LoadBalancer contains the current status of the load-balancer,
+	// if one is present.
+	//
+	// +optional
+	LoadBalancer *kube_core.LoadBalancerStatus `json:"loadBalancer,omitempty"`
+
+	// Conditions is an array of gateway instance conditions.
+	//
+	// +optional
+	// +patchMergeKey=type
+	// +patchStrategy=merge
+	// +listType=map
+	// +listMapKey=type
+	Conditions []kube_meta.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"`
+}
+
+// GatewayInstanceList contains a list of GatewayInstances.
+//
+// +kubebuilder:object:root=true
+type GatewayInstanceList struct {
+	kube_meta.TypeMeta `json:",inline"`
+	kube_meta.ListMeta `json:"metadata,omitempty"`
+	Items              []GatewayInstance `json:"items"`
+}
diff --git a/pkg/plugins/resources/k8s/native/api/v1alpha1/zz_generated.deepcopy.go b/pkg/plugins/resources/k8s/native/api/v1alpha1/zz_generated.deepcopy.go
index cd5222396..de3a9d032 100644
--- a/pkg/plugins/resources/k8s/native/api/v1alpha1/zz_generated.deepcopy.go
+++ b/pkg/plugins/resources/k8s/native/api/v1alpha1/zz_generated.deepcopy.go
@@ -22,6 +22,8 @@ limitations under the License.
 package v1alpha1
 
 import (
+	"k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/runtime"
 )
 
@@ -359,6 +361,119 @@ func (in *Gateway) DeepCopyObject() runtime.Object {
 	return nil
 }
 
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *GatewayInstance) DeepCopyInto(out *GatewayInstance) {
+	*out = *in
+	out.TypeMeta = in.TypeMeta
+	in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
+	in.Spec.DeepCopyInto(&out.Spec)
+	in.Status.DeepCopyInto(&out.Status)
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GatewayInstance.
+func (in *GatewayInstance) DeepCopy() *GatewayInstance {
+	if in == nil {
+		return nil
+	}
+	out := new(GatewayInstance)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *GatewayInstance) DeepCopyObject() runtime.Object {
+	if c := in.DeepCopy(); c != nil {
+		return c
+	}
+	return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *GatewayInstanceList) DeepCopyInto(out *GatewayInstanceList) {
+	*out = *in
+	out.TypeMeta = in.TypeMeta
+	in.ListMeta.DeepCopyInto(&out.ListMeta)
+	if in.Items != nil {
+		in, out := &in.Items, &out.Items
+		*out = make([]GatewayInstance, len(*in))
+		for i := range *in {
+			(*in)[i].DeepCopyInto(&(*out)[i])
+		}
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GatewayInstanceList.
+func (in *GatewayInstanceList) DeepCopy() *GatewayInstanceList {
+	if in == nil {
+		return nil
+	}
+	out := new(GatewayInstanceList)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *GatewayInstanceList) DeepCopyObject() runtime.Object {
+	if c := in.DeepCopy(); c != nil {
+		return c
+	}
+	return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *GatewayInstanceSpec) DeepCopyInto(out *GatewayInstanceSpec) {
+	*out = *in
+	if in.Tags != nil {
+		in, out := &in.Tags, &out.Tags
+		*out = make(map[string]string, len(*in))
+		for key, val := range *in {
+			(*out)[key] = val
+		}
+	}
+	if in.Resources != nil {
+		in, out := &in.Resources, &out.Resources
+		*out = new(v1.ResourceRequirements)
+		(*in).DeepCopyInto(*out)
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GatewayInstanceSpec.
+func (in *GatewayInstanceSpec) DeepCopy() *GatewayInstanceSpec {
+	if in == nil {
+		return nil
+	}
+	out := new(GatewayInstanceSpec)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *GatewayInstanceStatus) DeepCopyInto(out *GatewayInstanceStatus) {
+	*out = *in
+	if in.LoadBalancer != nil {
+		in, out := &in.LoadBalancer, &out.LoadBalancer
+		*out = new(v1.LoadBalancerStatus)
+		(*in).DeepCopyInto(*out)
+	}
+	if in.Conditions != nil {
+		in, out := &in.Conditions, &out.Conditions
+		*out = make([]metav1.Condition, len(*in))
+		for i := range *in {
+			(*in)[i].DeepCopyInto(&(*out)[i])
+		}
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GatewayInstanceStatus.
+func (in *GatewayInstanceStatus) DeepCopy() *GatewayInstanceStatus {
+	if in == nil {
+		return nil
+	}
+	out := new(GatewayInstanceStatus)
+	in.DeepCopyInto(out)
+	return out
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *GatewayList) DeepCopyInto(out *GatewayList) {
 	*out = *in
diff --git a/pkg/plugins/resources/k8s/native/config/crd/bases/kuma.io_gatewayinstances.yaml b/pkg/plugins/resources/k8s/native/config/crd/bases/kuma.io_gatewayinstances.yaml
new file mode 100644
index 000000000..e3c331a58
--- /dev/null
+++ b/pkg/plugins/resources/k8s/native/config/crd/bases/kuma.io_gatewayinstances.yaml
@@ -0,0 +1,243 @@
+
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+  annotations:
+    controller-gen.kubebuilder.io/version: v0.7.0
+  creationTimestamp: null
+  name: gatewayinstances.kuma.io
+spec:
+  group: kuma.io
+  names:
+    kind: GatewayInstance
+    listKind: GatewayInstanceList
+    plural: gatewayinstances
+    singular: gatewayinstance
+  scope: Namespaced
+  versions:
+  - name: v1alpha1
+    schema:
+      openAPIV3Schema:
+        description: GatewayInstance represents a managed instance of a dataplane
+          proxy for a Kuma Gateway.
+        properties:
+          apiVersion:
+            description: 'APIVersion defines the versioned schema of this representation
+              of an object. Servers should convert recognized schemas to the latest
+              internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
+            type: string
+          kind:
+            description: 'Kind is a string value representing the REST resource this
+              object represents. Servers may infer this from the endpoint the client
+              submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
+            type: string
+          metadata:
+            type: object
+          spec:
+            description: GatewayInstanceSpec specifies the options available for a
+              GatewayDataplane.
+            properties:
+              replicas:
+                default: 1
+                description: Replicas is the number of dataplane proxy replicas to
+                  create. For now this is a fixed number, but in the future it could
+                  be automatically scaled based on metrics.
+                format: int32
+                minimum: 1
+                type: integer
+              resources:
+                description: Resources specifies the compute resources for the proxy
+                  container. The default can be set in the control plane config.
+                properties:
+                  limits:
+                    additionalProperties:
+                      anyOf:
+                      - type: integer
+                      - type: string
+                      pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+                      x-kubernetes-int-or-string: true
+                    description: 'Limits describes the maximum amount of compute resources
+                      allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/'
+                    type: object
+                  requests:
+                    additionalProperties:
+                      anyOf:
+                      - type: integer
+                      - type: string
+                      pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+                      x-kubernetes-int-or-string: true
+                    description: 'Requests describes the minimum amount of compute
+                      resources required. If Requests is omitted for a container,
+                      it defaults to Limits if that is explicitly specified, otherwise
+                      to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/'
+                    type: object
+                type: object
+              serviceType:
+                default: LoadBalancer
+                description: ServiceType specifies the type of managed Service that
+                  will be created to expose the dataplane proxies to traffic from
+                  outside the cluster. The ports to expose will be taken from the
+                  matching Gateway resource. If there is no matching Gateway, the
+                  managed Service will be deleted.
+                enum:
+                - LoadBalancer
+                - ClusterIP
+                - NodePort
+                type: string
+              tags:
+                additionalProperties:
+                  type: string
+                description: Tags specifies the Kuma tags that are propagated to the
+                  managed dataplane proxies. These tags should include exactly one
+                  `kuma.io/service` tag, and should match exactly one Gateway resource.
+                type: object
+            type: object
+          status:
+            description: GatewayInstanceStatus holds information about the status
+              of the gateway instance.
+            properties:
+              conditions:
+                description: Conditions is an array of gateway instance conditions.
+                items:
+                  description: "Condition contains details for one aspect of the current
+                    state of this API Resource. --- This struct is intended for direct
+                    use as an array at the field path .status.conditions.  For example,
+                    type FooStatus struct{     // Represents the observations of a
+                    foo's current state.     // Known .status.conditions.type are:
+                    \"Available\", \"Progressing\", and \"Degraded\"     // +patchMergeKey=type
+                    \    // +patchStrategy=merge     // +listType=map     // +listMapKey=type
+                    \    Conditions []metav1.Condition `json:\"conditions,omitempty\"
+                    patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`
+                    \n     // other fields }"
+                  properties:
+                    lastTransitionTime:
+                      description: lastTransitionTime is the last time the condition
+                        transitioned from one status to another. This should be when
+                        the underlying condition changed.  If that is not known, then
+                        using the time when the API field changed is acceptable.
+                      format: date-time
+                      type: string
+                    message:
+                      description: message is a human readable message indicating
+                        details about the transition. This may be an empty string.
+                      maxLength: 32768
+                      type: string
+                    observedGeneration:
+                      description: observedGeneration represents the .metadata.generation
+                        that the condition was set based upon. For instance, if .metadata.generation
+                        is currently 12, but the .status.conditions[x].observedGeneration
+                        is 9, the condition is out of date with respect to the current
+                        state of the instance.
+                      format: int64
+                      minimum: 0
+                      type: integer
+                    reason:
+                      description: reason contains a programmatic identifier indicating
+                        the reason for the condition's last transition. Producers
+                        of specific condition types may define expected values and
+                        meanings for this field, and whether the values are considered
+                        a guaranteed API. The value should be a CamelCase string.
+                        This field may not be empty.
+                      maxLength: 1024
+                      minLength: 1
+                      pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
+                      type: string
+                    status:
+                      description: status of the condition, one of True, False, Unknown.
+                      enum:
+                      - "True"
+                      - "False"
+                      - Unknown
+                      type: string
+                    type:
+                      description: type of condition in CamelCase or in foo.example.com/CamelCase.
+                        --- Many .condition.type values are consistent across resources
+                        like Available, but because arbitrary conditions can be useful
+                        (see .node.status.conditions), the ability to deconflict is
+                        important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt)
+                      maxLength: 316
+                      pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
+                      type: string
+                  required:
+                  - lastTransitionTime
+                  - message
+                  - reason
+                  - status
+                  - type
+                  type: object
+                type: array
+                x-kubernetes-list-map-keys:
+                - type
+                x-kubernetes-list-type: map
+              loadBalancer:
+                description: LoadBalancer contains the current status of the load-balancer,
+                  if one is present.
+                properties:
+                  ingress:
+                    description: Ingress is a list containing ingress points for the
+                      load-balancer. Traffic intended for the service should be sent
+                      to these ingress points.
+                    items:
+                      description: 'LoadBalancerIngress represents the status of a
+                        load-balancer ingress point: traffic intended for the service
+                        should be sent to an ingress point.'
+                      properties:
+                        hostname:
+                          description: Hostname is set for load-balancer ingress points
+                            that are DNS based (typically AWS load-balancers)
+                          type: string
+                        ip:
+                          description: IP is set for load-balancer ingress points
+                            that are IP based (typically GCE or OpenStack load-balancers)
+                          type: string
+                        ports:
+                          description: Ports is a list of records of service ports
+                            If used, every port defined in the service should have
+                            an entry in it
+                          items:
+                            properties:
+                              error:
+                                description: 'Error is to record the problem with
+                                  the service port The format of the error shall comply
+                                  with the following rules: - built-in error values
+                                  shall be specified in this file and those shall
+                                  use   CamelCase names - cloud provider specific
+                                  error values must have names that comply with the   format
+                                  foo.example.com/CamelCase. --- The regex it matches
+                                  is (dns1123SubdomainFmt/)?(qualifiedNameFmt)'
+                                maxLength: 316
+                                pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
+                                type: string
+                              port:
+                                description: Port is the port number of the service
+                                  port of which status is recorded here
+                                format: int32
+                                type: integer
+                              protocol:
+                                default: TCP
+                                description: 'Protocol is the protocol of the service
+                                  port of which status is recorded here The supported
+                                  values are: "TCP", "UDP", "SCTP"'
+                                type: string
+                            required:
+                            - port
+                            - protocol
+                            type: object
+                          type: array
+                          x-kubernetes-list-type: atomic
+                      type: object
+                    type: array
+                type: object
+            type: object
+        type: object
+    served: true
+    storage: true
+    subresources:
+      status: {}
+status:
+  acceptedNames:
+    kind: ""
+    plural: ""
+  conditions: []
+  storedVersions: []
diff --git a/pkg/plugins/runtime/k8s/controllers/gatewayapi/gateway_controller.go b/pkg/plugins/runtime/k8s/controllers/gatewayapi/gateway_controller.go
index b575be3d0..8ad79d02b 100644
--- a/pkg/plugins/runtime/k8s/controllers/gatewayapi/gateway_controller.go
+++ b/pkg/plugins/runtime/k8s/controllers/gatewayapi/gateway_controller.go
@@ -31,8 +31,7 @@ const controllerName = "gateways.kuma.io/controller"
 // GatewayReconciler reconciles a GatewayAPI Gateway object.
 type GatewayReconciler struct {
 	kube_client.Client
-	Reader kube_client.Reader
-	Log    logr.Logger
+	Log logr.Logger
 
 	Scheme          *kube_runtime.Scheme
 	Converter       k8s_common.Converter
diff --git a/pkg/plugins/runtime/k8s/controllers/gatewayapi/http_route_controller.go b/pkg/plugins/runtime/k8s/controllers/gatewayapi/http_route_controller.go
index 5ddd81d3e..9c5ac9ab8 100644
--- a/pkg/plugins/runtime/k8s/controllers/gatewayapi/http_route_controller.go
+++ b/pkg/plugins/runtime/k8s/controllers/gatewayapi/http_route_controller.go
@@ -26,8 +26,7 @@ import (
 // HTTPRouteReconciler reconciles a GatewayAPI object into Kuma-native objects
 type HTTPRouteReconciler struct {
 	kube_client.Client
-	Reader kube_client.Reader
-	Log    logr.Logger
+	Log logr.Logger
 
 	Scheme          *kube_runtime.Scheme
 	Converter       k8s_common.Converter
diff --git a/pkg/plugins/runtime/k8s/controllers/mesh_controller.go b/pkg/plugins/runtime/k8s/controllers/mesh_controller.go
index 437867c48..01a178606 100644
--- a/pkg/plugins/runtime/k8s/controllers/mesh_controller.go
+++ b/pkg/plugins/runtime/k8s/controllers/mesh_controller.go
@@ -21,8 +21,7 @@ import (
 // MeshReconciler reconciles a Mesh object
 type MeshReconciler struct {
 	kube_client.Client
-	Reader kube_client.Reader
-	Log    logr.Logger
+	Log logr.Logger
 
 	Scheme          *kube_runtime.Scheme
 	Converter       k8s_common.Converter
diff --git a/pkg/plugins/runtime/k8s/plugin.go b/pkg/plugins/runtime/k8s/plugin.go
index c82771307..66f2b3f4f 100644
--- a/pkg/plugins/runtime/k8s/plugin.go
+++ b/pkg/plugins/runtime/k8s/plugin.go
@@ -125,7 +125,6 @@ func addMeshReconciler(mgr kube_ctrl.Manager, rt core_runtime.Runtime, converter
 	}
 	reconciler := &k8s_controllers.MeshReconciler{
 		Client:          mgr.GetClient(),
-		Reader:          mgr.GetAPIReader(),
 		Log:             core.Log.WithName("controllers").WithName("Mesh"),
 		Scheme:          mgr.GetScheme(),
 		Converter:       converter,
diff --git a/pkg/plugins/runtime/k8s/plugin_gateway.go b/pkg/plugins/runtime/k8s/plugin_gateway.go
index 0f0298a6b..00f467cb3 100644
--- a/pkg/plugins/runtime/k8s/plugin_gateway.go
+++ b/pkg/plugins/runtime/k8s/plugin_gateway.go
@@ -70,7 +70,6 @@ func addGatewayReconciler(mgr kube_ctrl.Manager, rt core_runtime.Runtime, conver
 
 	gatewayAPIGatewayReconciler := &gatewayapi_controllers.GatewayReconciler{
 		Client:          mgr.GetClient(),
-		Reader:          mgr.GetAPIReader(),
 		Log:             core.Log.WithName("controllers").WithName("gatewayapi").WithName("Gateway"),
 		Scheme:          mgr.GetScheme(),
 		Converter:       converter,
@@ -84,7 +83,6 @@ func addGatewayReconciler(mgr kube_ctrl.Manager, rt core_runtime.Runtime, conver
 
 	gatewayAPIHTTPRouteReconciler := &gatewayapi_controllers.HTTPRouteReconciler{
 		Client:          mgr.GetClient(),
-		Reader:          mgr.GetAPIReader(),
 		Log:             core.Log.WithName("controllers").WithName("gatewayapi").WithName("HTTPRoute"),
 		Scheme:          mgr.GetScheme(),
 		Converter:       converter,


Test patch:
diff --git a/app/kumactl/cmd/install/testdata/install-control-plane.cni-enabled.golden.yaml b/app/kumactl/cmd/install/testdata/install-control-plane.cni-enabled.golden.yaml
index 5debf3b23..cd6a1e186 100644
--- a/app/kumactl/cmd/install/testdata/install-control-plane.cni-enabled.golden.yaml
+++ b/app/kumactl/cmd/install/testdata/install-control-plane.cni-enabled.golden.yaml
@@ -120,6 +120,30 @@ spec:
 ---
 apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
+metadata:
+  creationTimestamp: null
+  name: meshes.kuma.io
+spec:
+  group: kuma.io
+  names:
+    kind: Mesh
+    plural: meshes
+  scope: Cluster
+  versions:
+    - name: v1alpha1
+      served: true
+      storage: true
+      schema:
+        openAPIV3Schema:
+          description: Mesh is the Schema for the meshes API
+          properties:
+            spec:
+              x-kubernetes-preserve-unknown-fields: true
+              type: object
+          type: object
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
 metadata:
   creationTimestamp: null
   name: proxytemplates.kuma.io
@@ -356,24 +380,24 @@ apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
 metadata:
   creationTimestamp: null
-  name: virtualoutbounds.kuma.io
+  name: dataplaneinsights.kuma.io
 spec:
   group: kuma.io
   names:
-    kind: VirtualOutbound
-    plural: virtualoutbounds
-  scope: Cluster
+    kind: DataplaneInsight
+    plural: dataplaneinsights
+  scope: Namespaced
   versions:
     - name: v1alpha1
       served: true
       storage: true
       schema:
         openAPIV3Schema:
-          description: VirtualOutbound is the Schema for the virtualoutbounds API
+          description: DataplaneInsight is the Schema for the dataplane insights API
           properties:
             mesh:
               type: string
-            spec:
+            status:
               x-kubernetes-preserve-unknown-fields: true
               type: object
           type: object
@@ -382,24 +406,24 @@ apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
 metadata:
   creationTimestamp: null
-  name: dataplaneinsights.kuma.io
+  name: virtualoutbounds.kuma.io
 spec:
   group: kuma.io
   names:
-    kind: DataplaneInsight
-    plural: dataplaneinsights
-  scope: Namespaced
+    kind: VirtualOutbound
+    plural: virtualoutbounds
+  scope: Cluster
   versions:
     - name: v1alpha1
       served: true
       storage: true
       schema:
         openAPIV3Schema:
-          description: DataplaneInsight is the Schema for the dataplane insights API
+          description: VirtualOutbound is the Schema for the virtualoutbounds API
           properties:
             mesh:
               type: string
-            status:
+            spec:
               x-kubernetes-preserve-unknown-fields: true
               type: object
           type: object
@@ -561,6 +585,248 @@ spec:
 ---
 apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
+metadata:
+  annotations:
+    controller-gen.kubebuilder.io/version: v0.7.0
+  creationTimestamp: null
+  name: gatewayinstances.kuma.io
+spec:
+  group: kuma.io
+  names:
+    kind: GatewayInstance
+    listKind: GatewayInstanceList
+    plural: gatewayinstances
+    singular: gatewayinstance
+  scope: Namespaced
+  versions:
+  - name: v1alpha1
+    schema:
+      openAPIV3Schema:
+        description: GatewayInstance represents a managed instance of a dataplane
+          proxy for a Kuma Gateway.
+        properties:
+          apiVersion:
+            description: 'APIVersion defines the versioned schema of this representation
+              of an object. Servers should convert recognized schemas to the latest
+              internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
+            type: string
+          kind:
+            description: 'Kind is a string value representing the REST resource this
+              object represents. Servers may infer this from the endpoint the client
+              submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
+            type: string
+          metadata:
+            type: object
+          spec:
+            description: GatewayInstanceSpec specifies the options available for a
+              GatewayDataplane.
+            properties:
+              replicas:
+                default: 1
+                description: Replicas is the number of dataplane proxy replicas to
+                  create. For now this is a fixed number, but in the future it could
+                  be automatically scaled based on metrics.
+                format: int32
+                minimum: 1
+                type: integer
+              resources:
+                description: Resources specifies the compute resources for the proxy
+                  container. The default can be set in the control plane config.
+                properties:
+                  limits:
+                    additionalProperties:
+                      anyOf:
+                      - type: integer
+                      - type: string
+                      pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+                      x-kubernetes-int-or-string: true
+                    description: 'Limits describes the maximum amount of compute resources
+                      allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/'
+                    type: object
+                  requests:
+                    additionalProperties:
+                      anyOf:
+                      - type: integer
+                      - type: string
+                      pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+                      x-kubernetes-int-or-string: true
+                    description: 'Requests describes the minimum amount of compute
+                      resources required. If Requests is omitted for a container,
+                      it defaults to Limits if that is explicitly specified, otherwise
+                      to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/'
+                    type: object
+                type: object
+              serviceType:
+                default: LoadBalancer
+                description: ServiceType specifies the type of managed Service that
+                  will be created to expose the dataplane proxies to traffic from
+                  outside the cluster. The ports to expose will be taken from the
+                  matching Gateway resource. If there is no matching Gateway, the
+                  managed Service will be deleted.
+                enum:
+                - LoadBalancer
+                - ClusterIP
+                - NodePort
+                type: string
+              tags:
+                additionalProperties:
+                  type: string
+                description: Tags specifies the Kuma tags that are propagated to the
+                  managed dataplane proxies. These tags should include exactly one
+                  `kuma.io/service` tag, and should match exactly one Gateway resource.
+                type: object
+            type: object
+          status:
+            description: GatewayInstanceStatus holds information about the status
+              of the gateway instance.
+            properties:
+              conditions:
+                description: Conditions is an array of gateway instance conditions.
+                items:
+                  description: "Condition contains details for one aspect of the current
+                    state of this API Resource. --- This struct is intended for direct
+                    use as an array at the field path .status.conditions.  For example,
+                    type FooStatus struct{     // Represents the observations of a
+                    foo's current state.     // Known .status.conditions.type are:
+                    \"Available\", \"Progressing\", and \"Degraded\"     // +patchMergeKey=type
+                    \    // +patchStrategy=merge     // +listType=map     // +listMapKey=type
+                    \    Conditions []metav1.Condition `json:\"conditions,omitempty\"
+                    patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`
+                    \n     // other fields }"
+                  properties:
+                    lastTransitionTime:
+                      description: lastTransitionTime is the last time the condition
+                        transitioned from one status to another. This should be when
+                        the underlying condition changed.  If that is not known, then
+                        using the time when the API field changed is acceptable.
+                      format: date-time
+                      type: string
+                    message:
+                      description: message is a human readable message indicating
+                        details about the transition. This may be an empty string.
+                      maxLength: 32768
+                      type: string
+                    observedGeneration:
+                      description: observedGeneration represents the .metadata.generation
+                        that the condition was set based upon. For instance, if .metadata.generation
+                        is currently 12, but the .status.conditions[x].observedGeneration
+                        is 9, the condition is out of date with respect to the current
+                        state of the instance.
+                      format: int64
+                      minimum: 0
+                      type: integer
+                    reason:
+                      description: reason contains a programmatic identifier indicating
+                        the reason for the condition's last transition. Producers
+                        of specific condition types may define expected values and
+                        meanings for this field, and whether the values are considered
+                        a guaranteed API. The value should be a CamelCase string.
+                        This field may not be empty.
+                      maxLength: 1024
+                      minLength: 1
+                      pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
+                      type: string
+                    status:
+                      description: status of the condition, one of True, False, Unknown.
+                      enum:
+                      - "True"
+                      - "False"
+                      - Unknown
+                      type: string
+                    type:
+                      description: type of condition in CamelCase or in foo.example.com/CamelCase.
+                        --- Many .condition.type values are consistent across resources
+                        like Available, but because arbitrary conditions can be useful
+                        (see .node.status.conditions), the ability to deconflict is
+                        important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt)
+                      maxLength: 316
+                      pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
+                      type: string
+                  required:
+                  - lastTransitionTime
+                  - message
+                  - reason
+                  - status
+                  - type
+                  type: object
+                type: array
+                x-kubernetes-list-map-keys:
+                - type
+                x-kubernetes-list-type: map
+              loadBalancer:
+                description: LoadBalancer contains the current status of the load-balancer,
+                  if one is present.
+                properties:
+                  ingress:
+                    description: Ingress is a list containing ingress points for the
+                      load-balancer. Traffic intended for the service should be sent
+                      to these ingress points.
+                    items:
+                      description: 'LoadBalancerIngress represents the status of a
+                        load-balancer ingress point: traffic intended for the service
+                        should be sent to an ingress point.'
+                      properties:
+                        hostname:
+                          description: Hostname is set for load-balancer ingress points
+                            that are DNS based (typically AWS load-balancers)
+                          type: string
+                        ip:
+                          description: IP is set for load-balancer ingress points
+                            that are IP based (typically GCE or OpenStack load-balancers)
+                          type: string
+                        ports:
+                          description: Ports is a list of records of service ports
+                            If used, every port defined in the service should have
+                            an entry in it
+                          items:
+                            properties:
+                              error:
+                                description: 'Error is to record the problem with
+                                  the service port The format of the error shall comply
+                                  with the following rules: - built-in error values
+                                  shall be specified in this file and those shall
+                                  use   CamelCase names - cloud provider specific
+                                  error values must have names that comply with the   format
+                                  foo.example.com/CamelCase. --- The regex it matches
+                                  is (dns1123SubdomainFmt/)?(qualifiedNameFmt)'
+                                maxLength: 316
+                                pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
+                                type: string
+                              port:
+                                description: Port is the port number of the service
+                                  port of which status is recorded here
+                                format: int32
+                                type: integer
+                              protocol:
+                                default: TCP
+                                description: 'Protocol is the protocol of the service
+                                  port of which status is recorded here The supported
+                                  values are: "TCP", "UDP", "SCTP"'
+                                type: string
+                            required:
+                            - port
+                            - protocol
+                            type: object
+                          type: array
+                          x-kubernetes-list-type: atomic
+                      type: object
+                    type: array
+                type: object
+            type: object
+        type: object
+    served: true
+    storage: true
+    subresources:
+      status: {}
+status:
+  acceptedNames:
+    kind: ""
+    plural: ""
+  conditions: []
+  storedVersions: []
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
 metadata:
   creationTimestamp: null
   name: gatewayroutes.kuma.io
@@ -663,30 +929,6 @@ spec:
               type: object
           type: object
 ---
-apiVersion: apiextensions.k8s.io/v1
-kind: CustomResourceDefinition
-metadata:
-  creationTimestamp: null
-  name: meshes.kuma.io
-spec:
-  group: kuma.io
-  names:
-    kind: Mesh
-    plural: meshes
-  scope: Cluster
-  versions:
-    - name: v1alpha1
-      served: true
-      storage: true
-      schema:
-        openAPIV3Schema:
-          description: Mesh is the Schema for the meshes API
-          properties:
-            spec:
-              x-kubernetes-preserve-unknown-fields: true
-              type: object
-          type: object
----
 apiVersion: rbac.authorization.k8s.io/v1
 kind: ClusterRole
 metadata:
diff --git a/app/kumactl/cmd/install/testdata/install-control-plane.defaults.golden.yaml b/app/kumactl/cmd/install/testdata/install-control-plane.defaults.golden.yaml
index e51756343..0ee9d1d65 100644
--- a/app/kumactl/cmd/install/testdata/install-control-plane.defaults.golden.yaml
+++ b/app/kumactl/cmd/install/testdata/install-control-plane.defaults.golden.yaml
@@ -84,6 +84,30 @@ spec:
 ---
 apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
+metadata:
+  creationTimestamp: null
+  name: meshes.kuma.io
+spec:
+  group: kuma.io
+  names:
+    kind: Mesh
+    plural: meshes
+  scope: Cluster
+  versions:
+    - name: v1alpha1
+      served: true
+      storage: true
+      schema:
+        openAPIV3Schema:
+          description: Mesh is the Schema for the meshes API
+          properties:
+            spec:
+              x-kubernetes-preserve-unknown-fields: true
+              type: object
+          type: object
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
 metadata:
   creationTimestamp: null
   name: proxytemplates.kuma.io
@@ -320,24 +344,24 @@ apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
 metadata:
   creationTimestamp: null
-  name: virtualoutbounds.kuma.io
+  name: dataplaneinsights.kuma.io
 spec:
   group: kuma.io
   names:
-    kind: VirtualOutbound
-    plural: virtualoutbounds
-  scope: Cluster
+    kind: DataplaneInsight
+    plural: dataplaneinsights
+  scope: Namespaced
   versions:
     - name: v1alpha1
       served: true
       storage: true
       schema:
         openAPIV3Schema:
-          description: VirtualOutbound is the Schema for the virtualoutbounds API
+          description: DataplaneInsight is the Schema for the dataplane insights API
           properties:
             mesh:
               type: string
-            spec:
+            status:
               x-kubernetes-preserve-unknown-fields: true
               type: object
           type: object
@@ -346,24 +370,24 @@ apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
 metadata:
   creationTimestamp: null
-  name: dataplaneinsights.kuma.io
+  name: virtualoutbounds.kuma.io
 spec:
   group: kuma.io
   names:
-    kind: DataplaneInsight
-    plural: dataplaneinsights
-  scope: Namespaced
+    kind: VirtualOutbound
+    plural: virtualoutbounds
+  scope: Cluster
   versions:
     - name: v1alpha1
       served: true
       storage: true
       schema:
         openAPIV3Schema:
-          description: DataplaneInsight is the Schema for the dataplane insights API
+          description: VirtualOutbound is the Schema for the virtualoutbounds API
           properties:
             mesh:
               type: string
-            status:
+            spec:
               x-kubernetes-preserve-unknown-fields: true
               type: object
           type: object
@@ -525,6 +549,248 @@ spec:
 ---
 apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
+metadata:
+  annotations:
+    controller-gen.kubebuilder.io/version: v0.7.0
+  creationTimestamp: null
+  name: gatewayinstances.kuma.io
+spec:
+  group: kuma.io
+  names:
+    kind: GatewayInstance
+    listKind: GatewayInstanceList
+    plural: gatewayinstances
+    singular: gatewayinstance
+  scope: Namespaced
+  versions:
+  - name: v1alpha1
+    schema:
+      openAPIV3Schema:
+        description: GatewayInstance represents a managed instance of a dataplane
+          proxy for a Kuma Gateway.
+        properties:
+          apiVersion:
+            description: 'APIVersion defines the versioned schema of this representation
+              of an object. Servers should convert recognized schemas to the latest
+              internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
+            type: string
+          kind:
+            description: 'Kind is a string value representing the REST resource this
+              object represents. Servers may infer this from the endpoint the client
+              submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
+            type: string
+          metadata:
+            type: object
+          spec:
+            description: GatewayInstanceSpec specifies the options available for a
+              GatewayDataplane.
+            properties:
+              replicas:
+                default: 1
+                description: Replicas is the number of dataplane proxy replicas to
+                  create. For now this is a fixed number, but in the future it could
+                  be automatically scaled based on metrics.
+                format: int32
+                minimum: 1
+                type: integer
+              resources:
+                description: Resources specifies the compute resources for the proxy
+                  container. The default can be set in the control plane config.
+                properties:
+                  limits:
+                    additionalProperties:
+                      anyOf:
+                      - type: integer
+                      - type: string
+                      pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+                      x-kubernetes-int-or-string: true
+                    description: 'Limits describes the maximum amount of compute resources
+                      allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/'
+                    type: object
+                  requests:
+                    additionalProperties:
+                      anyOf:
+                      - type: integer
+                      - type: string
+                      pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+                      x-kubernetes-int-or-string: true
+                    description: 'Requests describes the minimum amount of compute
+                      resources required. If Requests is omitted for a container,
+                      it defaults to Limits if that is explicitly specified, otherwise
+                      to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/'
+                    type: object
+                type: object
+              serviceType:
+                default: LoadBalancer
+                description: ServiceType specifies the type of managed Service that
+                  will be created to expose the dataplane proxies to traffic from
+                  outside the cluster. The ports to expose will be taken from the
+                  matching Gateway resource. If there is no matching Gateway, the
+                  managed Service will be deleted.
+                enum:
+                - LoadBalancer
+                - ClusterIP
+                - NodePort
+                type: string
+              tags:
+                additionalProperties:
+                  type: string
+                description: Tags specifies the Kuma tags that are propagated to the
+                  managed dataplane proxies. These tags should include exactly one
+                  `kuma.io/service` tag, and should match exactly one Gateway resource.
+                type: object
+            type: object
+          status:
+            description: GatewayInstanceStatus holds information about the status
+              of the gateway instance.
+            properties:
+              conditions:
+                description: Conditions is an array of gateway instance conditions.
+                items:
+                  description: "Condition contains details for one aspect of the current
+                    state of this API Resource. --- This struct is intended for direct
+                    use as an array at the field path .status.conditions.  For example,
+                    type FooStatus struct{     // Represents the observations of a
+                    foo's current state.     // Known .status.conditions.type are:
+                    \"Available\", \"Progressing\", and \"Degraded\"     // +patchMergeKey=type
+                    \    // +patchStrategy=merge     // +listType=map     // +listMapKey=type
+                    \    Conditions []metav1.Condition `json:\"conditions,omitempty\"
+                    patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`
+                    \n     // other fields }"
+                  properties:
+                    lastTransitionTime:
+                      description: lastTransitionTime is the last time the condition
+                        transitioned from one status to another. This should be when
+                        the underlying condition changed.  If that is not known, then
+                        using the time when the API field changed is acceptable.
+                      format: date-time
+                      type: string
+                    message:
+                      description: message is a human readable message indicating
+                        details about the transition. This may be an empty string.
+                      maxLength: 32768
+                      type: string
+                    observedGeneration:
+                      description: observedGeneration represents the .metadata.generation
+                        that the condition was set based upon. For instance, if .metadata.generation
+                        is currently 12, but the .status.conditions[x].observedGeneration
+                        is 9, the condition is out of date with respect to the current
+                        state of the instance.
+                      format: int64
+                      minimum: 0
+                      type: integer
+                    reason:
+                      description: reason contains a programmatic identifier indicating
+                        the reason for the condition's last transition. Producers
+                        of specific condition types may define expected values and
+                        meanings for this field, and whether the values are considered
+                        a guaranteed API. The value should be a CamelCase string.
+                        This field may not be empty.
+                      maxLength: 1024
+                      minLength: 1
+                      pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
+                      type: string
+                    status:
+                      description: status of the condition, one of True, False, Unknown.
+                      enum:
+                      - "True"
+                      - "False"
+                      - Unknown
+                      type: string
+                    type:
+                      description: type of condition in CamelCase or in foo.example.com/CamelCase.
+                        --- Many .condition.type values are consistent across resources
+                        like Available, but because arbitrary conditions can be useful
+                        (see .node.status.conditions), the ability to deconflict is
+                        important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt)
+                      maxLength: 316
+                      pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
+                      type: string
+                  required:
+                  - lastTransitionTime
+                  - message
+                  - reason
+                  - status
+                  - type
+                  type: object
+                type: array
+                x-kubernetes-list-map-keys:
+                - type
+                x-kubernetes-list-type: map
+              loadBalancer:
+                description: LoadBalancer contains the current status of the load-balancer,
+                  if one is present.
+                properties:
+                  ingress:
+                    description: Ingress is a list containing ingress points for the
+                      load-balancer. Traffic intended for the service should be sent
+                      to these ingress points.
+                    items:
+                      description: 'LoadBalancerIngress represents the status of a
+                        load-balancer ingress point: traffic intended for the service
+                        should be sent to an ingress point.'
+                      properties:
+                        hostname:
+                          description: Hostname is set for load-balancer ingress points
+                            that are DNS based (typically AWS load-balancers)
+                          type: string
+                        ip:
+                          description: IP is set for load-balancer ingress points
+                            that are IP based (typically GCE or OpenStack load-balancers)
+                          type: string
+                        ports:
+                          description: Ports is a list of records of service ports
+                            If used, every port defined in the service should have
+                            an entry in it
+                          items:
+                            properties:
+                              error:
+                                description: 'Error is to record the problem with
+                                  the service port The format of the error shall comply
+                                  with the following rules: - built-in error values
+                                  shall be specified in this file and those shall
+                                  use   CamelCase names - cloud provider specific
+                                  error values must have names that comply with the   format
+                                  foo.example.com/CamelCase. --- The regex it matches
+                                  is (dns1123SubdomainFmt/)?(qualifiedNameFmt)'
+                                maxLength: 316
+                                pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
+                                type: string
+                              port:
+                                description: Port is the port number of the service
+                                  port of which status is recorded here
+                                format: int32
+                                type: integer
+                              protocol:
+                                default: TCP
+                                description: 'Protocol is the protocol of the service
+                                  port of which status is recorded here The supported
+                                  values are: "TCP", "UDP", "SCTP"'
+                                type: string
+                            required:
+                            - port
+                            - protocol
+                            type: object
+                          type: array
+                          x-kubernetes-list-type: atomic
+                      type: object
+                    type: array
+                type: object
+            type: object
+        type: object
+    served: true
+    storage: true
+    subresources:
+      status: {}
+status:
+  acceptedNames:
+    kind: ""
+    plural: ""
+  conditions: []
+  storedVersions: []
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
 metadata:
   creationTimestamp: null
   name: gatewayroutes.kuma.io
@@ -627,30 +893,6 @@ spec:
               type: object
           type: object
 ---
-apiVersion: apiextensions.k8s.io/v1
-kind: CustomResourceDefinition
-metadata:
-  creationTimestamp: null
-  name: meshes.kuma.io
-spec:
-  group: kuma.io
-  names:
-    kind: Mesh
-    plural: meshes
-  scope: Cluster
-  versions:
-    - name: v1alpha1
-      served: true
-      storage: true
-      schema:
-        openAPIV3Schema:
-          description: Mesh is the Schema for the meshes API
-          properties:
-            spec:
-              x-kubernetes-preserve-unknown-fields: true
-              type: object
-          type: object
----
 apiVersion: rbac.authorization.k8s.io/v1
 kind: ClusterRole
 metadata:
diff --git a/app/kumactl/cmd/install/testdata/install-control-plane.global.golden.yaml b/app/kumactl/cmd/install/testdata/install-control-plane.global.golden.yaml
index bf21b715c..00dba3c33 100644
--- a/app/kumactl/cmd/install/testdata/install-control-plane.global.golden.yaml
+++ b/app/kumactl/cmd/install/testdata/install-control-plane.global.golden.yaml
@@ -84,6 +84,30 @@ spec:
 ---
 apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
+metadata:
+  creationTimestamp: null
+  name: meshes.kuma.io
+spec:
+  group: kuma.io
+  names:
+    kind: Mesh
+    plural: meshes
+  scope: Cluster
+  versions:
+    - name: v1alpha1
+      served: true
+      storage: true
+      schema:
+        openAPIV3Schema:
+          description: Mesh is the Schema for the meshes API
+          properties:
+            spec:
+              x-kubernetes-preserve-unknown-fields: true
+              type: object
+          type: object
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
 metadata:
   creationTimestamp: null
   name: proxytemplates.kuma.io
@@ -320,24 +344,24 @@ apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
 metadata:
   creationTimestamp: null
-  name: virtualoutbounds.kuma.io
+  name: dataplaneinsights.kuma.io
 spec:
   group: kuma.io
   names:
-    kind: VirtualOutbound
-    plural: virtualoutbounds
-  scope: Cluster
+    kind: DataplaneInsight
+    plural: dataplaneinsights
+  scope: Namespaced
   versions:
     - name: v1alpha1
       served: true
       storage: true
       schema:
         openAPIV3Schema:
-          description: VirtualOutbound is the Schema for the virtualoutbounds API
+          description: DataplaneInsight is the Schema for the dataplane insights API
           properties:
             mesh:
               type: string
-            spec:
+            status:
               x-kubernetes-preserve-unknown-fields: true
               type: object
           type: object
@@ -346,24 +370,24 @@ apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
 metadata:
   creationTimestamp: null
-  name: dataplaneinsights.kuma.io
+  name: virtualoutbounds.kuma.io
 spec:
   group: kuma.io
   names:
-    kind: DataplaneInsight
-    plural: dataplaneinsights
-  scope: Namespaced
+    kind: VirtualOutbound
+    plural: virtualoutbounds
+  scope: Cluster
   versions:
     - name: v1alpha1
       served: true
       storage: true
       schema:
         openAPIV3Schema:
-          description: DataplaneInsight is the Schema for the dataplane insights API
+          description: VirtualOutbound is the Schema for the virtualoutbounds API
           properties:
             mesh:
               type: string
-            status:
+            spec:
               x-kubernetes-preserve-unknown-fields: true
               type: object
           type: object
@@ -525,6 +549,248 @@ spec:
 ---
 apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
+metadata:
+  annotations:
+    controller-gen.kubebuilder.io/version: v0.7.0
+  creationTimestamp: null
+  name: gatewayinstances.kuma.io
+spec:
+  group: kuma.io
+  names:
+    kind: GatewayInstance
+    listKind: GatewayInstanceList
+    plural: gatewayinstances
+    singular: gatewayinstance
+  scope: Namespaced
+  versions:
+  - name: v1alpha1
+    schema:
+      openAPIV3Schema:
+        description: GatewayInstance represents a managed instance of a dataplane
+          proxy for a Kuma Gateway.
+        properties:
+          apiVersion:
+            description: 'APIVersion defines the versioned schema of this representation
+              of an object. Servers should convert recognized schemas to the latest
+              internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
+            type: string
+          kind:
+            description: 'Kind is a string value representing the REST resource this
+              object represents. Servers may infer this from the endpoint the client
+              submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
+            type: string
+          metadata:
+            type: object
+          spec:
+            description: GatewayInstanceSpec specifies the options available for a
+              GatewayDataplane.
+            properties:
+              replicas:
+                default: 1
+                description: Replicas is the number of dataplane proxy replicas to
+                  create. For now this is a fixed number, but in the future it could
+                  be automatically scaled based on metrics.
+                format: int32
+                minimum: 1
+                type: integer
+              resources:
+                description: Resources specifies the compute resources for the proxy
+                  container. The default can be set in the control plane config.
+                properties:
+                  limits:
+                    additionalProperties:
+                      anyOf:
+                      - type: integer
+                      - type: string
+                      pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+                      x-kubernetes-int-or-string: true
+                    description: 'Limits describes the maximum amount of compute resources
+                      allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/'
+                    type: object
+                  requests:
+                    additionalProperties:
+                      anyOf:
+                      - type: integer
+                      - type: string
+                      pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+                      x-kubernetes-int-or-string: true
+                    description: 'Requests describes the minimum amount of compute
+                      resources required. If Requests is omitted for a container,
+                      it defaults to Limits if that is explicitly specified, otherwise
+                      to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/'
+                    type: object
+                type: object
+              serviceType:
+                default: LoadBalancer
+                description: ServiceType specifies the type of managed Service that
+                  will be created to expose the dataplane proxies to traffic from
+                  outside the cluster. The ports to expose will be taken from the
+                  matching Gateway resource. If there is no matching Gateway, the
+                  managed Service will be deleted.
+                enum:
+                - LoadBalancer
+                - ClusterIP
+                - NodePort
+                type: string
+              tags:
+                additionalProperties:
+                  type: string
+                description: Tags specifies the Kuma tags that are propagated to the
+                  managed dataplane proxies. These tags should include exactly one
+                  `kuma.io/service` tag, and should match exactly one Gateway resource.
+                type: object
+            type: object
+          status:
+            description: GatewayInstanceStatus holds information about the status
+              of the gateway instance.
+            properties:
+              conditions:
+                description: Conditions is an array of gateway instance conditions.
+                items:
+                  description: "Condition contains details for one aspect of the current
+                    state of this API Resource. --- This struct is intended for direct
+                    use as an array at the field path .status.conditions.  For example,
+                    type FooStatus struct{     // Represents the observations of a
+                    foo's current state.     // Known .status.conditions.type are:
+                    \"Available\", \"Progressing\", and \"Degraded\"     // +patchMergeKey=type
+                    \    // +patchStrategy=merge     // +listType=map     // +listMapKey=type
+                    \    Conditions []metav1.Condition `json:\"conditions,omitempty\"
+                    patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`
+                    \n     // other fields }"
+                  properties:
+                    lastTransitionTime:
+                      description: lastTransitionTime is the last time the condition
+                        transitioned from one status to another. This should be when
+                        the underlying condition changed.  If that is not known, then
+                        using the time when the API field changed is acceptable.
+                      format: date-time
+                      type: string
+                    message:
+                      description: message is a human readable message indicating
+                        details about the transition. This may be an empty string.
+                      maxLength: 32768
+                      type: string
+                    observedGeneration:
+                      description: observedGeneration represents the .metadata.generation
+                        that the condition was set based upon. For instance, if .metadata.generation
+                        is currently 12, but the .status.conditions[x].observedGeneration
+                        is 9, the condition is out of date with respect to the current
+                        state of the instance.
+                      format: int64
+                      minimum: 0
+                      type: integer
+                    reason:
+                      description: reason contains a programmatic identifier indicating
+                        the reason for the condition's last transition. Producers
+                        of specific condition types may define expected values and
+                        meanings for this field, and whether the values are considered
+                        a guaranteed API. The value should be a CamelCase string.
+                        This field may not be empty.
+                      maxLength: 1024
+                      minLength: 1
+                      pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
+                      type: string
+                    status:
+                      description: status of the condition, one of True, False, Unknown.
+                      enum:
+                      - "True"
+                      - "False"
+                      - Unknown
+                      type: string
+                    type:
+                      description: type of condition in CamelCase or in foo.example.com/CamelCase.
+                        --- Many .condition.type values are consistent across resources
+                        like Available, but because arbitrary conditions can be useful
+                        (see .node.status.conditions), the ability to deconflict is
+                        important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt)
+                      maxLength: 316
+                      pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
+                      type: string
+                  required:
+                  - lastTransitionTime
+                  - message
+                  - reason
+                  - status
+                  - type
+                  type: object
+                type: array
+                x-kubernetes-list-map-keys:
+                - type
+                x-kubernetes-list-type: map
+              loadBalancer:
+                description: LoadBalancer contains the current status of the load-balancer,
+                  if one is present.
+                properties:
+                  ingress:
+                    description: Ingress is a list containing ingress points for the
+                      load-balancer. Traffic intended for the service should be sent
+                      to these ingress points.
+                    items:
+                      description: 'LoadBalancerIngress represents the status of a
+                        load-balancer ingress point: traffic intended for the service
+                        should be sent to an ingress point.'
+                      properties:
+                        hostname:
+                          description: Hostname is set for load-balancer ingress points
+                            that are DNS based (typically AWS load-balancers)
+                          type: string
+                        ip:
+                          description: IP is set for load-balancer ingress points
+                            that are IP based (typically GCE or OpenStack load-balancers)
+                          type: string
+                        ports:
+                          description: Ports is a list of records of service ports
+                            If used, every port defined in the service should have
+                            an entry in it
+                          items:
+                            properties:
+                              error:
+                                description: 'Error is to record the problem with
+                                  the service port The format of the error shall comply
+                                  with the following rules: - built-in error values
+                                  shall be specified in this file and those shall
+                                  use   CamelCase names - cloud provider specific
+                                  error values must have names that comply with the   format
+                                  foo.example.com/CamelCase. --- The regex it matches
+                                  is (dns1123SubdomainFmt/)?(qualifiedNameFmt)'
+                                maxLength: 316
+                                pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
+                                type: string
+                              port:
+                                description: Port is the port number of the service
+                                  port of which status is recorded here
+                                format: int32
+                                type: integer
+                              protocol:
+                                default: TCP
+                                description: 'Protocol is the protocol of the service
+                                  port of which status is recorded here The supported
+                                  values are: "TCP", "UDP", "SCTP"'
+                                type: string
+                            required:
+                            - port
+                            - protocol
+                            type: object
+                          type: array
+                          x-kubernetes-list-type: atomic
+                      type: object
+                    type: array
+                type: object
+            type: object
+        type: object
+    served: true
+    storage: true
+    subresources:
+      status: {}
+status:
+  acceptedNames:
+    kind: ""
+    plural: ""
+  conditions: []
+  storedVersions: []
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
 metadata:
   creationTimestamp: null
   name: gatewayroutes.kuma.io
@@ -627,30 +893,6 @@ spec:
               type: object
           type: object
 ---
-apiVersion: apiextensions.k8s.io/v1
-kind: CustomResourceDefinition
-metadata:
-  creationTimestamp: null
-  name: meshes.kuma.io
-spec:
-  group: kuma.io
-  names:
-    kind: Mesh
-    plural: meshes
-  scope: Cluster
-  versions:
-    - name: v1alpha1
-      served: true
-      storage: true
-      schema:
-        openAPIV3Schema:
-          description: Mesh is the Schema for the meshes API
-          properties:
-            spec:
-              x-kubernetes-preserve-unknown-fields: true
-              type: object
-          type: object
----
 apiVersion: rbac.authorization.k8s.io/v1
 kind: ClusterRole
 metadata:
diff --git a/app/kumactl/cmd/install/testdata/install-control-plane.override-env-vars.golden.yaml b/app/kumactl/cmd/install/testdata/install-control-plane.override-env-vars.golden.yaml
index e0d238455..93f51c303 100644
--- a/app/kumactl/cmd/install/testdata/install-control-plane.override-env-vars.golden.yaml
+++ b/app/kumactl/cmd/install/testdata/install-control-plane.override-env-vars.golden.yaml
@@ -84,6 +84,30 @@ spec:
 ---
 apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
+metadata:
+  creationTimestamp: null
+  name: meshes.kuma.io
+spec:
+  group: kuma.io
+  names:
+    kind: Mesh
+    plural: meshes
+  scope: Cluster
+  versions:
+    - name: v1alpha1
+      served: true
+      storage: true
+      schema:
+        openAPIV3Schema:
+          description: Mesh is the Schema for the meshes API
+          properties:
+            spec:
+              x-kubernetes-preserve-unknown-fields: true
+              type: object
+          type: object
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
 metadata:
   creationTimestamp: null
   name: proxytemplates.kuma.io
@@ -320,24 +344,24 @@ apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
 metadata:
   creationTimestamp: null
-  name: virtualoutbounds.kuma.io
+  name: dataplaneinsights.kuma.io
 spec:
   group: kuma.io
   names:
-    kind: VirtualOutbound
-    plural: virtualoutbounds
-  scope: Cluster
+    kind: DataplaneInsight
+    plural: dataplaneinsights
+  scope: Namespaced
   versions:
     - name: v1alpha1
       served: true
       storage: true
       schema:
         openAPIV3Schema:
-          description: VirtualOutbound is the Schema for the virtualoutbounds API
+          description: DataplaneInsight is the Schema for the dataplane insights API
           properties:
             mesh:
               type: string
-            spec:
+            status:
               x-kubernetes-preserve-unknown-fields: true
               type: object
           type: object
@@ -346,24 +370,24 @@ apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
 metadata:
   creationTimestamp: null
-  name: dataplaneinsights.kuma.io
+  name: virtualoutbounds.kuma.io
 spec:
   group: kuma.io
   names:
-    kind: DataplaneInsight
-    plural: dataplaneinsights
-  scope: Namespaced
+    kind: VirtualOutbound
+    plural: virtualoutbounds
+  scope: Cluster
   versions:
     - name: v1alpha1
       served: true
       storage: true
       schema:
         openAPIV3Schema:
-          description: DataplaneInsight is the Schema for the dataplane insights API
+          description: VirtualOutbound is the Schema for the virtualoutbounds API
           properties:
             mesh:
               type: string
-            status:
+            spec:
               x-kubernetes-preserve-unknown-fields: true
               type: object
           type: object
@@ -525,6 +549,248 @@ spec:
 ---
 apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
+metadata:
+  annotations:
+    controller-gen.kubebuilder.io/version: v0.7.0
+  creationTimestamp: null
+  name: gatewayinstances.kuma.io
+spec:
+  group: kuma.io
+  names:
+    kind: GatewayInstance
+    listKind: GatewayInstanceList
+    plural: gatewayinstances
+    singular: gatewayinstance
+  scope: Namespaced
+  versions:
+  - name: v1alpha1
+    schema:
+      openAPIV3Schema:
+        description: GatewayInstance represents a managed instance of a dataplane
+          proxy for a Kuma Gateway.
+        properties:
+          apiVersion:
+            description: 'APIVersion defines the versioned schema of this representation
+              of an object. Servers should convert recognized schemas to the latest
+              internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
+            type: string
+          kind:
+            description: 'Kind is a string value representing the REST resource this
+              object represents. Servers may infer this from the endpoint the client
+              submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
+            type: string
+          metadata:
+            type: object
+          spec:
+            description: GatewayInstanceSpec specifies the options available for a
+              GatewayDataplane.
+            properties:
+              replicas:
+                default: 1
+                description: Replicas is the number of dataplane proxy replicas to
+                  create. For now this is a fixed number, but in the future it could
+                  be automatically scaled based on metrics.
+                format: int32
+                minimum: 1
+                type: integer
+              resources:
+                description: Resources specifies the compute resources for the proxy
+                  container. The default can be set in the control plane config.
+                properties:
+                  limits:
+                    additionalProperties:
+                      anyOf:
+                      - type: integer
+                      - type: string
+                      pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+                      x-kubernetes-int-or-string: true
+                    description: 'Limits describes the maximum amount of compute resources
+                      allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/'
+                    type: object
+                  requests:
+                    additionalProperties:
+                      anyOf:
+                      - type: integer
+                      - type: string
+                      pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+                      x-kubernetes-int-or-string: true
+                    description: 'Requests describes the minimum amount of compute
+                      resources required. If Requests is omitted for a container,
+                      it defaults to Limits if that is explicitly specified, otherwise
+                      to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/'
+                    type: object
+                type: object
+              serviceType:
+                default: LoadBalancer
+                description: ServiceType specifies the type of managed Service that
+                  will be created to expose the dataplane proxies to traffic from
+                  outside the cluster. The ports to expose will be taken from the
+                  matching Gateway resource. If there is no matching Gateway, the
+                  managed Service will be deleted.
+                enum:
+                - LoadBalancer
+                - ClusterIP
+                - NodePort
+                type: string
+              tags:
+                additionalProperties:
+                  type: string
+                description: Tags specifies the Kuma tags that are propagated to the
+                  managed dataplane proxies. These tags should include exactly one
+                  `kuma.io/service` tag, and should match exactly one Gateway resource.
+                type: object
+            type: object
+          status:
+            description: GatewayInstanceStatus holds information about the status
+              of the gateway instance.
+            properties:
+              conditions:
+                description: Conditions is an array of gateway instance conditions.
+                items:
+                  description: "Condition contains details for one aspect of the current
+                    state of this API Resource. --- This struct is intended for direct
+                    use as an array at the field path .status.conditions.  For example,
+                    type FooStatus struct{     // Represents the observations of a
+                    foo's current state.     // Known .status.conditions.type are:
+                    \"Available\", \"Progressing\", and \"Degraded\"     // +patchMergeKey=type
+                    \    // +patchStrategy=merge     // +listType=map     // +listMapKey=type
+                    \    Conditions []metav1.Condition `json:\"conditions,omitempty\"
+                    patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`
+                    \n     // other fields }"
+                  properties:
+                    lastTransitionTime:
+                      description: lastTransitionTime is the last time the condition
+                        transitioned from one status to another. This should be when
+                        the underlying condition changed.  If that is not known, then
+                        using the time when the API field changed is acceptable.
+                      format: date-time
+                      type: string
+                    message:
+                      description: message is a human readable message indicating
+                        details about the transition. This may be an empty string.
+                      maxLength: 32768
+                      type: string
+                    observedGeneration:
+                      description: observedGeneration represents the .metadata.generation
+                        that the condition was set based upon. For instance, if .metadata.generation
+                        is currently 12, but the .status.conditions[x].observedGeneration
+                        is 9, the condition is out of date with respect to the current
+                        state of the instance.
+                      format: int64
+                      minimum: 0
+                      type: integer
+                    reason:
+                      description: reason contains a programmatic identifier indicating
+                        the reason for the condition's last transition. Producers
+                        of specific condition types may define expected values and
+                        meanings for this field, and whether the values are considered
+                        a guaranteed API. The value should be a CamelCase string.
+                        This field may not be empty.
+                      maxLength: 1024
+                      minLength: 1
+                      pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
+                      type: string
+                    status:
+                      description: status of the condition, one of True, False, Unknown.
+                      enum:
+                      - "True"
+                      - "False"
+                      - Unknown
+                      type: string
+                    type:
+                      description: type of condition in CamelCase or in foo.example.com/CamelCase.
+                        --- Many .condition.type values are consistent across resources
+                        like Available, but because arbitrary conditions can be useful
+                        (see .node.status.conditions), the ability to deconflict is
+                        important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt)
+                      maxLength: 316
+                      pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
+                      type: string
+                  required:
+                  - lastTransitionTime
+                  - message
+                  - reason
+                  - status
+                  - type
+                  type: object
+                type: array
+                x-kubernetes-list-map-keys:
+                - type
+                x-kubernetes-list-type: map
+              loadBalancer:
+                description: LoadBalancer contains the current status of the load-balancer,
+                  if one is present.
+                properties:
+                  ingress:
+                    description: Ingress is a list containing ingress points for the
+                      load-balancer. Traffic intended for the service should be sent
+                      to these ingress points.
+                    items:
+                      description: 'LoadBalancerIngress represents the status of a
+                        load-balancer ingress point: traffic intended for the service
+                        should be sent to an ingress point.'
+                      properties:
+                        hostname:
+                          description: Hostname is set for load-balancer ingress points
+                            that are DNS based (typically AWS load-balancers)
+                          type: string
+                        ip:
+                          description: IP is set for load-balancer ingress points
+                            that are IP based (typically GCE or OpenStack load-balancers)
+                          type: string
+                        ports:
+                          description: Ports is a list of records of service ports
+                            If used, every port defined in the service should have
+                            an entry in it
+                          items:
+                            properties:
+                              error:
+                                description: 'Error is to record the problem with
+                                  the service port The format of the error shall comply
+                                  with the following rules: - built-in error values
+                                  shall be specified in this file and those shall
+                                  use   CamelCase names - cloud provider specific
+                                  error values must have names that comply with the   format
+                                  foo.example.com/CamelCase. --- The regex it matches
+                                  is (dns1123SubdomainFmt/)?(qualifiedNameFmt)'
+                                maxLength: 316
+                                pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
+                                type: string
+                              port:
+                                description: Port is the port number of the service
+                                  port of which status is recorded here
+                                format: int32
+                                type: integer
+                              protocol:
+                                default: TCP
+                                description: 'Protocol is the protocol of the service
+                                  port of which status is recorded here The supported
+                                  values are: "TCP", "UDP", "SCTP"'
+                                type: string
+                            required:
+                            - port
+                            - protocol
+                            type: object
+                          type: array
+                          x-kubernetes-list-type: atomic
+                      type: object
+                    type: array
+                type: object
+            type: object
+        type: object
+    served: true
+    storage: true
+    subresources:
+      status: {}
+status:
+  acceptedNames:
+    kind: ""
+    plural: ""
+  conditions: []
+  storedVersions: []
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
 metadata:
   creationTimestamp: null
   name: gatewayroutes.kuma.io
@@ -627,30 +893,6 @@ spec:
               type: object
           type: object
 ---
-apiVersion: apiextensions.k8s.io/v1
-kind: CustomResourceDefinition
-metadata:
-  creationTimestamp: null
-  name: meshes.kuma.io
-spec:
-  group: kuma.io
-  names:
-    kind: Mesh
-    plural: meshes
-  scope: Cluster
-  versions:
-    - name: v1alpha1
-      served: true
-      storage: true
-      schema:
-        openAPIV3Schema:
-          description: Mesh is the Schema for the meshes API
-          properties:
-            spec:
-              x-kubernetes-preserve-unknown-fields: true
-              type: object
-          type: object
----
 apiVersion: rbac.authorization.k8s.io/v1
 kind: ClusterRole
 metadata:
diff --git a/app/kumactl/cmd/install/testdata/install-control-plane.overrides.golden.yaml b/app/kumactl/cmd/install/testdata/install-control-plane.overrides.golden.yaml
index a3fd7b79d..c3fa316d0 100644
--- a/app/kumactl/cmd/install/testdata/install-control-plane.overrides.golden.yaml
+++ b/app/kumactl/cmd/install/testdata/install-control-plane.overrides.golden.yaml
@@ -84,6 +84,30 @@ spec:
 ---
 apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
+metadata:
+  creationTimestamp: null
+  name: meshes.kuma.io
+spec:
+  group: kuma.io
+  names:
+    kind: Mesh
+    plural: meshes
+  scope: Cluster
+  versions:
+    - name: v1alpha1
+      served: true
+      storage: true
+      schema:
+        openAPIV3Schema:
+          description: Mesh is the Schema for the meshes API
+          properties:
+            spec:
+              x-kubernetes-preserve-unknown-fields: true
+              type: object
+          type: object
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
 metadata:
   creationTimestamp: null
   name: proxytemplates.kuma.io
@@ -320,24 +344,24 @@ apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
 metadata:
   creationTimestamp: null
-  name: virtualoutbounds.kuma.io
+  name: dataplaneinsights.kuma.io
 spec:
   group: kuma.io
   names:
-    kind: VirtualOutbound
-    plural: virtualoutbounds
-  scope: Cluster
+    kind: DataplaneInsight
+    plural: dataplaneinsights
+  scope: Namespaced
   versions:
     - name: v1alpha1
       served: true
       storage: true
       schema:
         openAPIV3Schema:
-          description: VirtualOutbound is the Schema for the virtualoutbounds API
+          description: DataplaneInsight is the Schema for the dataplane insights API
           properties:
             mesh:
               type: string
-            spec:
+            status:
               x-kubernetes-preserve-unknown-fields: true
               type: object
           type: object
@@ -346,24 +370,24 @@ apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
 metadata:
   creationTimestamp: null
-  name: dataplaneinsights.kuma.io
+  name: virtualoutbounds.kuma.io
 spec:
   group: kuma.io
   names:
-    kind: DataplaneInsight
-    plural: dataplaneinsights
-  scope: Namespaced
+    kind: VirtualOutbound
+    plural: virtualoutbounds
+  scope: Cluster
   versions:
     - name: v1alpha1
       served: true
       storage: true
       schema:
         openAPIV3Schema:
-          description: DataplaneInsight is the Schema for the dataplane insights API
+          description: VirtualOutbound is the Schema for the virtualoutbounds API
           properties:
             mesh:
               type: string
-            status:
+            spec:
               x-kubernetes-preserve-unknown-fields: true
               type: object
           type: object
@@ -525,6 +549,248 @@ spec:
 ---
 apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
+metadata:
+  annotations:
+    controller-gen.kubebuilder.io/version: v0.7.0
+  creationTimestamp: null
+  name: gatewayinstances.kuma.io
+spec:
+  group: kuma.io
+  names:
+    kind: GatewayInstance
+    listKind: GatewayInstanceList
+    plural: gatewayinstances
+    singular: gatewayinstance
+  scope: Namespaced
+  versions:
+  - name: v1alpha1
+    schema:
+      openAPIV3Schema:
+        description: GatewayInstance represents a managed instance of a dataplane
+          proxy for a Kuma Gateway.
+        properties:
+          apiVersion:
+            description: 'APIVersion defines the versioned schema of this representation
+              of an object. Servers should convert recognized schemas to the latest
+              internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
+            type: string
+          kind:
+            description: 'Kind is a string value representing the REST resource this
+              object represents. Servers may infer this from the endpoint the client
+              submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
+            type: string
+          metadata:
+            type: object
+          spec:
+            description: GatewayInstanceSpec specifies the options available for a
+              GatewayDataplane.
+            properties:
+              replicas:
+                default: 1
+                description: Replicas is the number of dataplane proxy replicas to
+                  create. For now this is a fixed number, but in the future it could
+                  be automatically scaled based on metrics.
+                format: int32
+                minimum: 1
+                type: integer
+              resources:
+                description: Resources specifies the compute resources for the proxy
+                  container. The default can be set in the control plane config.
+                properties:
+                  limits:
+                    additionalProperties:
+                      anyOf:
+                      - type: integer
+                      - type: string
+                      pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+                      x-kubernetes-int-or-string: true
+                    description: 'Limits describes the maximum amount of compute resources
+                      allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/'
+                    type: object
+                  requests:
+                    additionalProperties:
+                      anyOf:
+                      - type: integer
+                      - type: string
+                      pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+                      x-kubernetes-int-or-string: true
+                    description: 'Requests describes the minimum amount of compute
+                      resources required. If Requests is omitted for a container,
+                      it defaults to Limits if that is explicitly specified, otherwise
+                      to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/'
+                    type: object
+                type: object
+              serviceType:
+                default: LoadBalancer
+                description: ServiceType specifies the type of managed Service that
+                  will be created to expose the dataplane proxies to traffic from
+                  outside the cluster. The ports to expose will be taken from the
+                  matching Gateway resource. If there is no matching Gateway, the
+                  managed Service will be deleted.
+                enum:
+                - LoadBalancer
+                - ClusterIP
+                - NodePort
+                type: string
+              tags:
+                additionalProperties:
+                  type: string
+                description: Tags specifies the Kuma tags that are propagated to the
+                  managed dataplane proxies. These tags should include exactly one
+                  `kuma.io/service` tag, and should match exactly one Gateway resource.
+                type: object
+            type: object
+          status:
+            description: GatewayInstanceStatus holds information about the status
+              of the gateway instance.
+            properties:
+              conditions:
+                description: Conditions is an array of gateway instance conditions.
+                items:
+                  description: "Condition contains details for one aspect of the current
+                    state of this API Resource. --- This struct is intended for direct
+                    use as an array at the field path .status.conditions.  For example,
+                    type FooStatus struct{     // Represents the observations of a
+                    foo's current state.     // Known .status.conditions.type are:
+                    \"Available\", \"Progressing\", and \"Degraded\"     // +patchMergeKey=type
+                    \    // +patchStrategy=merge     // +listType=map     // +listMapKey=type
+                    \    Conditions []metav1.Condition `json:\"conditions,omitempty\"
+                    patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`
+                    \n     // other fields }"
+                  properties:
+                    lastTransitionTime:
+                      description: lastTransitionTime is the last time the condition
+                        transitioned from one status to another. This should be when
+                        the underlying condition changed.  If that is not known, then
+                        using the time when the API field changed is acceptable.
+                      format: date-time
+                      type: string
+                    message:
+                      description: message is a human readable message indicating
+                        details about the transition. This may be an empty string.
+                      maxLength: 32768
+                      type: string
+                    observedGeneration:
+                      description: observedGeneration represents the .metadata.generation
+                        that the condition was set based upon. For instance, if .metadata.generation
+                        is currently 12, but the .status.conditions[x].observedGeneration
+                        is 9, the condition is out of date with respect to the current
+                        state of the instance.
+                      format: int64
+                      minimum: 0
+                      type: integer
+                    reason:
+                      description: reason contains a programmatic identifier indicating
+                        the reason for the condition's last transition. Producers
+                        of specific condition types may define expected values and
+                        meanings for this field, and whether the values are considered
+                        a guaranteed API. The value should be a CamelCase string.
+                        This field may not be empty.
+                      maxLength: 1024
+                      minLength: 1
+                      pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
+                      type: string
+                    status:
+                      description: status of the condition, one of True, False, Unknown.
+                      enum:
+                      - "True"
+                      - "False"
+                      - Unknown
+                      type: string
+                    type:
+                      description: type of condition in CamelCase or in foo.example.com/CamelCase.
+                        --- Many .condition.type values are consistent across resources
+                        like Available, but because arbitrary conditions can be useful
+                        (see .node.status.conditions), the ability to deconflict is
+                        important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt)
+                      maxLength: 316
+                      pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
+                      type: string
+                  required:
+                  - lastTransitionTime
+                  - message
+                  - reason
+                  - status
+                  - type
+                  type: object
+                type: array
+                x-kubernetes-list-map-keys:
+                - type
+                x-kubernetes-list-type: map
+              loadBalancer:
+                description: LoadBalancer contains the current status of the load-balancer,
+                  if one is present.
+                properties:
+                  ingress:
+                    description: Ingress is a list containing ingress points for the
+                      load-balancer. Traffic intended for the service should be sent
+                      to these ingress points.
+                    items:
+                      description: 'LoadBalancerIngress represents the status of a
+                        load-balancer ingress point: traffic intended for the service
+                        should be sent to an ingress point.'
+                      properties:
+                        hostname:
+                          description: Hostname is set for load-balancer ingress points
+                            that are DNS based (typically AWS load-balancers)
+                          type: string
+                        ip:
+                          description: IP is set for load-balancer ingress points
+                            that are IP based (typically GCE or OpenStack load-balancers)
+                          type: string
+                        ports:
+                          description: Ports is a list of records of service ports
+                            If used, every port defined in the service should have
+                            an entry in it
+                          items:
+                            properties:
+                              error:
+                                description: 'Error is to record the problem with
+                                  the service port The format of the error shall comply
+                                  with the following rules: - built-in error values
+                                  shall be specified in this file and those shall
+                                  use   CamelCase names - cloud provider specific
+                                  error values must have names that comply with the   format
+                                  foo.example.com/CamelCase. --- The regex it matches
+                                  is (dns1123SubdomainFmt/)?(qualifiedNameFmt)'
+                                maxLength: 316
+                                pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
+                                type: string
+                              port:
+                                description: Port is the port number of the service
+                                  port of which status is recorded here
+                                format: int32
+                                type: integer
+                              protocol:
+                                default: TCP
+                                description: 'Protocol is the protocol of the service
+                                  port of which status is recorded here The supported
+                                  values are: "TCP", "UDP", "SCTP"'
+                                type: string
+                            required:
+                            - port
+                            - protocol
+                            type: object
+                          type: array
+                          x-kubernetes-list-type: atomic
+                      type: object
+                    type: array
+                type: object
+            type: object
+        type: object
+    served: true
+    storage: true
+    subresources:
+      status: {}
+status:
+  acceptedNames:
+    kind: ""
+    plural: ""
+  conditions: []
+  storedVersions: []
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
 metadata:
   creationTimestamp: null
   name: gatewayroutes.kuma.io
@@ -627,30 +893,6 @@ spec:
               type: object
           type: object
 ---
-apiVersion: apiextensions.k8s.io/v1
-kind: CustomResourceDefinition
-metadata:
-  creationTimestamp: null
-  name: meshes.kuma.io
-spec:
-  group: kuma.io
-  names:
-    kind: Mesh
-    plural: meshes
-  scope: Cluster
-  versions:
-    - name: v1alpha1
-      served: true
-      storage: true
-      schema:
-        openAPIV3Schema:
-          description: Mesh is the Schema for the meshes API
-          properties:
-            spec:
-              x-kubernetes-preserve-unknown-fields: true
-              type: object
-          type: object
----
 apiVersion: rbac.authorization.k8s.io/v1
 kind: ClusterRole
 metadata:
diff --git a/app/kumactl/cmd/install/testdata/install-control-plane.with-ingress.golden.yaml b/app/kumactl/cmd/install/testdata/install-control-plane.with-ingress.golden.yaml
index b122623e1..ab3f423d0 100644
--- a/app/kumactl/cmd/install/testdata/install-control-plane.with-ingress.golden.yaml
+++ b/app/kumactl/cmd/install/testdata/install-control-plane.with-ingress.golden.yaml
@@ -93,6 +93,30 @@ spec:
 ---
 apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
+metadata:
+  creationTimestamp: null
+  name: meshes.kuma.io
+spec:
+  group: kuma.io
+  names:
+    kind: Mesh
+    plural: meshes
+  scope: Cluster
+  versions:
+    - name: v1alpha1
+      served: true
+      storage: true
+      schema:
+        openAPIV3Schema:
+          description: Mesh is the Schema for the meshes API
+          properties:
+            spec:
+              x-kubernetes-preserve-unknown-fields: true
+              type: object
+          type: object
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
 metadata:
   creationTimestamp: null
   name: proxytemplates.kuma.io
@@ -329,24 +353,24 @@ apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
 metadata:
   creationTimestamp: null
-  name: virtualoutbounds.kuma.io
+  name: dataplaneinsights.kuma.io
 spec:
   group: kuma.io
   names:
-    kind: VirtualOutbound
-    plural: virtualoutbounds
-  scope: Cluster
+    kind: DataplaneInsight
+    plural: dataplaneinsights
+  scope: Namespaced
   versions:
     - name: v1alpha1
       served: true
       storage: true
       schema:
         openAPIV3Schema:
-          description: VirtualOutbound is the Schema for the virtualoutbounds API
+          description: DataplaneInsight is the Schema for the dataplane insights API
           properties:
             mesh:
               type: string
-            spec:
+            status:
               x-kubernetes-preserve-unknown-fields: true
               type: object
           type: object
@@ -355,24 +379,24 @@ apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
 metadata:
   creationTimestamp: null
-  name: dataplaneinsights.kuma.io
+  name: virtualoutbounds.kuma.io
 spec:
   group: kuma.io
   names:
-    kind: DataplaneInsight
-    plural: dataplaneinsights
-  scope: Namespaced
+    kind: VirtualOutbound
+    plural: virtualoutbounds
+  scope: Cluster
   versions:
     - name: v1alpha1
       served: true
       storage: true
       schema:
         openAPIV3Schema:
-          description: DataplaneInsight is the Schema for the dataplane insights API
+          description: VirtualOutbound is the Schema for the virtualoutbounds API
           properties:
             mesh:
               type: string
-            status:
+            spec:
               x-kubernetes-preserve-unknown-fields: true
               type: object
           type: object
@@ -534,6 +558,248 @@ spec:
 ---
 apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
+metadata:
+  annotations:
+    controller-gen.kubebuilder.io/version: v0.7.0
+  creationTimestamp: null
+  name: gatewayinstances.kuma.io
+spec:
+  group: kuma.io
+  names:
+    kind: GatewayInstance
+    listKind: GatewayInstanceList
+    plural: gatewayinstances
+    singular: gatewayinstance
+  scope: Namespaced
+  versions:
+  - name: v1alpha1
+    schema:
+      openAPIV3Schema:
+        description: GatewayInstance represents a managed instance of a dataplane
+          proxy for a Kuma Gateway.
+        properties:
+          apiVersion:
+            description: 'APIVersion defines the versioned schema of this representation
+              of an object. Servers should convert recognized schemas to the latest
+              internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
+            type: string
+          kind:
+            description: 'Kind is a string value representing the REST resource this
+              object represents. Servers may infer this from the endpoint the client
+              submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
+            type: string
+          metadata:
+            type: object
+          spec:
+            description: GatewayInstanceSpec specifies the options available for a
+              GatewayDataplane.
+            properties:
+              replicas:
+                default: 1
+                description: Replicas is the number of dataplane proxy replicas to
+                  create. For now this is a fixed number, but in the future it could
+                  be automatically scaled based on metrics.
+                format: int32
+                minimum: 1
+                type: integer
+              resources:
+                description: Resources specifies the compute resources for the proxy
+                  container. The default can be set in the control plane config.
+                properties:
+                  limits:
+                    additionalProperties:
+                      anyOf:
+                      - type: integer
+                      - type: string
+                      pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+                      x-kubernetes-int-or-string: true
+                    description: 'Limits describes the maximum amount of compute resources
+                      allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/'
+                    type: object
+                  requests:
+                    additionalProperties:
+                      anyOf:
+                      - type: integer
+                      - type: string
+                      pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+                      x-kubernetes-int-or-string: true
+                    description: 'Requests describes the minimum amount of compute
+                      resources required. If Requests is omitted for a container,
+                      it defaults to Limits if that is explicitly specified, otherwise
+                      to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/'
+                    type: object
+                type: object
+              serviceType:
+                default: LoadBalancer
+                description: ServiceType specifies the type of managed Service that
+                  will be created to expose the dataplane proxies to traffic from
+                  outside the cluster. The ports to expose will be taken from the
+                  matching Gateway resource. If there is no matching Gateway, the
+                  managed Service will be deleted.
+                enum:
+                - LoadBalancer
+                - ClusterIP
+                - NodePort
+                type: string
+              tags:
+                additionalProperties:
+                  type: string
+                description: Tags specifies the Kuma tags that are propagated to the
+                  managed dataplane proxies. These tags should include exactly one
+                  `kuma.io/service` tag, and should match exactly one Gateway resource.
+                type: object
+            type: object
+          status:
+            description: GatewayInstanceStatus holds information about the status
+              of the gateway instance.
+            properties:
+              conditions:
+                description: Conditions is an array of gateway instance conditions.
+                items:
+                  description: "Condition contains details for one aspect of the current
+                    state of this API Resource. --- This struct is intended for direct
+                    use as an array at the field path .status.conditions.  For example,
+                    type FooStatus struct{     // Represents the observations of a
+                    foo's current state.     // Known .status.conditions.type are:
+                    \"Available\", \"Progressing\", and \"Degraded\"     // +patchMergeKey=type
+                    \    // +patchStrategy=merge     // +listType=map     // +listMapKey=type
+                    \    Conditions []metav1.Condition `json:\"conditions,omitempty\"
+                    patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`
+                    \n     // other fields }"
+                  properties:
+                    lastTransitionTime:
+                      description: lastTransitionTime is the last time the condition
+                        transitioned from one status to another. This should be when
+                        the underlying condition changed.  If that is not known, then
+                        using the time when the API field changed is acceptable.
+                      format: date-time
+                      type: string
+                    message:
+                      description: message is a human readable message indicating
+                        details about the transition. This may be an empty string.
+                      maxLength: 32768
+                      type: string
+                    observedGeneration:
+                      description: observedGeneration represents the .metadata.generation
+                        that the condition was set based upon. For instance, if .metadata.generation
+                        is currently 12, but the .status.conditions[x].observedGeneration
+                        is 9, the condition is out of date with respect to the current
+                        state of the instance.
+                      format: int64
+                      minimum: 0
+                      type: integer
+                    reason:
+                      description: reason contains a programmatic identifier indicating
+                        the reason for the condition's last transition. Producers
+                        of specific condition types may define expected values and
+                        meanings for this field, and whether the values are considered
+                        a guaranteed API. The value should be a CamelCase string.
+                        This field may not be empty.
+                      maxLength: 1024
+                      minLength: 1
+                      pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
+                      type: string
+                    status:
+                      description: status of the condition, one of True, False, Unknown.
+                      enum:
+                      - "True"
+                      - "False"
+                      - Unknown
+                      type: string
+                    type:
+                      description: type of condition in CamelCase or in foo.example.com/CamelCase.
+                        --- Many .condition.type values are consistent across resources
+                        like Available, but because arbitrary conditions can be useful
+                        (see .node.status.conditions), the ability to deconflict is
+                        important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt)
+                      maxLength: 316
+                      pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
+                      type: string
+                  required:
+                  - lastTransitionTime
+                  - message
+                  - reason
+                  - status
+                  - type
+                  type: object
+                type: array
+                x-kubernetes-list-map-keys:
+                - type
+                x-kubernetes-list-type: map
+              loadBalancer:
+                description: LoadBalancer contains the current status of the load-balancer,
+                  if one is present.
+                properties:
+                  ingress:
+                    description: Ingress is a list containing ingress points for the
+                      load-balancer. Traffic intended for the service should be sent
+                      to these ingress points.
+                    items:
+                      description: 'LoadBalancerIngress represents the status of a
+                        load-balancer ingress point: traffic intended for the service
+                        should be sent to an ingress point.'
+                      properties:
+                        hostname:
+                          description: Hostname is set for load-balancer ingress points
+                            that are DNS based (typically AWS load-balancers)
+                          type: string
+                        ip:
+                          description: IP is set for load-balancer ingress points
+                            that are IP based (typically GCE or OpenStack load-balancers)
+                          type: string
+                        ports:
+                          description: Ports is a list of records of service ports
+                            If used, every port defined in the service should have
+                            an entry in it
+                          items:
+                            properties:
+                              error:
+                                description: 'Error is to record the problem with
+                                  the service port The format of the error shall comply
+                                  with the following rules: - built-in error values
+                                  shall be specified in this file and those shall
+                                  use   CamelCase names - cloud provider specific
+                                  error values must have names that comply with the   format
+                                  foo.example.com/CamelCase. --- The regex it matches
+                                  is (dns1123SubdomainFmt/)?(qualifiedNameFmt)'
+                                maxLength: 316
+                                pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
+                                type: string
+                              port:
+                                description: Port is the port number of the service
+                                  port of which status is recorded here
+                                format: int32
+                                type: integer
+                              protocol:
+                                default: TCP
+                                description: 'Protocol is the protocol of the service
+                                  port of which status is recorded here The supported
+                                  values are: "TCP", "UDP", "SCTP"'
+                                type: string
+                            required:
+                            - port
+                            - protocol
+                            type: object
+                          type: array
+                          x-kubernetes-list-type: atomic
+                      type: object
+                    type: array
+                type: object
+            type: object
+        type: object
+    served: true
+    storage: true
+    subresources:
+      status: {}
+status:
+  acceptedNames:
+    kind: ""
+    plural: ""
+  conditions: []
+  storedVersions: []
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
 metadata:
   creationTimestamp: null
   name: gatewayroutes.kuma.io
@@ -636,30 +902,6 @@ spec:
               type: object
           type: object
 ---
-apiVersion: apiextensions.k8s.io/v1
-kind: CustomResourceDefinition
-metadata:
-  creationTimestamp: null
-  name: meshes.kuma.io
-spec:
-  group: kuma.io
-  names:
-    kind: Mesh
-    plural: meshes
-  scope: Cluster
-  versions:
-    - name: v1alpha1
-      served: true
-      storage: true
-      schema:
-        openAPIV3Schema:
-          description: Mesh is the Schema for the meshes API
-          properties:
-            spec:
-              x-kubernetes-preserve-unknown-fields: true
-              type: object
-          type: object
----
 apiVersion: rbac.authorization.k8s.io/v1
 kind: ClusterRole
 metadata:
diff --git a/app/kumactl/cmd/install/testdata/install-control-plane.zone.golden.yaml b/app/kumactl/cmd/install/testdata/install-control-plane.zone.golden.yaml
index f410d5373..e144ffea8 100644
--- a/app/kumactl/cmd/install/testdata/install-control-plane.zone.golden.yaml
+++ b/app/kumactl/cmd/install/testdata/install-control-plane.zone.golden.yaml
@@ -84,6 +84,30 @@ spec:
 ---
 apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
+metadata:
+  creationTimestamp: null
+  name: meshes.kuma.io
+spec:
+  group: kuma.io
+  names:
+    kind: Mesh
+    plural: meshes
+  scope: Cluster
+  versions:
+    - name: v1alpha1
+      served: true
+      storage: true
+      schema:
+        openAPIV3Schema:
+          description: Mesh is the Schema for the meshes API
+          properties:
+            spec:
+              x-kubernetes-preserve-unknown-fields: true
+              type: object
+          type: object
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
 metadata:
   creationTimestamp: null
   name: proxytemplates.kuma.io
@@ -320,24 +344,24 @@ apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
 metadata:
   creationTimestamp: null
-  name: virtualoutbounds.kuma.io
+  name: dataplaneinsights.kuma.io
 spec:
   group: kuma.io
   names:
-    kind: VirtualOutbound
-    plural: virtualoutbounds
-  scope: Cluster
+    kind: DataplaneInsight
+    plural: dataplaneinsights
+  scope: Namespaced
   versions:
     - name: v1alpha1
       served: true
       storage: true
       schema:
         openAPIV3Schema:
-          description: VirtualOutbound is the Schema for the virtualoutbounds API
+          description: DataplaneInsight is the Schema for the dataplane insights API
           properties:
             mesh:
               type: string
-            spec:
+            status:
               x-kubernetes-preserve-unknown-fields: true
               type: object
           type: object
@@ -346,24 +370,24 @@ apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
 metadata:
   creationTimestamp: null
-  name: dataplaneinsights.kuma.io
+  name: virtualoutbounds.kuma.io
 spec:
   group: kuma.io
   names:
-    kind: DataplaneInsight
-    plural: dataplaneinsights
-  scope: Namespaced
+    kind: VirtualOutbound
+    plural: virtualoutbounds
+  scope: Cluster
   versions:
     - name: v1alpha1
       served: true
       storage: true
       schema:
         openAPIV3Schema:
-          description: DataplaneInsight is the Schema for the dataplane insights API
+          description: VirtualOutbound is the Schema for the virtualoutbounds API
           properties:
             mesh:
               type: string
-            status:
+            spec:
               x-kubernetes-preserve-unknown-fields: true
               type: object
           type: object
@@ -525,6 +549,248 @@ spec:
 ---
 apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
+metadata:
+  annotations:
+    controller-gen.kubebuilder.io/version: v0.7.0
+  creationTimestamp: null
+  name: gatewayinstances.kuma.io
+spec:
+  group: kuma.io
+  names:
+    kind: GatewayInstance
+    listKind: GatewayInstanceList
+    plural: gatewayinstances
+    singular: gatewayinstance
+  scope: Namespaced
+  versions:
+  - name: v1alpha1
+    schema:
+      openAPIV3Schema:
+        description: GatewayInstance represents a managed instance of a dataplane
+          proxy for a Kuma Gateway.
+        properties:
+          apiVersion:
+            description: 'APIVersion defines the versioned schema of this representation
+              of an object. Servers should convert recognized schemas to the latest
+              internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
+            type: string
+          kind:
+            description: 'Kind is a string value representing the REST resource this
+              object represents. Servers may infer this from the endpoint the client
+              submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
+            type: string
+          metadata:
+            type: object
+          spec:
+            description: GatewayInstanceSpec specifies the options available for a
+              GatewayDataplane.
+            properties:
+              replicas:
+                default: 1
+                description: Replicas is the number of dataplane proxy replicas to
+                  create. For now this is a fixed number, but in the future it could
+                  be automatically scaled based on metrics.
+                format: int32
+                minimum: 1
+                type: integer
+              resources:
+                description: Resources specifies the compute resources for the proxy
+                  container. The default can be set in the control plane config.
+                properties:
+                  limits:
+                    additionalProperties:
+                      anyOf:
+                      - type: integer
+                      - type: string
+                      pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+                      x-kubernetes-int-or-string: true
+                    description: 'Limits describes the maximum amount of compute resources
+                      allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/'
+                    type: object
+                  requests:
+                    additionalProperties:
+                      anyOf:
+                      - type: integer
+                      - type: string
+                      pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+                      x-kubernetes-int-or-string: true
+                    description: 'Requests describes the minimum amount of compute
+                      resources required. If Requests is omitted for a container,
+                      it defaults to Limits if that is explicitly specified, otherwise
+                      to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/'
+                    type: object
+                type: object
+              serviceType:
+                default: LoadBalancer
+                description: ServiceType specifies the type of managed Service that
+                  will be created to expose the dataplane proxies to traffic from
+                  outside the cluster. The ports to expose will be taken from the
+                  matching Gateway resource. If there is no matching Gateway, the
+                  managed Service will be deleted.
+                enum:
+                - LoadBalancer
+                - ClusterIP
+                - NodePort
+                type: string
+              tags:
+                additionalProperties:
+                  type: string
+                description: Tags specifies the Kuma tags that are propagated to the
+                  managed dataplane proxies. These tags should include exactly one
+                  `kuma.io/service` tag, and should match exactly one Gateway resource.
+                type: object
+            type: object
+          status:
+            description: GatewayInstanceStatus holds information about the status
+              of the gateway instance.
+            properties:
+              conditions:
+                description: Conditions is an array of gateway instance conditions.
+                items:
+                  description: "Condition contains details for one aspect of the current
+                    state of this API Resource. --- This struct is intended for direct
+                    use as an array at the field path .status.conditions.  For example,
+                    type FooStatus struct{     // Represents the observations of a
+                    foo's current state.     // Known .status.conditions.type are:
+                    \"Available\", \"Progressing\", and \"Degraded\"     // +patchMergeKey=type
+                    \    // +patchStrategy=merge     // +listType=map     // +listMapKey=type
+                    \    Conditions []metav1.Condition `json:\"conditions,omitempty\"
+                    patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`
+                    \n     // other fields }"
+                  properties:
+                    lastTransitionTime:
+                      description: lastTransitionTime is the last time the condition
+                        transitioned from one status to another. This should be when
+                        the underlying condition changed.  If that is not known, then
+                        using the time when the API field changed is acceptable.
+                      format: date-time
+                      type: string
+                    message:
+                      description: message is a human readable message indicating
+                        details about the transition. This may be an empty string.
+                      maxLength: 32768
+                      type: string
+                    observedGeneration:
+                      description: observedGeneration represents the .metadata.generation
+                        that the condition was set based upon. For instance, if .metadata.generation
+                        is currently 12, but the .status.conditions[x].observedGeneration
+                        is 9, the condition is out of date with respect to the current
+                        state of the instance.
+                      format: int64
+                      minimum: 0
+                      type: integer
+                    reason:
+                      description: reason contains a programmatic identifier indicating
+                        the reason for the condition's last transition. Producers
+                        of specific condition types may define expected values and
+                        meanings for this field, and whether the values are considered
+                        a guaranteed API. The value should be a CamelCase string.
+                        This field may not be empty.
+                      maxLength: 1024
+                      minLength: 1
+                      pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
+                      type: string
+                    status:
+                      description: status of the condition, one of True, False, Unknown.
+                      enum:
+                      - "True"
+                      - "False"
+                      - Unknown
+                      type: string
+                    type:
+                      description: type of condition in CamelCase or in foo.example.com/CamelCase.
+                        --- Many .condition.type values are consistent across resources
+                        like Available, but because arbitrary conditions can be useful
+                        (see .node.status.conditions), the ability to deconflict is
+                        important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt)
+                      maxLength: 316
+                      pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
+                      type: string
+                  required:
+                  - lastTransitionTime
+                  - message
+                  - reason
+                  - status
+                  - type
+                  type: object
+                type: array
+                x-kubernetes-list-map-keys:
+                - type
+                x-kubernetes-list-type: map
+              loadBalancer:
+                description: LoadBalancer contains the current status of the load-balancer,
+                  if one is present.
+                properties:
+                  ingress:
+                    description: Ingress is a list containing ingress points for the
+                      load-balancer. Traffic intended for the service should be sent
+                      to these ingress points.
+                    items:
+                      description: 'LoadBalancerIngress represents the status of a
+                        load-balancer ingress point: traffic intended for the service
+                        should be sent to an ingress point.'
+                      properties:
+                        hostname:
+                          description: Hostname is set for load-balancer ingress points
+                            that are DNS based (typically AWS load-balancers)
+                          type: string
+                        ip:
+                          description: IP is set for load-balancer ingress points
+                            that are IP based (typically GCE or OpenStack load-balancers)
+                          type: string
+                        ports:
+                          description: Ports is a list of records of service ports
+                            If used, every port defined in the service should have
+                            an entry in it
+                          items:
+                            properties:
+                              error:
+                                description: 'Error is to record the problem with
+                                  the service port The format of the error shall comply
+                                  with the following rules: - built-in error values
+                                  shall be specified in this file and those shall
+                                  use   CamelCase names - cloud provider specific
+                                  error values must have names that comply with the   format
+                                  foo.example.com/CamelCase. --- The regex it matches
+                                  is (dns1123SubdomainFmt/)?(qualifiedNameFmt)'
+                                maxLength: 316
+                                pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
+                                type: string
+                              port:
+                                description: Port is the port number of the service
+                                  port of which status is recorded here
+                                format: int32
+                                type: integer
+                              protocol:
+                                default: TCP
+                                description: 'Protocol is the protocol of the service
+                                  port of which status is recorded here The supported
+                                  values are: "TCP", "UDP", "SCTP"'
+                                type: string
+                            required:
+                            - port
+                            - protocol
+                            type: object
+                          type: array
+                          x-kubernetes-list-type: atomic
+                      type: object
+                    type: array
+                type: object
+            type: object
+        type: object
+    served: true
+    storage: true
+    subresources:
+      status: {}
+status:
+  acceptedNames:
+    kind: ""
+    plural: ""
+  conditions: []
+  storedVersions: []
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
 metadata:
   creationTimestamp: null
   name: gatewayroutes.kuma.io
@@ -627,30 +893,6 @@ spec:
               type: object
           type: object
 ---
-apiVersion: apiextensions.k8s.io/v1
-kind: CustomResourceDefinition
-metadata:
-  creationTimestamp: null
-  name: meshes.kuma.io
-spec:
-  group: kuma.io
-  names:
-    kind: Mesh
-    plural: meshes
-  scope: Cluster
-  versions:
-    - name: v1alpha1
-      served: true
-      storage: true
-      schema:
-        openAPIV3Schema:
-          description: Mesh is the Schema for the meshes API
-          properties:
-            spec:
-              x-kubernetes-preserve-unknown-fields: true
-              type: object
-          type: object
----
 apiVersion: rbac.authorization.k8s.io/v1
 kind: ClusterRole
 metadata:
diff --git a/app/kumactl/cmd/install/testdata/install-crds.all.golden.yaml b/app/kumactl/cmd/install/testdata/install-crds.all.golden.yaml
index 86d644ee7..6c0b6c382 100644
--- a/app/kumactl/cmd/install/testdata/install-crds.all.golden.yaml
+++ b/app/kumactl/cmd/install/testdata/install-crds.all.golden.yaml
@@ -131,6 +131,248 @@ spec:
 ---
 apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
+metadata:
+  annotations:
+    controller-gen.kubebuilder.io/version: v0.7.0
+  creationTimestamp: null
+  name: gatewayinstances.kuma.io
+spec:
+  group: kuma.io
+  names:
+    kind: GatewayInstance
+    listKind: GatewayInstanceList
+    plural: gatewayinstances
+    singular: gatewayinstance
+  scope: Namespaced
+  versions:
+  - name: v1alpha1
+    schema:
+      openAPIV3Schema:
+        description: GatewayInstance represents a managed instance of a dataplane
+          proxy for a Kuma Gateway.
+        properties:
+          apiVersion:
+            description: 'APIVersion defines the versioned schema of this representation
+              of an object. Servers should convert recognized schemas to the latest
+              internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
+            type: string
+          kind:
+            description: 'Kind is a string value representing the REST resource this
+              object represents. Servers may infer this from the endpoint the client
+              submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
+            type: string
+          metadata:
+            type: object
+          spec:
+            description: GatewayInstanceSpec specifies the options available for a
+              GatewayDataplane.
+            properties:
+              replicas:
+                default: 1
+                description: Replicas is the number of dataplane proxy replicas to
+                  create. For now this is a fixed number, but in the future it could
+                  be automatically scaled based on metrics.
+                format: int32
+                minimum: 1
+                type: integer
+              resources:
+                description: Resources specifies the compute resources for the proxy
+                  container. The default can be set in the control plane config.
+                properties:
+                  limits:
+                    additionalProperties:
+                      anyOf:
+                      - type: integer
+                      - type: string
+                      pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+                      x-kubernetes-int-or-string: true
+                    description: 'Limits describes the maximum amount of compute resources
+                      allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/'
+                    type: object
+                  requests:
+                    additionalProperties:
+                      anyOf:
+                      - type: integer
+                      - type: string
+                      pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+                      x-kubernetes-int-or-string: true
+                    description: 'Requests describes the minimum amount of compute
+                      resources required. If Requests is omitted for a container,
+                      it defaults to Limits if that is explicitly specified, otherwise
+                      to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/'
+                    type: object
+                type: object
+              serviceType:
+                default: LoadBalancer
+                description: ServiceType specifies the type of managed Service that
+                  will be created to expose the dataplane proxies to traffic from
+                  outside the cluster. The ports to expose will be taken from the
+                  matching Gateway resource. If there is no matching Gateway, the
+                  managed Service will be deleted.
+                enum:
+                - LoadBalancer
+                - ClusterIP
+                - NodePort
+                type: string
+              tags:
+                additionalProperties:
+                  type: string
+                description: Tags specifies the Kuma tags that are propagated to the
+                  managed dataplane proxies. These tags should include exactly one
+                  `kuma.io/service` tag, and should match exactly one Gateway resource.
+                type: object
+            type: object
+          status:
+            description: GatewayInstanceStatus holds information about the status
+              of the gateway instance.
+            properties:
+              conditions:
+                description: Conditions is an array of gateway instance conditions.
+                items:
+                  description: "Condition contains details for one aspect of the current
+                    state of this API Resource. --- This struct is intended for direct
+                    use as an array at the field path .status.conditions.  For example,
+                    type FooStatus struct{     // Represents the observations of a
+                    foo's current state.     // Known .status.conditions.type are:
+                    \"Available\", \"Progressing\", and \"Degraded\"     // +patchMergeKey=type
+                    \    // +patchStrategy=merge     // +listType=map     // +listMapKey=type
+                    \    Conditions []metav1.Condition `json:\"conditions,omitempty\"
+                    patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`
+                    \n     // other fields }"
+                  properties:
+                    lastTransitionTime:
+                      description: lastTransitionTime is the last time the condition
+                        transitioned from one status to another. This should be when
+                        the underlying condition changed.  If that is not known, then
+                        using the time when the API field changed is acceptable.
+                      format: date-time
+                      type: string
+                    message:
+                      description: message is a human readable message indicating
+                        details about the transition. This may be an empty string.
+                      maxLength: 32768
+                      type: string
+                    observedGeneration:
+                      description: observedGeneration represents the .metadata.generation
+                        that the condition was set based upon. For instance, if .metadata.generation
+                        is currently 12, but the .status.conditions[x].observedGeneration
+                        is 9, the condition is out of date with respect to the current
+                        state of the instance.
+                      format: int64
+                      minimum: 0
+                      type: integer
+                    reason:
+                      description: reason contains a programmatic identifier indicating
+                        the reason for the condition's last transition. Producers
+                        of specific condition types may define expected values and
+                        meanings for this field, and whether the values are considered
+                        a guaranteed API. The value should be a CamelCase string.
+                        This field may not be empty.
+                      maxLength: 1024
+                      minLength: 1
+                      pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
+                      type: string
+                    status:
+                      description: status of the condition, one of True, False, Unknown.
+                      enum:
+                      - "True"
+                      - "False"
+                      - Unknown
+                      type: string
+                    type:
+                      description: type of condition in CamelCase or in foo.example.com/CamelCase.
+                        --- Many .condition.type values are consistent across resources
+                        like Available, but because arbitrary conditions can be useful
+                        (see .node.status.conditions), the ability to deconflict is
+                        important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt)
+                      maxLength: 316
+                      pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
+                      type: string
+                  required:
+                  - lastTransitionTime
+                  - message
+                  - reason
+                  - status
+                  - type
+                  type: object
+                type: array
+                x-kubernetes-list-map-keys:
+                - type
+                x-kubernetes-list-type: map
+              loadBalancer:
+                description: LoadBalancer contains the current status of the load-balancer,
+                  if one is present.
+                properties:
+                  ingress:
+                    description: Ingress is a list containing ingress points for the
+                      load-balancer. Traffic intended for the service should be sent
+                      to these ingress points.
+                    items:
+                      description: 'LoadBalancerIngress represents the status of a
+                        load-balancer ingress point: traffic intended for the service
+                        should be sent to an ingress point.'
+                      properties:
+                        hostname:
+                          description: Hostname is set for load-balancer ingress points
+                            that are DNS based (typically AWS load-balancers)
+                          type: string
+                        ip:
+                          description: IP is set for load-balancer ingress points
+                            that are IP based (typically GCE or OpenStack load-balancers)
+                          type: string
+                        ports:
+                          description: Ports is a list of records of service ports
+                            If used, every port defined in the service should have
+                            an entry in it
+                          items:
+                            properties:
+                              error:
+                                description: 'Error is to record the problem with
+                                  the service port The format of the error shall comply
+                                  with the following rules: - built-in error values
+                                  shall be specified in this file and those shall
+                                  use   CamelCase names - cloud provider specific
+                                  error values must have names that comply with the   format
+                                  foo.example.com/CamelCase. --- The regex it matches
+                                  is (dns1123SubdomainFmt/)?(qualifiedNameFmt)'
+                                maxLength: 316
+                                pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
+                                type: string
+                              port:
+                                description: Port is the port number of the service
+                                  port of which status is recorded here
+                                format: int32
+                                type: integer
+                              protocol:
+                                default: TCP
+                                description: 'Protocol is the protocol of the service
+                                  port of which status is recorded here The supported
+                                  values are: "TCP", "UDP", "SCTP"'
+                                type: string
+                            required:
+                            - port
+                            - protocol
+                            type: object
+                          type: array
+                          x-kubernetes-list-type: atomic
+                      type: object
+                    type: array
+                type: object
+            type: object
+        type: object
+    served: true
+    storage: true
+    subresources:
+      status: {}
+status:
+  acceptedNames:
+    kind: ""
+    plural: ""
+  conditions: []
+  storedVersions: []
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
 metadata:
   creationTimestamp: null
   name: gatewayroutes.kuma.io
diff --git a/pkg/api-server/config_ws_test.go b/pkg/api-server/config_ws_test.go
index efd95ec35..4b5f94cb1 100644
--- a/pkg/api-server/config_ws_test.go
+++ b/pkg/api-server/config_ws_test.go
@@ -133,7 +133,6 @@ var _ = Describe("Config WS", func() {
 		  },
 		  "metrics": {
 			"dataplane": {
-			  "enabled": true,
 			  "subscriptionLimit": 2,
 			  "idleTimeout": "5m0s"
 			},
@@ -142,7 +141,6 @@ var _ = Describe("Config WS", func() {
 			  "minResyncTimeout": "1s"
 			},
 			"zone": {
-			  "enabled": true,
 			  "subscriptionLimit": 10,
 			  "idleTimeout": "5m0s"
 			}
diff --git a/pkg/config/loader_test.go b/pkg/config/loader_test.go
index e25c70063..c194dc52b 100644
--- a/pkg/config/loader_test.go
+++ b/pkg/config/loader_test.go
@@ -218,12 +218,10 @@ var _ = Describe("Config loader", func() {
 			Expect(cfg.XdsServer.DataplaneConfigurationRefreshInterval).To(Equal(21 * time.Second))
 			Expect(cfg.XdsServer.NACKBackoff).To(Equal(10 * time.Second))
 
-			Expect(cfg.Metrics.Zone.Enabled).To(BeFalse())
 			Expect(cfg.Metrics.Zone.SubscriptionLimit).To(Equal(23))
 			Expect(cfg.Metrics.Zone.IdleTimeout).To(Equal(2 * time.Minute))
 			Expect(cfg.Metrics.Mesh.MinResyncTimeout).To(Equal(35 * time.Second))
 			Expect(cfg.Metrics.Mesh.MaxResyncTimeout).To(Equal(27 * time.Second))
-			Expect(cfg.Metrics.Dataplane.Enabled).To(BeFalse())
 			Expect(cfg.Metrics.Dataplane.SubscriptionLimit).To(Equal(47))
 			Expect(cfg.Metrics.Dataplane.IdleTimeout).To(Equal(1 * time.Minute))
 
@@ -418,7 +416,6 @@ xdsServer:
   nackBackoff: 10s
 metrics:
   zone:
-    enabled: false
     subscriptionLimit: 23
     idleTimeout: 2m
   mesh:
@@ -426,7 +423,6 @@ metrics:
     maxResyncTimeout: 27s
   dataplane:
     subscriptionLimit: 47
-    enabled: false
     idleTimeout: 1m
 dpServer:
   tlsCertFile: /test/path
@@ -573,11 +569,9 @@ access:
 				"KUMA_XDS_SERVER_DATAPLANE_STATUS_FLUSH_INTERVAL":                                          "7s",
 				"KUMA_XDS_SERVER_DATAPLANE_CONFIGURATION_REFRESH_INTERVAL":                                 "21s",
 				"KUMA_XDS_SERVER_NACK_BACKOFF":                                                             "10s",
-				"KUMA_METRICS_ZONE_ENABLED":                                                                "false",
 				"KUMA_METRICS_ZONE_SUBSCRIPTION_LIMIT":                                                     "23",
 				"KUMA_METRICS_ZONE_IDLE_TIMEOUT":                                                           "2m",
 				"KUMA_METRICS_MESH_MAX_RESYNC_TIMEOUT":                                                     "27s",
-				"KUMA_METRICS_DATAPLANE_ENABLED":                                                           "false",
 				"KUMA_METRICS_MESH_MIN_RESYNC_TIMEOUT":                                                     "35s",
 				"KUMA_METRICS_DATAPLANE_SUBSCRIPTION_LIMIT":                                                "47",
 				"KUMA_METRICS_DATAPLANE_IDLE_TIMEOUT":                                                      "1m",
diff --git a/pkg/config/plugins/runtime/k8s/testdata/default-config.golden.yaml b/pkg/config/plugins/runtime/k8s/testdata/default-config.golden.yaml
index 0bbc816ff..92bd40c2d 100644
--- a/pkg/config/plugins/runtime/k8s/testdata/default-config.golden.yaml
+++ b/pkg/config/plugins/runtime/k8s/testdata/default-config.golden.yaml
@@ -5,9 +5,6 @@ admissionServer:
 injector:
   sidecarContainer:
     image: kuma/kuma-dp:latest
-    redirectPortInbound: 15006
-    redirectPortInboundV6: 15010
-    redirectPortOutbound: 15001
     uid: 5678
     gid: 5678
     adminPort: 9901
@@ -31,6 +28,9 @@ injector:
         cpu: 1000m
         memory: 512Mi
     envVars: {}
+    redirectPortInbound: 15006
+    redirectPortInboundV6: 15010
+    redirectPortOutbound: 15001
   initContainer:
     image: kuma/kuma-init:latest
   cniEnabled: false
diff --git a/pkg/core/managers/apis/dataplaneinsight/dataplane_insight_manager_test.go b/pkg/core/managers/apis/dataplaneinsight/dataplane_insight_manager_test.go
index ff8c5962c..58133c7d5 100644
--- a/pkg/core/managers/apis/dataplaneinsight/dataplane_insight_manager_test.go
+++ b/pkg/core/managers/apis/dataplaneinsight/dataplane_insight_manager_test.go
@@ -21,7 +21,6 @@ var _ = Describe("DataplaneInsight Manager", func() {
 		// setup
 		s := memory.NewStore()
 		cfg := &kuma_cp.DataplaneMetrics{
-			Enabled:           true,
 			SubscriptionLimit: 3,
 		}
 		manager := dataplaneinsight.NewDataplaneInsightManager(s, cfg)
@@ -51,11 +50,11 @@ var _ = Describe("DataplaneInsight Manager", func() {
 		Expect(actual.Spec.Subscriptions[2].Id).To(Equal("9"))
 	})
 
-	It("should cleanup subscriptions if disabled", func() {
+	It("should have 0 subscriptions if limit is 0", func() {
 		// setup
 		s := memory.NewStore()
 		cfg := &kuma_cp.DataplaneMetrics{
-			Enabled: false,
+			SubscriptionLimit: 0,
 		}
 		manager := dataplaneinsight.NewDataplaneInsightManager(s, cfg)
 
diff --git a/pkg/core/managers/apis/zoneingressinsight/zone_ingress_insight_manager_test.go b/pkg/core/managers/apis/zoneingressinsight/zone_ingress_insight_manager_test.go
index c5d9ebe53..921cdac36 100644
--- a/pkg/core/managers/apis/zoneingressinsight/zone_ingress_insight_manager_test.go
+++ b/pkg/core/managers/apis/zoneingressinsight/zone_ingress_insight_manager_test.go
@@ -22,7 +22,6 @@ var _ = Describe("ZoneIngressInsight Manager", func() {
 		// setup
 		s := memory.NewStore()
 		cfg := &kuma_cp.DataplaneMetrics{
-			Enabled:           true,
 			SubscriptionLimit: 3,
 		}
 		manager := zoneingressinsight.NewZoneIngressInsightManager(s, cfg)
@@ -52,11 +51,11 @@ var _ = Describe("ZoneIngressInsight Manager", func() {
 		Expect(actual.Spec.Subscriptions[2].Id).To(Equal("9"))
 	})
 
-	It("should cleanup subscriptions if disabled", func() {
+	It("should cleanup subscriptions if limit is 0", func() {
 		// setup
 		s := memory.NewStore()
 		cfg := &kuma_cp.DataplaneMetrics{
-			Enabled: false,
+			SubscriptionLimit: 0,
 		}
 		manager := zoneingressinsight.NewZoneIngressInsightManager(s, cfg)
 
diff --git a/pkg/core/managers/apis/zoneinsight/zone_insight_manager_test.go b/pkg/core/managers/apis/zoneinsight/zone_insight_manager_test.go
index 06c06399d..4cc17569d 100644
--- a/pkg/core/managers/apis/zoneinsight/zone_insight_manager_test.go
+++ b/pkg/core/managers/apis/zoneinsight/zone_insight_manager_test.go
@@ -22,7 +22,6 @@ var _ = Describe("ZoneInsight Manager", func() {
 		// setup
 		s := memory.NewStore()
 		cfg := &kuma_cp.ZoneMetrics{
-			Enabled:           true,
 			SubscriptionLimit: 3,
 		}
 		manager := zoneinsight.NewZoneInsightManager(s, cfg)
@@ -52,11 +51,11 @@ var _ = Describe("ZoneInsight Manager", func() {
 		Expect(actual.Spec.Subscriptions[2].Id).To(Equal("9"))
 	})
 
-	It("should cleanup subscriptions if disabled", func() {
+	It("should cleanup subscriptions if limit is 0", func() {
 		// setup
 		s := memory.NewStore()
 		cfg := &kuma_cp.ZoneMetrics{
-			Enabled: false,
+			SubscriptionLimit: 0,
 		}
 		manager := zoneinsight.NewZoneInsightManager(s, cfg)