diff --git a/docs/content/config/config.md b/docs/content/config/config.md
index 4161ddb15..45c6d717d 100644
--- a/docs/content/config/config.md
+++ b/docs/content/config/config.md
@@ -149,6 +149,24 @@ This works for all Metrics properties.
(1) Boolean value, `true` or `false`. Default see Javadoc.
+## OpenMetrics 2.0 Properties
+
+
+
+| Name | Javadoc | Note |
+| ---------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | ---- |
+| io.prometheus.openmetrics2.enabled | [OpenMetrics2Properties.getEnabled()]() | (1) |
+| io.prometheus.openmetrics2.content_negotiation | [OpenMetrics2Properties.getContentNegotiation()]() | (1) |
+| io.prometheus.openmetrics2.composite_values | [OpenMetrics2Properties.getCompositeValues()]() | (1) |
+| io.prometheus.openmetrics2.exemplar_compliance | [OpenMetrics2Properties.getExemplarCompliance()]() | (1) |
+| io.prometheus.openmetrics2.native_histograms | [OpenMetrics2Properties.getNativeHistograms()]() | (1) |
+
+
+
+(1) Boolean value, `true` or `false`. `enabled=true` switches OpenMetrics responses to the OM2
+writer, preserving metric names as written by the application. The other OM2 properties remain
+opt-in. All OpenMetrics 2.0 flags are experimental and default to `false`.
+
## Exporter Filter Properties
@@ -198,6 +216,7 @@ Only metrics starting with these prefixes will be exposed.
| io.prometheus.exporter.opentelemetry.service_instance_id | [OpenTelemetryExporter.Builder.serviceInstanceId()]() | |
| io.prometheus.exporter.opentelemetry.service_version | [OpenTelemetryExporter.Builder.serviceVersion()]() | |
| io.prometheus.exporter.opentelemetry.resource_attributes | [OpenTelemetryExporter.Builder.resourceAttributes()]() | (3) |
+| io.prometheus.exporter.opentelemetry.preserve_names | [ExporterOpenTelemetryProperties.getPreserveNames()]() | (4) |
@@ -205,7 +224,8 @@ Only metrics starting with these prefixes will be exposed.
(1) Protocol can be `grpc` or `http/protobuf`.
(2) Format: `key1=value1,key2=value2`
-(3) Format: `key1=value1,key2=value2`
+(3) Format: `key1=value1,key2=value2`
+(4) Boolean value, `true` or `false`. Default is `false` for backward compatibility.
diff --git a/docs/content/exporters/formats.md b/docs/content/exporters/formats.md
index a6485c84c..6360c66cd 100644
--- a/docs/content/exporters/formats.md
+++ b/docs/content/exporters/formats.md
@@ -11,6 +11,11 @@ All exporters the following exposition formats:
Moreover, gzip encoding is supported for each of these formats.
+## OpenMetrics 2.0 Preview
+
+The library also includes an experimental OpenMetrics 2.0 writer. It is disabled by default and
+must be enabled explicitly. See [OpenMetrics 2.0 Preview]({{< relref "./openmetrics2.md" >}}).
+
## Scraping with a Prometheus server
The Prometheus server sends an `Accept` header to specify which format is requested. By default, the
diff --git a/docs/content/exporters/openmetrics2.md b/docs/content/exporters/openmetrics2.md
new file mode 100644
index 000000000..607273881
--- /dev/null
+++ b/docs/content/exporters/openmetrics2.md
@@ -0,0 +1,129 @@
+---
+title: OpenMetrics 2.0 Preview
+weight: 2
+---
+
+The Prometheus Java client library includes experimental support for the OpenMetrics 2.0 text
+format.
+
+{{< hint type=warning >}}
+OpenMetrics 2.0 support is opt-in, experimental, and subject to change while the specification is
+still in draft.
+{{< /hint >}}
+
+{{< toc >}}
+
+## Enable OpenMetrics 2.0
+
+To switch OpenMetrics responses from the legacy OM1 writer to the OM2 writer, set:
+
+```properties
+io.prometheus.openmetrics2.enabled=true
+```
+
+Programmatic configuration:
+
+```java
+PrometheusProperties properties = PrometheusProperties.builder()
+ .enableOpenMetrics2(om2 -> {})
+ .build();
+```
+
+Enabling `enableOpenMetrics2(...)` also enables the top-level `enabled` flag automatically, so you
+only need to configure the sub-flags you want.
+
+With `enabled=true` alone:
+
+- OpenMetrics requests use the OM2 writer.
+- Metric names are preserved as written by the application.
+- Optional OM2 features such as `composite_values`, `exemplar_compliance`, and
+ `native_histograms` remain off.
+
+To enable OM2 only when the scraper explicitly requests `version=2.0.0`, set:
+
+```properties
+io.prometheus.openmetrics2.enabled=true
+io.prometheus.openmetrics2.content_negotiation=true
+```
+
+Programmatic equivalent:
+
+```java
+PrometheusProperties properties = PrometheusProperties.builder()
+ .enableOpenMetrics2(om2 -> om2.contentNegotiation(true))
+ .build();
+```
+
+## Naming Behavior
+
+OpenMetrics 2.0 removes OM1 suffix rewriting.
+
+- Counters do not get `_total` appended automatically.
+- Units do not get appended automatically.
+- Info metrics still end in `_info` because that is required by the spec.
+
+Examples:
+
+| Metric builder input | OM1 output | OM2 output |
+| ---------------------------------- | ----------------- | -------------- |
+| `Counter("events")` | `events_total` | `events` |
+| `Counter("events_total")` | `events_total` | `events_total` |
+| `Counter("req").unit(BYTES)` | `req_bytes_total` | `req` |
+| `Counter("req_bytes").unit(BYTES)` | `req_bytes_total` | `req_bytes` |
+| `Info("target")` | `target_info` | `target_info` |
+
+This means OpenMetrics 2.0 does not apply OM1 suffix behavior such as appending `_total` or unit
+suffixes, while the legacy OpenMetrics 1.0 and Prometheus text formats keep that existing suffix
+behavior.
+
+## Feature Flags
+
+All OpenMetrics 2.0 flags default to `false`.
+
+| Property | Effect |
+| ------------------------------------------------ | -------------------------------------------------------------------------------------- |
+| `io.prometheus.openmetrics2.enabled` | Metric names are preserved as written by the application. |
+| `io.prometheus.openmetrics2.content_negotiation` | Apply OM2 behavior only when the scraper requests `version=2.0.0`. |
+| `io.prometheus.openmetrics2.composite_values` | Emit histograms, summaries, and gauge histograms as single composite lines with `st@`. |
+| `io.prometheus.openmetrics2.exemplar_compliance` | Emit only OM2-compliant exemplars with timestamps. |
+| `io.prometheus.openmetrics2.native_histograms` | Emit OM2 native histogram text fields. |
+
+Enable all flags at once:
+
+```java
+PrometheusProperties properties = PrometheusProperties.builder()
+ .enableOpenMetrics2(om2 -> om2.enableAll())
+ .build();
+```
+
+Equivalent properties:
+
+```properties
+io.prometheus.openmetrics2.enabled=true
+io.prometheus.openmetrics2.content_negotiation=true
+io.prometheus.openmetrics2.composite_values=true
+io.prometheus.openmetrics2.exemplar_compliance=true
+io.prometheus.openmetrics2.native_histograms=true
+```
+
+## Content Negotiation
+
+If `content_negotiation=false`, OpenMetrics 2.0 behavior is applied to OpenMetrics responses even
+if the scraper requested OpenMetrics 1.0.
+
+If `content_negotiation=true`, OpenMetrics 2.0 behavior is only used when the scraper explicitly
+requests `version=2.0.0`. Otherwise the legacy OpenMetrics 1.0 response is returned.
+
+## Native Histograms
+
+With `io.prometheus.openmetrics2.native_histograms=true`, the OpenMetrics 2.0 writer emits native
+histogram fields such as:
+
+- `schema`
+- `zero_threshold`
+- `zero_count`
+- positive and negative spans
+- positive and negative buckets
+
+OM2 native histogram output can coexist with classic histogram buckets. When both are present, the
+native histogram sample is written first.
diff --git a/docs/content/getting-started/metric-types.md b/docs/content/getting-started/metric-types.md
index f1c2a2321..a4fee31ad 100644
--- a/docs/content/getting-started/metric-types.md
+++ b/docs/content/getting-started/metric-types.md
@@ -37,9 +37,13 @@ serviceTimeSeconds.inc(Unit.millisToSeconds(200));
The resulting counter has the value `0.2`. As `SECONDS` is the standard time unit in Prometheus, the
`Unit` utility class has methods to convert other time units to seconds.
-As defined in [OpenMetrics](https://openmetrics.io/), counter metric names must have the `_total`
-suffix. If you create a counter without the `_total` suffix the suffix will be appended
-automatically.
+For the default OpenMetrics 1.0 and Prometheus text formats, counters are exposed with the
+`_total` suffix. You can name a counter either `service_time_seconds` or
+`service_time_seconds_total`; the exposed name will be `service_time_seconds_total` in both cases.
+
+The experimental OpenMetrics 2.0 writer behaves differently: It preserves metric names instead of
+appending `_total` or unit suffixes automatically. In OpenMetrics 2.0, `_total` is recommended for
+counters, but not enforced by the Java client.
## Gauge
diff --git a/docs/content/otel/names.md b/docs/content/otel/names.md
index a5425e07f..589be1c2c 100644
--- a/docs/content/otel/names.md
+++ b/docs/content/otel/names.md
@@ -21,6 +21,26 @@ The main steps when converting OpenTelemetry metric names to Prometheus metric n
- If the metric has a unit, append the unit to the metric name, like `_seconds`.
- If the metric type has a suffix, append it, like `_total` for counters.
+## `preserve_names`
+
+The Prometheus Java client library can also export its own metrics to OpenTelemetry using the
+[OpenTelemetryExporter](/client_java/api/io/prometheus/metrics/exporter/opentelemetry/OpenTelemetryExporter.html).
+
+For that exporter, `io.prometheus.exporter.opentelemetry.preserve_names=true` preserves metric
+names exactly as they were written in the Prometheus Java client.
+
+Examples:
+
+| Prometheus Java metric | Default OTel export | With `preserve_names=true` |
+| ---------------------------------- | --------------------- | --------------------------- |
+| `Counter("events")` | `events` | `events` |
+| `Counter("events_total")` | `events` | `events_total` |
+| `Counter("req").unit(BYTES)` | name `req`, unit `By` | name `req`, unit `By` |
+| `Counter("req_bytes").unit(BYTES)` | name `req`, unit `By` | name `req_bytes`, unit `By` |
+
+Today the default is `false` for backward compatibility. It is planned to change to `true` in the
+next major release.
+
## Dots in Metric and Label Names
OpenTelemetry defines not only a line protocol, but also _semantic conventions_, i.e. standardized