diff --git a/.gitignore b/.gitignore
index 64ec9b3ace..52601151a8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,3 +2,4 @@
 /vendor/
 tools
 /testing/kuttl/e2e-generated*/
+postgres_exporter.tar.gz
diff --git a/internal/controller/postgrescluster/cluster.go b/internal/controller/postgrescluster/cluster.go
index 6ab5a720c5..296aa41341 100644
--- a/internal/controller/postgrescluster/cluster.go
+++ b/internal/controller/postgrescluster/cluster.go
@@ -25,6 +25,7 @@ import (
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/util/intstr"
 
+	"github.com/crunchydata/postgres-operator/internal/initialize"
 	"github.com/crunchydata/postgres-operator/internal/naming"
 	"github.com/crunchydata/postgres-operator/internal/patroni"
 	"github.com/crunchydata/postgres-operator/internal/pki"
@@ -143,10 +144,11 @@ func (r *Reconciler) generateClusterPrimaryService(
 	service.Spec.Selector = nil
 
 	service.Spec.Ports = []corev1.ServicePort{{
-		Name:       naming.PortPostgreSQL,
-		Port:       *cluster.Spec.Port,
-		Protocol:   corev1.ProtocolTCP,
-		TargetPort: intstr.FromString(naming.PortPostgreSQL),
+		Name:        naming.PortPostgreSQL,
+		Port:        *cluster.Spec.Port,
+		Protocol:    corev1.ProtocolTCP,
+		TargetPort:  intstr.FromString(naming.PortPostgreSQL),
+		AppProtocol: initialize.String(postgres.IANAServiceName),
 	}}
 
 	// Resolve to the ClusterIP for which Patroni has configured the Endpoints.
@@ -158,9 +160,10 @@ func (r *Reconciler) generateClusterPrimaryService(
 	for _, sp := range service.Spec.Ports {
 		endpoints.Subsets[0].Ports = append(endpoints.Subsets[0].Ports,
 			corev1.EndpointPort{
-				Name:     sp.Name,
-				Port:     sp.Port,
-				Protocol: sp.Protocol,
+				Name:        sp.Name,
+				Port:        sp.Port,
+				Protocol:    sp.Protocol,
+				AppProtocol: sp.AppProtocol,
 			})
 	}
 
@@ -221,10 +224,11 @@ func (r *Reconciler) generateClusterReplicaService(
 	// ContainerPort. This name allows the port number to differ between Pods,
 	// which can happen during a rolling update.
 	service.Spec.Ports = []corev1.ServicePort{{
-		Name:       naming.PortPostgreSQL,
-		Port:       *cluster.Spec.Port,
-		Protocol:   corev1.ProtocolTCP,
-		TargetPort: intstr.FromString(naming.PortPostgreSQL),
+		Name:        naming.PortPostgreSQL,
+		Port:        *cluster.Spec.Port,
+		Protocol:    corev1.ProtocolTCP,
+		TargetPort:  intstr.FromString(naming.PortPostgreSQL),
+		AppProtocol: initialize.String(postgres.IANAServiceName),
 	}}
 
 	err := errors.WithStack(r.setControllerReference(cluster, service))
diff --git a/internal/controller/postgrescluster/cluster_test.go b/internal/controller/postgrescluster/cluster_test.go
index e6f4318f75..e4cfb965d0 100644
--- a/internal/controller/postgrescluster/cluster_test.go
+++ b/internal/controller/postgrescluster/cluster_test.go
@@ -634,7 +634,8 @@ ownerReferences:
   uid: ""
 		`))
 		assert.Assert(t, marshalMatches(service.Spec.Ports, `
-- name: postgres
+- appProtocol: postgresql
+  name: postgres
   port: 2600
   protocol: TCP
   targetPort: postgres
@@ -665,7 +666,8 @@ subsets:
 - addresses:
   - ip: 1.9.8.3
   ports:
-  - name: postgres
+  - appProtocol: postgresql
+    name: postgres
     port: 2600
     protocol: TCP
 		`))
@@ -753,7 +755,8 @@ ownerReferences:
 	`))
 	assert.Assert(t, marshalMatches(service.Spec, `
 ports:
-- name: postgres
+- appProtocol: postgresql
+  name: postgres
   port: 9876
   protocol: TCP
   targetPort: postgres
diff --git a/internal/controller/postgrescluster/patroni.go b/internal/controller/postgrescluster/patroni.go
index c01e8ce824..9242dce9e5 100644
--- a/internal/controller/postgrescluster/patroni.go
+++ b/internal/controller/postgrescluster/patroni.go
@@ -262,10 +262,11 @@ func (r *Reconciler) generatePatroniLeaderLeaseService(
 	// ContainerPort. This name allows the port number to differ between
 	// instances, which can happen during a rolling update.
 	servicePort := corev1.ServicePort{
-		Name:       naming.PortPostgreSQL,
-		Port:       *cluster.Spec.Port,
-		Protocol:   corev1.ProtocolTCP,
-		TargetPort: intstr.FromString(naming.PortPostgreSQL),
+		Name:        naming.PortPostgreSQL,
+		Port:        *cluster.Spec.Port,
+		Protocol:    corev1.ProtocolTCP,
+		TargetPort:  intstr.FromString(naming.PortPostgreSQL),
+		AppProtocol: initialize.String(postgres.IANAServiceName),
 	}
 
 	if spec := cluster.Spec.Service; spec == nil {
diff --git a/internal/controller/postgrescluster/patroni_test.go b/internal/controller/postgrescluster/patroni_test.go
index ebcb40cde6..1dc4b0193a 100644
--- a/internal/controller/postgrescluster/patroni_test.go
+++ b/internal/controller/postgrescluster/patroni_test.go
@@ -93,7 +93,8 @@ ownerReferences:
 		// Defaults to ClusterIP.
 		assert.Equal(t, service.Spec.Type, corev1.ServiceTypeClusterIP)
 		assert.Assert(t, marshalMatches(service.Spec.Ports, `
-- name: postgres
+- appProtocol: postgresql
+  name: postgres
   port: 9876
   protocol: TCP
   targetPort: postgres
@@ -182,7 +183,8 @@ ownerReferences:
 			alwaysExpect(t, service)
 			test.Expect(t, service)
 			assert.Assert(t, marshalMatches(service.Spec.Ports, `
-- name: postgres
+- appProtocol: postgresql
+  name: postgres
   port: 9876
   protocol: TCP
   targetPort: postgres
@@ -207,7 +209,8 @@ ownerReferences:
 				alwaysExpect(t, service)
 				assert.Equal(t, service.Spec.Type, corev1.ServiceTypeNodePort)
 				assert.Assert(t, marshalMatches(service.Spec.Ports, `
-- name: postgres
+- appProtocol: postgresql
+  name: postgres
   nodePort: 32001
   port: 9876
   protocol: TCP
@@ -220,7 +223,8 @@ ownerReferences:
 				assert.NilError(t, err)
 				alwaysExpect(t, service)
 				assert.Assert(t, marshalMatches(service.Spec.Ports, `
-- name: postgres
+- appProtocol: postgresql
+  name: postgres
   nodePort: 32002
   port: 9876
   protocol: TCP
diff --git a/internal/controller/postgrescluster/pgadmin.go b/internal/controller/postgrescluster/pgadmin.go
index fc6fe05fa2..ead028baa2 100644
--- a/internal/controller/postgrescluster/pgadmin.go
+++ b/internal/controller/postgrescluster/pgadmin.go
@@ -167,10 +167,11 @@ func (r *Reconciler) generatePGAdminService(
 	// TODO(tjmoore4): A custom service port is not currently supported as this
 	// requires updates to the pgAdmin service configuration.
 	servicePort := corev1.ServicePort{
-		Name:       naming.PortPGAdmin,
-		Port:       *initialize.Int32(5050),
-		Protocol:   corev1.ProtocolTCP,
-		TargetPort: intstr.FromString(naming.PortPGAdmin),
+		Name:        naming.PortPGAdmin,
+		Port:        *initialize.Int32(5050),
+		Protocol:    corev1.ProtocolTCP,
+		TargetPort:  intstr.FromString(naming.PortPGAdmin),
+		AppProtocol: initialize.String(naming.AppProtocolHTTP),
 	}
 
 	if spec := cluster.Spec.UserInterface.PGAdmin.Service; spec == nil {
diff --git a/internal/controller/postgrescluster/pgadmin_test.go b/internal/controller/postgrescluster/pgadmin_test.go
index b985c8e1f1..76b294856f 100644
--- a/internal/controller/postgrescluster/pgadmin_test.go
+++ b/internal/controller/postgrescluster/pgadmin_test.go
@@ -266,7 +266,8 @@ ownerReferences:
 		// Defaults to ClusterIP.
 		assert.Equal(t, service.Spec.Type, corev1.ServiceTypeClusterIP)
 		assert.Assert(t, marshalMatches(service.Spec.Ports, `
-- name: pgadmin
+- appProtocol: http
+  name: pgadmin
   port: 5050
   protocol: TCP
   targetPort: pgadmin
@@ -299,7 +300,8 @@ ownerReferences:
 			alwaysExpect(t, service)
 			test.Expect(t, service)
 			assert.Assert(t, marshalMatches(service.Spec.Ports, `
-- name: pgadmin
+- appProtocol: http
+  name: pgadmin
   port: 5050
   protocol: TCP
   targetPort: pgadmin
@@ -324,7 +326,8 @@ ownerReferences:
 				assert.Equal(t, service.Spec.Type, corev1.ServiceTypeNodePort)
 				alwaysExpect(t, service)
 				assert.Assert(t, marshalMatches(service.Spec.Ports, `
-- name: pgadmin
+- appProtocol: http
+  name: pgadmin
   nodePort: 32001
   port: 5050
   protocol: TCP
@@ -337,7 +340,8 @@ ownerReferences:
 				assert.Equal(t, service.Spec.Type, corev1.ServiceTypeLoadBalancer)
 				alwaysExpect(t, service)
 				assert.Assert(t, marshalMatches(service.Spec.Ports, `
-- name: pgadmin
+- appProtocol: http
+  name: pgadmin
   nodePort: 32002
   port: 5050
   protocol: TCP
diff --git a/internal/controller/postgrescluster/pgbouncer.go b/internal/controller/postgrescluster/pgbouncer.go
index a1e26cdc3c..b306e139bd 100644
--- a/internal/controller/postgrescluster/pgbouncer.go
+++ b/internal/controller/postgrescluster/pgbouncer.go
@@ -291,10 +291,11 @@ func (r *Reconciler) generatePGBouncerService(
 	// ContainerPort. This name allows the port number to differ between Pods,
 	// which can happen during a rolling update.
 	servicePort := corev1.ServicePort{
-		Name:       naming.PortPGBouncer,
-		Port:       *cluster.Spec.Proxy.PGBouncer.Port,
-		Protocol:   corev1.ProtocolTCP,
-		TargetPort: intstr.FromString(naming.PortPGBouncer),
+		Name:        naming.PortPGBouncer,
+		Port:        *cluster.Spec.Proxy.PGBouncer.Port,
+		Protocol:    corev1.ProtocolTCP,
+		TargetPort:  intstr.FromString(naming.PortPGBouncer),
+		AppProtocol: initialize.String(postgres.IANAServiceName),
 	}
 
 	if spec := cluster.Spec.Proxy.PGBouncer.Service; spec == nil {
diff --git a/internal/controller/postgrescluster/pgbouncer_test.go b/internal/controller/postgrescluster/pgbouncer_test.go
index 681680fe93..bd27560d5b 100644
--- a/internal/controller/postgrescluster/pgbouncer_test.go
+++ b/internal/controller/postgrescluster/pgbouncer_test.go
@@ -175,7 +175,8 @@ ownerReferences:
 		// Defaults to ClusterIP.
 		assert.Equal(t, service.Spec.Type, corev1.ServiceTypeClusterIP)
 		assert.Assert(t, marshalMatches(service.Spec.Ports, `
-- name: pgbouncer
+- appProtocol: postgresql
+  name: pgbouncer
   port: 9651
   protocol: TCP
   targetPort: pgbouncer
@@ -208,7 +209,8 @@ ownerReferences:
 			alwaysExpect(t, service)
 			test.Expect(t, service)
 			assert.Assert(t, marshalMatches(service.Spec.Ports, `
-- name: pgbouncer
+- appProtocol: postgresql
+  name: pgbouncer
   port: 9651
   protocol: TCP
   targetPort: pgbouncer
@@ -233,7 +235,8 @@ ownerReferences:
 				assert.Equal(t, service.Spec.Type, corev1.ServiceTypeNodePort)
 				alwaysExpect(t, service)
 				assert.Assert(t, marshalMatches(service.Spec.Ports, `
-- name: pgbouncer
+- appProtocol: postgresql
+  name: pgbouncer
   nodePort: 32001
   port: 9651
   protocol: TCP
@@ -246,7 +249,8 @@ ownerReferences:
 				assert.Equal(t, service.Spec.Type, corev1.ServiceTypeLoadBalancer)
 				alwaysExpect(t, service)
 				assert.Assert(t, marshalMatches(service.Spec.Ports, `
-- name: pgbouncer
+- appProtocol: postgresql
+  name: pgbouncer
   nodePort: 32002
   port: 9651
   protocol: TCP
diff --git a/internal/naming/names.go b/internal/naming/names.go
index 6ef9c1dcfa..677bc76717 100644
--- a/internal/naming/names.go
+++ b/internal/naming/names.go
@@ -186,6 +186,11 @@ const (
 	RestoreConfigCopySuffix = "%s-restorecopy-%d"
 )
 
+const (
+	// AppProtocolHTTP is the name of the appProtocol for Services which use the HTTP protocol
+	AppProtocolHTTP = "http"
+)
+
 // AsObjectKey converts the ObjectMeta API type to a client.ObjectKey.
 // When you have a client.Object, use client.ObjectKeyFromObject() instead.
 func AsObjectKey(m metav1.ObjectMeta) client.ObjectKey {
diff --git a/internal/patroni/config.go b/internal/patroni/config.go
index 205222f102..0fa0edd278 100644
--- a/internal/patroni/config.go
+++ b/internal/patroni/config.go
@@ -334,9 +334,10 @@ func instanceEnvironment(
 			for _, cp := range podContainers[i].Ports {
 				if sp.TargetPort.StrVal == cp.Name {
 					ports = append(ports, corev1.EndpointPort{
-						Name:     sp.Name,
-						Port:     cp.ContainerPort,
-						Protocol: cp.Protocol,
+						Name:        sp.Name,
+						Port:        cp.ContainerPort,
+						Protocol:    cp.Protocol,
+						AppProtocol: sp.AppProtocol,
 					})
 				}
 			}