diff --git a/Documentation/api.md b/Documentation/api.md
index 494acea68..2260cce62 100644
--- a/Documentation/api.md
+++ b/Documentation/api.md
@@ -2794,6 +2794,22 @@ in a breaking way.</p>
 </tr>
 <tr>
 <td>
+<code>serviceDiscoveryRole</code><br/>
+<em>
+<a href="#monitoring.coreos.com/v1.ServiceDiscoveryRole">
+ServiceDiscoveryRole
+</a>
+</em>
+</td>
+<td>
+<em>(Optional)</em>
+<p>Defines the service discovery role used to discover targets from <code>ServiceMonitor</code> objects.
+If set, the value should be either &ldquo;Endpoints&rdquo; or &ldquo;EndpointSlice&rdquo;.
+If unset, the operator assumes the &ldquo;Endpoints&rdquo; role.</p>
+</td>
+</tr>
+<tr>
+<td>
 <code>baseImage</code><br/>
 <em>
 string
@@ -7468,6 +7484,22 @@ PodMonitors, ServiceMonitors, Probes and ScrapeConfigs.</p>
 in a breaking way.</p>
 </td>
 </tr>
+<tr>
+<td>
+<code>serviceDiscoveryRole</code><br/>
+<em>
+<a href="#monitoring.coreos.com/v1.ServiceDiscoveryRole">
+ServiceDiscoveryRole
+</a>
+</em>
+</td>
+<td>
+<em>(Optional)</em>
+<p>Defines the service discovery role used to discover targets from <code>ServiceMonitor</code> objects.
+If set, the value should be either &ldquo;Endpoints&rdquo; or &ldquo;EndpointSlice&rdquo;.
+If unset, the operator assumes the &ldquo;Endpoints&rdquo; role.</p>
+</td>
+</tr>
 </tbody>
 </table>
 <h3 id="monitoring.coreos.com/v1.Condition">Condition
@@ -11644,6 +11676,22 @@ in a breaking way.</p>
 </tr>
 <tr>
 <td>
+<code>serviceDiscoveryRole</code><br/>
+<em>
+<a href="#monitoring.coreos.com/v1.ServiceDiscoveryRole">
+ServiceDiscoveryRole
+</a>
+</em>
+</td>
+<td>
+<em>(Optional)</em>
+<p>Defines the service discovery role used to discover targets from <code>ServiceMonitor</code> objects.
+If set, the value should be either &ldquo;Endpoints&rdquo; or &ldquo;EndpointSlice&rdquo;.
+If unset, the operator assumes the &ldquo;Endpoints&rdquo; role.</p>
+</td>
+</tr>
+<tr>
+<td>
 <code>baseImage</code><br/>
 <em>
 string
@@ -13892,6 +13940,26 @@ Kubernetes core/v1.ConfigMapKeySelector
 </tr>
 </tbody>
 </table>
+<h3 id="monitoring.coreos.com/v1.ServiceDiscoveryRole">ServiceDiscoveryRole
+(<code>string</code> alias)</h3>
+<p>
+(<em>Appears on:</em><a href="#monitoring.coreos.com/v1.CommonPrometheusFields">CommonPrometheusFields</a>)
+</p>
+<div>
+</div>
+<table>
+<thead>
+<tr>
+<th>Value</th>
+<th>Description</th>
+</tr>
+</thead>
+<tbody><tr><td><p>&#34;EndpointSlice&#34;</p></td>
+<td></td>
+</tr><tr><td><p>&#34;Endpoints&#34;</p></td>
+<td></td>
+</tr></tbody>
+</table>
 <h3 id="monitoring.coreos.com/v1.ServiceMonitorSpec">ServiceMonitorSpec
 </h3>
 <p>
@@ -17772,6 +17840,22 @@ PodMonitors, ServiceMonitors, Probes and ScrapeConfigs.</p>
 in a breaking way.</p>
 </td>
 </tr>
+<tr>
+<td>
+<code>serviceDiscoveryRole</code><br/>
+<em>
+<a href="#monitoring.coreos.com/v1.ServiceDiscoveryRole">
+ServiceDiscoveryRole
+</a>
+</em>
+</td>
+<td>
+<em>(Optional)</em>
+<p>Defines the service discovery role used to discover targets from <code>ServiceMonitor</code> objects.
+If set, the value should be either &ldquo;Endpoints&rdquo; or &ldquo;EndpointSlice&rdquo;.
+If unset, the operator assumes the &ldquo;Endpoints&rdquo; role.</p>
+</td>
+</tr>
 </table>
 </td>
 </tr>
@@ -25016,6 +25100,22 @@ PodMonitors, ServiceMonitors, Probes and ScrapeConfigs.</p>
 in a breaking way.</p>
 </td>
 </tr>
+<tr>
+<td>
+<code>serviceDiscoveryRole</code><br/>
+<em>
+<a href="#monitoring.coreos.com/v1.ServiceDiscoveryRole">
+ServiceDiscoveryRole
+</a>
+</em>
+</td>
+<td>
+<em>(Optional)</em>
+<p>Defines the service discovery role used to discover targets from <code>ServiceMonitor</code> objects.
+If set, the value should be either &ldquo;Endpoints&rdquo; or &ldquo;EndpointSlice&rdquo;.
+If unset, the operator assumes the &ldquo;Endpoints&rdquo; role.</p>
+</td>
+</tr>
 </tbody>
 </table>
 <h3 id="monitoring.coreos.com/v1alpha1.PuppetDBSDConfig">PuppetDBSDConfig
diff --git a/Documentation/design.md b/Documentation/design.md
index ff2c78157..a1b879232 100644
--- a/Documentation/design.md
+++ b/Documentation/design.md
@@ -52,17 +52,17 @@ Further information can also be found in the [Thanos section]({{< ref "thanos.md
 
 The `ServiceMonitor` custom resource definition (CRD) allows to declaratively define how a dynamic set of services should be monitored. Which services are selected to be monitored with the desired configuration is defined using label selections. This allows an organization to introduce conventions around how metrics are exposed, and then following these conventions new services are automatically discovered, without the need to reconfigure the system.
 
-For Prometheus to monitor any application within Kubernetes an `Endpoints` object needs to exist. `Endpoints` objects are essentially lists of IP addresses. Typically an `Endpoints` object is populated by a `Service` object. A `Service` object discovers `Pod`s by a label selector and adds those to the `Endpoints` object.
+For Prometheus to monitor any application within Kubernetes, either an `EndpointSlice` object or an `Endpoints` object needs to exist. These objects are essentially lists of IP addresses. Typically an `EndpointSlice` or an `Endpoints` object is populated by a `Service` object. A `Service` object discovers `Pod`s by a label selector and adds those to the `EndpointSlice` or `Endpoints` object.
 
-A `Service` may expose one or more service ports, which are backed by a list of multiple endpoints that point to a `Pod` in the common case. This is reflected in the respective `Endpoints` object as well.
+A `Service` may expose one or more service ports, which are backed by a list of multiple endpoints that point to a `Pod` in the common case. This is reflected in the respective `EndpointSlice` or `Endpoints` object as well.
 
-The `ServiceMonitor` object introduced by the Prometheus Operator in turn discovers those `Endpoints` objects and configures Prometheus to monitor those `Pod`s.
+The `ServiceMonitor` object introduced by the Prometheus Operator in turn discovers those `EndpointSlice` or `Endpoints` objects and configures Prometheus to monitor those `Pod`s.
 
-The `endpoints` section of the `ServiceMonitorSpec`, is used to configure which ports of these `Endpoints` are going to be scraped for metrics, and with which parameters. For advanced use cases one may want to monitor ports of backing `Pod`s, which are not directly part of the service endpoints. Therefore when specifying an endpoint in the `endpoints` section, they are strictly used.
+The `endpoints` section of the `ServiceMonitorSpec`, is used to configure which ports of these `EndpointSlice`s or `Endpoints` are going to be scraped for metrics, and with which parameters. For advanced use cases one may want to monitor ports of backing `Pod`s, which are not directly part of the service endpoints. Therefore when specifying an endpoint in the `endpoints` section, they are strictly used.
 
-> Note: `endpoints` (lowercase) is the field in the `ServiceMonitor` CRD, while `Endpoints` (capitalized) is the Kubernetes object kind.
+> Note: `endpoints` (lowercase) is the field in the `ServiceMonitor` CRD, while `EndpointSlice` or `Endpoints` (capitalized) is the Kubernetes object kind.
 
-Both `ServiceMonitors` as well as discovered targets may come from any namespace. This is important to allow cross-namespace monitoring use cases, e.g. for meta-monitoring. Using the `ServiceMonitorNamespaceSelector` of the `PrometheusSpec`, one can restrict the namespaces `ServiceMonitor`s are selected from by the respective Prometheus server. Using the `namespaceSelector` of the `ServiceMonitorSpec`, one can restrict the namespaces the `Endpoints` objects are allowed to be discovered from.
+Both `ServiceMonitors` as well as discovered targets may come from any namespace. This is important to allow cross-namespace monitoring use cases, e.g. for meta-monitoring. Using the `ServiceMonitorNamespaceSelector` of the `PrometheusSpec`, one can restrict the namespaces `ServiceMonitor`s are selected from by the respective Prometheus server. Using the `namespaceSelector` of the `ServiceMonitorSpec`, one can restrict the namespaces the `EndpointSlice` or `Endpoints` objects are allowed to be discovered from.
 
 One can discover targets in all namespaces like this:
 
diff --git a/bundle.yaml b/bundle.yaml
index d03012cde..4390b7987 100644
--- a/bundle.yaml
+++ b/bundle.yaml
@@ -23784,6 +23784,15 @@ spec:
                   ServiceAccountName is the name of the ServiceAccount to use to run the
                   Prometheus Pods.
                 type: string
+              serviceDiscoveryRole:
+                description: |-
+                  Defines the service discovery role used to discover targets from `ServiceMonitor` objects.
+                  If set, the value should be either "Endpoints" or "EndpointSlice".
+                  If unset, the operator assumes the "Endpoints" role.
+                enum:
+                - Endpoints
+                - EndpointSlice
+                type: string
               serviceMonitorNamespaceSelector:
                 description: |-
                   Namespaces to match for ServicedMonitors discovery. An empty label selector
@@ -35627,6 +35636,15 @@ spec:
                   ServiceAccountName is the name of the ServiceAccount to use to run the
                   Prometheus Pods.
                 type: string
+              serviceDiscoveryRole:
+                description: |-
+                  Defines the service discovery role used to discover targets from `ServiceMonitor` objects.
+                  If set, the value should be either "Endpoints" or "EndpointSlice".
+                  If unset, the operator assumes the "Endpoints" role.
+                enum:
+                - Endpoints
+                - EndpointSlice
+                type: string
               serviceMonitorNamespaceSelector:
                 description: |-
                   Namespaces to match for ServicedMonitors discovery. An empty label selector
diff --git a/example/prometheus-operator-crd-full/monitoring.coreos.com_prometheusagents.yaml b/example/prometheus-operator-crd-full/monitoring.coreos.com_prometheusagents.yaml
index 8359dcdb8..ec206cae0 100644
--- a/example/prometheus-operator-crd-full/monitoring.coreos.com_prometheusagents.yaml
+++ b/example/prometheus-operator-crd-full/monitoring.coreos.com_prometheusagents.yaml
@@ -6825,6 +6825,15 @@ spec:
                   ServiceAccountName is the name of the ServiceAccount to use to run the
                   Prometheus Pods.
                 type: string
+              serviceDiscoveryRole:
+                description: |-
+                  Defines the service discovery role used to discover targets from `ServiceMonitor` objects.
+                  If set, the value should be either "Endpoints" or "EndpointSlice".
+                  If unset, the operator assumes the "Endpoints" role.
+                enum:
+                - Endpoints
+                - EndpointSlice
+                type: string
               serviceMonitorNamespaceSelector:
                 description: |-
                   Namespaces to match for ServicedMonitors discovery. An empty label selector
diff --git a/example/prometheus-operator-crd-full/monitoring.coreos.com_prometheuses.yaml b/example/prometheus-operator-crd-full/monitoring.coreos.com_prometheuses.yaml
index 353b41d1c..e98ad973c 100644
--- a/example/prometheus-operator-crd-full/monitoring.coreos.com_prometheuses.yaml
+++ b/example/prometheus-operator-crd-full/monitoring.coreos.com_prometheuses.yaml
@@ -8302,6 +8302,15 @@ spec:
                   ServiceAccountName is the name of the ServiceAccount to use to run the
                   Prometheus Pods.
                 type: string
+              serviceDiscoveryRole:
+                description: |-
+                  Defines the service discovery role used to discover targets from `ServiceMonitor` objects.
+                  If set, the value should be either "Endpoints" or "EndpointSlice".
+                  If unset, the operator assumes the "Endpoints" role.
+                enum:
+                - Endpoints
+                - EndpointSlice
+                type: string
               serviceMonitorNamespaceSelector:
                 description: |-
                   Namespaces to match for ServicedMonitors discovery. An empty label selector
diff --git a/example/prometheus-operator-crd/monitoring.coreos.com_prometheusagents.yaml b/example/prometheus-operator-crd/monitoring.coreos.com_prometheusagents.yaml
index 6fc1b51a7..f304ec7de 100644
--- a/example/prometheus-operator-crd/monitoring.coreos.com_prometheusagents.yaml
+++ b/example/prometheus-operator-crd/monitoring.coreos.com_prometheusagents.yaml
@@ -6826,6 +6826,15 @@ spec:
                   ServiceAccountName is the name of the ServiceAccount to use to run the
                   Prometheus Pods.
                 type: string
+              serviceDiscoveryRole:
+                description: |-
+                  Defines the service discovery role used to discover targets from `ServiceMonitor` objects.
+                  If set, the value should be either "Endpoints" or "EndpointSlice".
+                  If unset, the operator assumes the "Endpoints" role.
+                enum:
+                - Endpoints
+                - EndpointSlice
+                type: string
               serviceMonitorNamespaceSelector:
                 description: |-
                   Namespaces to match for ServicedMonitors discovery. An empty label selector
diff --git a/example/prometheus-operator-crd/monitoring.coreos.com_prometheuses.yaml b/example/prometheus-operator-crd/monitoring.coreos.com_prometheuses.yaml
index 972ebc2d6..9139debe0 100644
--- a/example/prometheus-operator-crd/monitoring.coreos.com_prometheuses.yaml
+++ b/example/prometheus-operator-crd/monitoring.coreos.com_prometheuses.yaml
@@ -8303,6 +8303,15 @@ spec:
                   ServiceAccountName is the name of the ServiceAccount to use to run the
                   Prometheus Pods.
                 type: string
+              serviceDiscoveryRole:
+                description: |-
+                  Defines the service discovery role used to discover targets from `ServiceMonitor` objects.
+                  If set, the value should be either "Endpoints" or "EndpointSlice".
+                  If unset, the operator assumes the "Endpoints" role.
+                enum:
+                - Endpoints
+                - EndpointSlice
+                type: string
               serviceMonitorNamespaceSelector:
                 description: |-
                   Namespaces to match for ServicedMonitors discovery. An empty label selector
diff --git a/jsonnet/prometheus-operator/prometheusagents-crd.json b/jsonnet/prometheus-operator/prometheusagents-crd.json
index 4ad0b2d88..ff1955f51 100644
--- a/jsonnet/prometheus-operator/prometheusagents-crd.json
+++ b/jsonnet/prometheus-operator/prometheusagents-crd.json
@@ -5590,6 +5590,14 @@
                     "description": "ServiceAccountName is the name of the ServiceAccount to use to run the\nPrometheus Pods.",
                     "type": "string"
                   },
+                  "serviceDiscoveryRole": {
+                    "description": "Defines the service discovery role used to discover targets from `ServiceMonitor` objects.\nIf set, the value should be either \"Endpoints\" or \"EndpointSlice\".\nIf unset, the operator assumes the \"Endpoints\" role.",
+                    "enum": [
+                      "Endpoints",
+                      "EndpointSlice"
+                    ],
+                    "type": "string"
+                  },
                   "serviceMonitorNamespaceSelector": {
                     "description": "Namespaces to match for ServicedMonitors discovery. An empty label selector\nmatches all namespaces. A null label selector (default value) matches the current\nnamespace only.",
                     "properties": {
diff --git a/jsonnet/prometheus-operator/prometheuses-crd.json b/jsonnet/prometheus-operator/prometheuses-crd.json
index 571788635..bea311423 100644
--- a/jsonnet/prometheus-operator/prometheuses-crd.json
+++ b/jsonnet/prometheus-operator/prometheuses-crd.json
@@ -6797,6 +6797,14 @@
                     "description": "ServiceAccountName is the name of the ServiceAccount to use to run the\nPrometheus Pods.",
                     "type": "string"
                   },
+                  "serviceDiscoveryRole": {
+                    "description": "Defines the service discovery role used to discover targets from `ServiceMonitor` objects.\nIf set, the value should be either \"Endpoints\" or \"EndpointSlice\".\nIf unset, the operator assumes the \"Endpoints\" role.",
+                    "enum": [
+                      "Endpoints",
+                      "EndpointSlice"
+                    ],
+                    "type": "string"
+                  },
                   "serviceMonitorNamespaceSelector": {
                     "description": "Namespaces to match for ServicedMonitors discovery. An empty label selector\nmatches all namespaces. A null label selector (default value) matches the current\nnamespace only.",
                     "properties": {
diff --git a/pkg/apis/monitoring/v1/prometheus_types.go b/pkg/apis/monitoring/v1/prometheus_types.go
index fb35bf499..978766141 100644
--- a/pkg/apis/monitoring/v1/prometheus_types.go
+++ b/pkg/apis/monitoring/v1/prometheus_types.go
@@ -772,6 +772,12 @@ type CommonPrometheusFields struct {
 	// +listType=map
 	// +listMapKey=name
 	ScrapeClasses []ScrapeClass `json:"scrapeClasses,omitempty"`
+
+	// Defines the service discovery role used to discover targets from `ServiceMonitor` objects.
+	// If set, the value should be either "Endpoints" or "EndpointSlice".
+	// If unset, the operator assumes the "Endpoints" role.
+	// +optional
+	ServiceDiscoveryRole *ServiceDiscoveryRole `json:"serviceDiscoveryRole,omitempty"`
 }
 
 // +kubebuilder:validation:Enum=HTTP;ProcessSignal
@@ -785,6 +791,14 @@ const (
 	ProcessSignalReloadStrategyType ReloadStrategyType = "ProcessSignal"
 )
 
+// +kubebuilder:validation:Enum=Endpoints;EndpointSlice
+type ServiceDiscoveryRole string
+
+const (
+	EndpointsRole     ServiceDiscoveryRole = "Endpoints"
+	EndpointSliceRole ServiceDiscoveryRole = "EndpointSlice"
+)
+
 func (cpf *CommonPrometheusFields) PrometheusURIScheme() string {
 	if cpf.Web != nil && cpf.Web.TLSConfig != nil {
 		return "https"
diff --git a/pkg/apis/monitoring/v1/zz_generated.deepcopy.go b/pkg/apis/monitoring/v1/zz_generated.deepcopy.go
index 3e4346da7..fd39f2fe3 100644
--- a/pkg/apis/monitoring/v1/zz_generated.deepcopy.go
+++ b/pkg/apis/monitoring/v1/zz_generated.deepcopy.go
@@ -969,6 +969,11 @@ func (in *CommonPrometheusFields) DeepCopyInto(out *CommonPrometheusFields) {
 			(*in)[i].DeepCopyInto(&(*out)[i])
 		}
 	}
+	if in.ServiceDiscoveryRole != nil {
+		in, out := &in.ServiceDiscoveryRole, &out.ServiceDiscoveryRole
+		*out = new(ServiceDiscoveryRole)
+		**out = **in
+	}
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CommonPrometheusFields.
diff --git a/pkg/client/applyconfiguration/monitoring/v1/commonprometheusfields.go b/pkg/client/applyconfiguration/monitoring/v1/commonprometheusfields.go
index 809d55add..83380f257 100644
--- a/pkg/client/applyconfiguration/monitoring/v1/commonprometheusfields.go
+++ b/pkg/client/applyconfiguration/monitoring/v1/commonprometheusfields.go
@@ -107,6 +107,7 @@ type CommonPrometheusFieldsApplyConfiguration struct {
 	ReloadStrategy                       *monitoringv1.ReloadStrategyType                        `json:"reloadStrategy,omitempty"`
 	MaximumStartupDurationSeconds        *int32                                                  `json:"maximumStartupDurationSeconds,omitempty"`
 	ScrapeClasses                        []ScrapeClassApplyConfiguration                         `json:"scrapeClasses,omitempty"`
+	ServiceDiscoveryRole                 *monitoringv1.ServiceDiscoveryRole                      `json:"serviceDiscoveryRole,omitempty"`
 }
 
 // CommonPrometheusFieldsApplyConfiguration constructs an declarative configuration of the CommonPrometheusFields type for use with
@@ -826,3 +827,11 @@ func (b *CommonPrometheusFieldsApplyConfiguration) WithScrapeClasses(values ...*
 	}
 	return b
 }
+
+// WithServiceDiscoveryRole sets the ServiceDiscoveryRole field in the declarative configuration to the given value
+// and returns the receiver, so that objects can be built by chaining "With" function invocations.
+// If called multiple times, the ServiceDiscoveryRole field is set to the value of the last call.
+func (b *CommonPrometheusFieldsApplyConfiguration) WithServiceDiscoveryRole(value monitoringv1.ServiceDiscoveryRole) *CommonPrometheusFieldsApplyConfiguration {
+	b.ServiceDiscoveryRole = &value
+	return b
+}
diff --git a/pkg/client/applyconfiguration/monitoring/v1/prometheusspec.go b/pkg/client/applyconfiguration/monitoring/v1/prometheusspec.go
index 82dd8ffc3..03f4bec94 100644
--- a/pkg/client/applyconfiguration/monitoring/v1/prometheusspec.go
+++ b/pkg/client/applyconfiguration/monitoring/v1/prometheusspec.go
@@ -769,6 +769,14 @@ func (b *PrometheusSpecApplyConfiguration) WithScrapeClasses(values ...*ScrapeCl
 	return b
 }
 
+// WithServiceDiscoveryRole sets the ServiceDiscoveryRole field in the declarative configuration to the given value
+// and returns the receiver, so that objects can be built by chaining "With" function invocations.
+// If called multiple times, the ServiceDiscoveryRole field is set to the value of the last call.
+func (b *PrometheusSpecApplyConfiguration) WithServiceDiscoveryRole(value monitoringv1.ServiceDiscoveryRole) *PrometheusSpecApplyConfiguration {
+	b.ServiceDiscoveryRole = &value
+	return b
+}
+
 // WithBaseImage sets the BaseImage field in the declarative configuration to the given value
 // and returns the receiver, so that objects can be built by chaining "With" function invocations.
 // If called multiple times, the BaseImage field is set to the value of the last call.
diff --git a/pkg/client/applyconfiguration/monitoring/v1alpha1/prometheusagentspec.go b/pkg/client/applyconfiguration/monitoring/v1alpha1/prometheusagentspec.go
index 19ee2be20..56d73ca0c 100644
--- a/pkg/client/applyconfiguration/monitoring/v1alpha1/prometheusagentspec.go
+++ b/pkg/client/applyconfiguration/monitoring/v1alpha1/prometheusagentspec.go
@@ -756,3 +756,11 @@ func (b *PrometheusAgentSpecApplyConfiguration) WithScrapeClasses(values ...*v1.
 	}
 	return b
 }
+
+// WithServiceDiscoveryRole sets the ServiceDiscoveryRole field in the declarative configuration to the given value
+// and returns the receiver, so that objects can be built by chaining "With" function invocations.
+// If called multiple times, the ServiceDiscoveryRole field is set to the value of the last call.
+func (b *PrometheusAgentSpecApplyConfiguration) WithServiceDiscoveryRole(value monitoringv1.ServiceDiscoveryRole) *PrometheusAgentSpecApplyConfiguration {
+	b.ServiceDiscoveryRole = &value
+	return b
+}
diff --git a/pkg/prometheus/agent/operator.go b/pkg/prometheus/agent/operator.go
index f3297ef4e..8b56b381a 100644
--- a/pkg/prometheus/agent/operator.go
+++ b/pkg/prometheus/agent/operator.go
@@ -89,7 +89,7 @@ type Operator struct {
 	reconciliations *operator.ReconciliationTracker
 
 	config                 prompkg.Config
-	endpointSliceSupported bool
+	endpointSliceSupported bool // Whether the Kubernetes API suports the EndpointSlice kind.
 	scrapeConfigSupported  bool
 	canReadStorageClass    bool
 
@@ -318,15 +318,11 @@ func New(ctx context.Context, restConfig *rest.Config, c operator.Config, logger
 		}
 	}
 
-	endpointSliceSupported, err := k8sutil.IsAPIGroupVersionResourceSupported(o.kclient.Discovery(), schema.GroupVersion{Group: "discovery.k8s.io", Version: "v1"}, "endpointslices")
+	o.endpointSliceSupported, err = k8sutil.IsAPIGroupVersionResourceSupported(o.kclient.Discovery(), schema.GroupVersion{Group: "discovery.k8s.io", Version: "v1"}, "endpointslices")
 	if err != nil {
 		level.Warn(o.logger).Log("msg", "failed to check if the API supports the endpointslice resources", "err ", err)
 	}
-	level.Info(o.logger).Log("msg", "Kubernetes API capabilities", "endpointslices", endpointSliceSupported)
-	// The operator doesn't yet support the endpointslices API.
-	// See https://github.com/prometheus-operator/prometheus-operator/issues/3862
-	// for details.
-	o.endpointSliceSupported = false
+	level.Info(o.logger).Log("msg", "Kubernetes API capabilities", "endpointslices", o.endpointSliceSupported)
 
 	o.statusReporter = prompkg.StatusReporter{
 		Kclient:         o.kclient,
diff --git a/pkg/prometheus/promcfg.go b/pkg/prometheus/promcfg.go
index 280438b50..6d510f0b6 100644
--- a/pkg/prometheus/promcfg.go
+++ b/pkg/prometheus/promcfg.go
@@ -64,13 +64,15 @@ type ConfigGenerator struct {
 	version                semver.Version
 	notCompatible          bool
 	prom                   monitoringv1.PrometheusInterface
-	endpointSliceSupported bool
+	useEndpointSlice       bool // Whether to use EndpointSlice for service discovery from `ServiceMonitor` objects.
 	scrapeClasses          map[string]monitoringv1.ScrapeClass
 	defaultScrapeClassName string
 }
 
 // NewConfigGenerator creates a ConfigGenerator for the provided Prometheus resource.
-func NewConfigGenerator(logger log.Logger, p monitoringv1.PrometheusInterface, endpointSliceSupported bool) (*ConfigGenerator, error) {
+func NewConfigGenerator(logger log.Logger,
+	p monitoringv1.PrometheusInterface,
+	endpointSliceSupported bool) (*ConfigGenerator, error) {
 	if logger == nil {
 		logger = log.NewNopLogger()
 	}
@@ -94,11 +96,28 @@ func NewConfigGenerator(logger log.Logger, p monitoringv1.PrometheusInterface, e
 		return nil, fmt.Errorf("failed to parse scrape classes: %w", err)
 	}
 
+	endpointSliceConfigured := false // Always assume false to preserve original prometheus-operator behaviour.
+
+	// Check if the user has explicitly set the service discovery role to use.
+	switch serviceDiscoveryRole := ptr.Deref(cpf.ServiceDiscoveryRole, monitoringv1.EndpointsRole); serviceDiscoveryRole {
+	case monitoringv1.EndpointSliceRole:
+		level.Info(logger).Log("msg", "using endpointslice as service discovery role")
+		endpointSliceConfigured = true
+	case monitoringv1.EndpointsRole:
+		level.Info(logger).Log("msg", "using endpoints as service discovery role")
+		endpointSliceConfigured = false
+	default:
+		level.Warn(logger).Log("msg",
+			"unknown service discovery role %q, defaulting to endpoints. Configure serviceDiscoveryRole to 'EndpointSlice' to use endpointslice as service discovery role.",
+			serviceDiscoveryRole)
+		endpointSliceConfigured = false
+	}
+
 	return &ConfigGenerator{
 		logger:                 logger,
 		version:                version,
 		prom:                   p,
-		endpointSliceSupported: endpointSliceSupported,
+		useEndpointSlice:       endpointSliceConfigured && endpointSliceSupported,
 		scrapeClasses:          scrapeClasses,
 		defaultScrapeClassName: defaultScrapeClassName,
 	}, nil
@@ -142,7 +161,7 @@ func (cg *ConfigGenerator) WithKeyVals(keyvals ...interface{}) *ConfigGenerator
 		version:                cg.version,
 		notCompatible:          cg.notCompatible,
 		prom:                   cg.prom,
-		endpointSliceSupported: cg.endpointSliceSupported,
+		useEndpointSlice:       cg.useEndpointSlice,
 		scrapeClasses:          cg.scrapeClasses,
 		defaultScrapeClassName: cg.defaultScrapeClassName,
 	}
@@ -161,7 +180,7 @@ func (cg *ConfigGenerator) WithMinimumVersion(version string) *ConfigGenerator {
 			version:                cg.version,
 			notCompatible:          true,
 			prom:                   cg.prom,
-			endpointSliceSupported: cg.endpointSliceSupported,
+			useEndpointSlice:       cg.useEndpointSlice,
 			scrapeClasses:          cg.scrapeClasses,
 			defaultScrapeClassName: cg.defaultScrapeClassName,
 		}
@@ -183,7 +202,7 @@ func (cg *ConfigGenerator) WithMaximumVersion(version string) *ConfigGenerator {
 			version:                cg.version,
 			notCompatible:          true,
 			prom:                   cg.prom,
-			endpointSliceSupported: cg.endpointSliceSupported,
+			useEndpointSlice:       cg.useEndpointSlice,
 			scrapeClasses:          cg.scrapeClasses,
 			defaultScrapeClassName: cg.defaultScrapeClassName,
 		}
@@ -329,8 +348,11 @@ func (cg *ConfigGenerator) AddHonorLabels(cfg yaml.MapSlice, honorLabels bool) y
 	return cg.AppendMapItem(cfg, "honor_labels", honorLabels)
 }
 
+// Returns true if the Prometheus version used supports service discovery via EndpointSlice
+// and the prometheus-operator config generator is configured to use EndpointSlices for
+// service discovery from ServiceMonitor objects.
 func (cg *ConfigGenerator) EndpointSliceSupported() bool {
-	return cg.version.GTE(semver.MustParse("2.21.0")) && cg.endpointSliceSupported
+	return cg.version.GTE(semver.MustParse("2.21.0")) && cg.useEndpointSlice
 }
 
 // stringMapToMapSlice returns a yaml.MapSlice from a string map to ensure that
diff --git a/pkg/prometheus/promcfg_test.go b/pkg/prometheus/promcfg_test.go
index 484103031..05a68b3a1 100644
--- a/pkg/prometheus/promcfg_test.go
+++ b/pkg/prometheus/promcfg_test.go
@@ -66,7 +66,15 @@ func mustNewConfigGenerator(t *testing.T, p *monitoringv1.Prometheus) *ConfigGen
 	}
 	logger := level.NewFilter(log.NewLogfmtLogger(os.Stdout), level.AllowWarn())
 
-	cg, err := NewConfigGenerator(log.With(logger, "test", t.Name()), p, false)
+	useEndpointSlice := false
+
+	if p.Spec.ServiceDiscoveryRole == nil || *p.Spec.ServiceDiscoveryRole == monitoringv1.EndpointsRole {
+		useEndpointSlice = false
+	} else if *p.Spec.ServiceDiscoveryRole == monitoringv1.EndpointSliceRole {
+		useEndpointSlice = true
+	}
+
+	cg, err := NewConfigGenerator(log.With(logger, "test", t.Name()), p, useEndpointSlice)
 	require.NoError(t, err)
 
 	return cg
@@ -825,11 +833,13 @@ func TestK8SSDConfigGeneration(t *testing.T) {
 	testcases := []struct {
 		apiServerConfig *monitoringv1.APIServerConfig
 		store           *assets.StoreBuilder
+		role            string
 		golden          string
 	}{
 		{
 			apiServerConfig: nil,
 			store:           assets.NewTestStoreBuilder(),
+			role:            "endpoints",
 			golden:          "K8SSDConfigGenerationFirst.golden",
 		},
 		{
@@ -864,8 +874,15 @@ func TestK8SSDConfigGeneration(t *testing.T) {
 					},
 				},
 			),
+			role:   "endpoints",
 			golden: "K8SSDConfigGenerationTwo.golden",
 		},
+		{
+			apiServerConfig: nil,
+			store:           assets.NewTestStoreBuilder(),
+			role:            "endpointslice",
+			golden:          "K8SSDConfigGenerationThree.golden",
+		},
 		{
 			apiServerConfig: &monitoringv1.APIServerConfig{
 				Host: "example.com",
@@ -897,6 +914,7 @@ func TestK8SSDConfigGeneration(t *testing.T) {
 				},
 			},
 			store:  assets.NewTestStoreBuilder(),
+			role:   "endpoints",
 			golden: "K8SSDConfigGenerationTLSConfig.golden",
 		},
 	}
@@ -925,7 +943,7 @@ func TestK8SSDConfigGeneration(t *testing.T) {
 			sm.Namespace,
 			tc.apiServerConfig,
 			tc.store.ForNamespace(sm.Namespace),
-			kubernetesSDRoleEndpoint,
+			tc.role,
 			attachMetaConfig,
 		)
 		s, err := yaml.Marshal(yaml.MapSlice{c})
@@ -1564,9 +1582,10 @@ func TestNoEnforcedNamespaceLabelServiceMonitor(t *testing.T) {
 func TestServiceMonitorWithEndpointSliceEnable(t *testing.T) {
 	p := defaultPrometheus()
 	p.Spec.CommonPrometheusFields.EnforcedNamespaceLabel = "ns-key"
+	p.Spec.CommonPrometheusFields.ServiceDiscoveryRole = ptr.To(monitoringv1.EndpointSliceRole)
 
 	cg := mustNewConfigGenerator(t, p)
-	cg.endpointSliceSupported = true
+
 	cfg, err := cg.GenerateServerConfiguration(
 		p.Spec.EvaluationInterval,
 		p.Spec.QueryLogFile,
diff --git a/pkg/prometheus/server/operator.go b/pkg/prometheus/server/operator.go
index ca61db04a..cbfa29507 100644
--- a/pkg/prometheus/server/operator.go
+++ b/pkg/prometheus/server/operator.go
@@ -355,15 +355,11 @@ func New(ctx context.Context, restConfig *rest.Config, c operator.Config, logger
 		}
 	}
 
-	endpointSliceSupported, err := k8sutil.IsAPIGroupVersionResourceSupported(o.kclient.Discovery(), schema.GroupVersion{Group: "discovery.k8s.io", Version: "v1"}, "endpointslices")
+	o.endpointSliceSupported, err = k8sutil.IsAPIGroupVersionResourceSupported(o.kclient.Discovery(), schema.GroupVersion{Group: "discovery.k8s.io", Version: "v1"}, "endpointslices")
 	if err != nil {
 		level.Warn(o.logger).Log("msg", "failed to check if the API supports the endpointslice resources", "err ", err)
 	}
-	level.Info(o.logger).Log("msg", "Kubernetes API capabilities", "endpointslices", endpointSliceSupported)
-	// The operator doesn't yet support the endpointslices API.
-	// See https://github.com/prometheus-operator/prometheus-operator/issues/3862
-	// for details.
-	o.endpointSliceSupported = false
+	level.Info(o.logger).Log("msg", "Kubernetes API capabilities", "endpointslices", o.endpointSliceSupported)
 
 	o.statusReporter = prompkg.StatusReporter{
 		Kclient:         o.kclient,
@@ -813,6 +809,7 @@ func (c *Operator) sync(ctx context.Context, key string) error {
 	}
 
 	assetStore := assets.NewStoreBuilder(c.kclient.CoreV1(), c.kclient.CoreV1())
+
 	cg, err := prompkg.NewConfigGenerator(c.logger, p, c.endpointSliceSupported)
 	if err != nil {
 		return err
diff --git a/pkg/prometheus/testdata/K8SSDConfigGenerationThree.golden b/pkg/prometheus/testdata/K8SSDConfigGenerationThree.golden
new file mode 100644
index 000000000..cc0797c81
--- /dev/null
+++ b/pkg/prometheus/testdata/K8SSDConfigGenerationThree.golden
@@ -0,0 +1,5 @@
+kubernetes_sd_configs:
+- role: endpointslice
+  namespaces:
+    names:
+    - test