This Kubernetes integration automatically discovers HAProxy pods in your cluster and collects metrics without manual configuration for each instance. It uses the OpenTelemetry haproxyreceiver and receivercreator to dynamically monitor HAProxy sessions, requests, errors, and backend server health across your containerized environment.
Sugerencia
Running HAProxy on Linux hosts? See the self-hosted installation guide for virtual machine or bare metal deployments.
Before you begin
Ensure you have:
- Valid New Relic license key
- HAProxy pods with the stats endpoint enabled (typically on port 8404)
- A label on each HAProxy pod for discovery (for example,
app: haproxy) - If you're using the Helm install method, complete the base Kubernetes OpenTelemetry Helm installation
Set up HAProxy monitoring
Add the following sections under
deployment.extraConfigin yourvalues.yaml:Importante
In
deployment.extraConfig,pipelines:must be defined at the root level — not nested underservice:. The Helm chart template mapsextraConfig.pipelinesintoservice.pipelines, andextraConfig.service.extensionsintoservice.extensions.Extensions:
extensions:k8s_observer:auth_type: serviceAccountobserve_pods: trueobserve_nodes: falseEnvironment variables (add under
deployment.envs):deployment:envs:- name: OTEL_RESOURCE_ATTRIBUTESvalue: "host.id=$(KUBE_NODE_NAME)"Importante
The
host.idresource attribute is required for entity synthesis. Container images don't have/etc/machine-id, so you must inject it viaOTEL_RESOURCE_ATTRIBUTES. The Helm chart already setsKUBE_NODE_NAMEfrom the Kubernetes Downward API.Receivers:
receivers:receiver_creator/haproxy:watch_observers: [k8s_observer]receivers:haproxy:rule: type == "pod" && labels["app"] == "haproxy"config:endpoint: 'http://`endpoint`:8404/stats'collection_interval: 30sProcessors:
processors:resource/cluster:attributes:- key: k8s.cluster.namevalue: "<YOUR_CLUSTER_NAME>"action: inserttransform/haproxy:metric_statements:- context: resourcestatements:- set(attributes["haproxy.display.name"], Concat(["server","k8s",attributes["k8s.cluster.name"],attributes["k8s.namespace.name"],"pod",attributes["k8s.pod.name"],"haproxy","8404"], ":"))transform/metadata_nullify:metric_statements:- context: metricstatements:- set(description, "")- set(unit, "")Service extensions:
service:extensions: [k8s_observer]Pipelines:
pipelines:metrics/haproxy:receivers: [receiver_creator/haproxy]processors: [batch, resource/cluster, transform/haproxy, transform/metadata_nullify, memory_limiter]exporters: [otlphttp/newrelic]Configuration parameters
The following table describes the key configuration parameters:
Parameter
Description
labels["app"] == "haproxy"Pod label selector used to discover HAProxy pods. Update the rule to match your pod labels.
<YOUR_PORT_NUMBER>The port where HAProxy exposes its stats endpoint.
<YOUR_CLUSTER_NAME>Replace with your Kubernetes cluster name for identification in New Relic.
collection_intervalHow often to collect metrics. The default is
30s.Apply the updated values to your running Helm release:
bash$helm upgrade nr-k8s-otel-collector newrelic/nr-k8s-otel-collector \>--namespace newrelic \>--reuse-values \>-f values.yaml
After completing the base Kubernetes OpenTelemetry manifest installation, configure HAProxy monitoring:
Update the collector image to use NRDOT collector.
In the
deployment.yamlfile in your localrendereddirectory, update the image to:image: newrelic/nrdot-collector:latestMount the host's
/etc/machine-idinto the collector container by adding these to the deployment spec:volumeMounts:- name: machine-idmountPath: /etc/machine-idreadOnly: truevolumes:- name: machine-idhostPath:path: /etc/machine-idtype: FileImportante
The
host.idresource attribute is required for entity synthesis. Theresourcedetectionprocessor reads/etc/machine-idto populate it. Without this volume mount,host.idwon't be set and entities won't be created.Replace the content in
deployment-configmap.yamlunderdeployment-config.yaml: |with:extensions:health_check:endpoint: 0.0.0.0:13133k8s_observer:auth_type: serviceAccountobserve_pods: trueobserve_nodes: falsereceivers:receiver_creator/haproxy:watch_observers: [k8s_observer]receivers:haproxy:rule: type == "pod" && labels["app"] == "haproxy"config:endpoint: 'http://`endpoint`:8404/stats'collection_interval: 30sprocessors:resourcedetection:detectors: [env, system]system:resource_attributes:host.name:enabled: truehost.id:enabled: trueos.type:enabled: truebatch:send_batch_max_size: 1000timeout: 30ssend_batch_size: 800memory_limiter:check_interval: 1slimit_percentage: 80spike_limit_percentage: 25resource/cluster:attributes:- key: k8s.cluster.namevalue: "<YOUR_CLUSTER_NAME>"action: inserttransform/haproxy:metric_statements:- context: resourcestatements:- set(attributes["haproxy.display.name"], Concat(["server","k8s",attributes["k8s.cluster.name"],attributes["k8s.namespace.name"],"pod",attributes["k8s.pod.name"],"haproxy","8404"], ":"))transform/metadata_nullify:metric_statements:- context: metricstatements:- set(description, "")- set(unit, "")exporters:otlp_http/newrelic:endpoint: "<YOUR_NEWRELIC_OTLP_ENDPOINT>"headers:api-key: ${env:NR_LICENSE_KEY}service:extensions: [health_check, k8s_observer]pipelines:metrics/haproxy:receivers: [receiver_creator/haproxy]processors: [resourcedetection, batch, resource/cluster, transform/haproxy, transform/metadata_nullify, memory_limiter]exporters: [otlp_http/newrelic]Delete the DaemonSet and kube-state-metrics deployment (not needed for HAProxy-only monitoring):
bash$kubectl delete daemonset nr-k8s-otel-collector-daemonset -n newrelic$kubectl delete deployment nr-k8s-otel-collector-kube-state-metrics -n newrelicRestart the collector deployment to apply your changes:
bash$kubectl rollout restart deployment nr-k8s-otel-collector-deployment -n newrelicConfiguration parameters
Parameter
Description
labels["app"] == "haproxy"Pod label selector used to discover HAProxy pods. Update the rule to match your pod labels.
<YOUR_PORT_NUMBER>The port where HAProxy exposes its stats endpoint.
<YOUR_CLUSTER_NAME>Replace with your Kubernetes cluster name for identification in New Relic.
<YOUR_NEWRELIC_OTLP_ENDPOINT>Your New Relic OTLP endpoint. Use
https://otlp.nr-data.netfor US,https://otlp.eu01.nr-data.netfor EU.
Deploy the OpenTelemetry Collector Contrib to your Kubernetes cluster. The collector automatically discovers and scrapes metrics from your HAProxy pods. Choose between Helm (using the upstream OTel Helm chart) or raw Kubernetes manifests.
Create a
values.yamlfile with the following HAProxy monitoring configuration:mode: deploymentimage:repository: otel/opentelemetry-collector-contribclusterRole:create: truerules:- apiGroups: [""]resources: ["pods", "nodes"]verbs: ["get", "list", "watch"]- apiGroups: ["apps"]resources: ["replicasets"]verbs: ["get", "list", "watch"]extraVolumes:- name: machine-idhostPath:path: /etc/machine-idtype: FileextraVolumeMounts:- name: machine-idmountPath: /etc/machine-idreadOnly: trueconfig:extensions:health_check:endpoint: ${env:MY_POD_IP}:13133k8s_observer:auth_type: serviceAccountobserve_pods: trueobserve_nodes: falsereceivers:receiver_creator/haproxy:watch_observers: [k8s_observer]receivers:haproxy:rule: type == "pod" && labels["app"] == "haproxy"config:endpoint: 'http://`endpoint`:8404/stats'collection_interval: 30sprocessors:resourcedetection:detectors: [env, system]system:resource_attributes:host.name:enabled: truehost.id:enabled: trueos.type:enabled: truebatch:send_batch_size: 1024timeout: 30smemory_limiter:check_interval: 1slimit_percentage: 80spike_limit_percentage: 25resource/cluster:attributes:- key: k8s.cluster.namevalue: "<YOUR_CLUSTER_NAME>"action: inserttransform/haproxy:metric_statements:- context: resourcestatements:- set(attributes["haproxy.display.name"], Concat(["server", "k8s", attributes["k8s.cluster.name"], attributes["k8s.namespace.name"], "pod", attributes["k8s.pod.name"], "haproxy", "8404"], ":"))transform/metadata_nullify:metric_statements:- context: metricstatements:- set(description, "")- set(unit, "")exporters:otlp_http/newrelic:endpoint: "<YOUR_NEWRELIC_OTLP_ENDPOINT>"headers:api-key: "<YOUR_NEW_RELIC_LICENSE_KEY>"service:extensions: [health_check, k8s_observer]pipelines:metrics/haproxy:receivers: [receiver_creator/haproxy]processors: [resourcedetection, batch, resource/cluster, transform/haproxy, transform/metadata_nullify, memory_limiter]exporters: [otlp_http/newrelic]Configuration parameters
Parameter
Description
labels["app"] == "haproxy"Pod label selector used to discover HAProxy pods. Update the rule to match your pod labels.
<YOUR_PORT_NUMBER>The port where HAProxy exposes its stats endpoint.
<YOUR_CLUSTER_NAME>Replace with your Kubernetes cluster name for identification in New Relic.
<YOUR_NEWRELIC_OTLP_ENDPOINT>Your New Relic OTLP endpoint. Use
https://otlp.nr-data.netfor US,https://otlp.eu01.nr-data.netfor EU.<YOUR_NEW_RELIC_LICENSE_KEY>Your New Relic ingest license key.
Install using the upstream OpenTelemetry Helm chart:
bash$helm repo add open-telemetry https://open-telemetry.github.io/opentelemetry-helm-charts$helm repo update$helm upgrade --install haproxy-otel-collector open-telemetry/opentelemetry-collector \>-f values.yaml \>-n newrelic --create-namespace
Use this option if you can't use Helm or prefer to manage Kubernetes resources directly with kubectl.
Create a secret with your New Relic license key:
bash$kubectl create namespace newrelic$kubectl create secret generic otel-collector-haproxy-secret \>--namespace newrelic \>--from-literal=licenseKey='<YOUR_NEW_RELIC_LICENSE_KEY>'Save the following manifest to a file (for example,
otel-collector-haproxy.yaml):---apiVersion: v1kind: ServiceAccountmetadata:name: otel-collector-haproxynamespace: newreliclabels:app: otel-collector-haproxy---apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRolemetadata:name: otel-collector-haproxylabels:app: otel-collector-haproxyrules:- apiGroups: [""]resources: ["pods", "nodes"]verbs: ["get", "list", "watch"]- apiGroups: ["apps"]resources: ["replicasets"]verbs: ["get", "list", "watch"]---apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRoleBindingmetadata:name: otel-collector-haproxylabels:app: otel-collector-haproxyroleRef:apiGroup: rbac.authorization.k8s.iokind: ClusterRolename: otel-collector-haproxysubjects:- kind: ServiceAccountname: otel-collector-haproxynamespace: newrelic---apiVersion: v1kind: ConfigMapmetadata:name: otel-collector-haproxy-confignamespace: newreliclabels:app: otel-collector-haproxydata:collector-config.yaml: |extensions:health_check:endpoint: 0.0.0.0:13133k8s_observer:auth_type: serviceAccountobserve_pods: trueobserve_nodes: falsereceivers:receiver_creator/haproxy:watch_observers: [k8s_observer]receivers:haproxy:rule: type == "pod" && labels["app"] == "haproxy"config:endpoint: 'http://`endpoint`:8404/stats'collection_interval: 30sprocessors:resourcedetection:detectors: [env, system]system:resource_attributes:host.name:enabled: truehost.id:enabled: trueos.type:enabled: truebatch:send_batch_size: 1024timeout: 30smemory_limiter:check_interval: 1slimit_percentage: 80spike_limit_percentage: 25resource/cluster:attributes:- key: k8s.cluster.namevalue: "<YOUR_CLUSTER_NAME>"action: inserttransform/haproxy:metric_statements:- context: resourcestatements:- set(attributes["haproxy.display.name"], Concat(["server", "k8s", attributes["k8s.cluster.name"], attributes["k8s.namespace.name"], "pod", attributes["k8s.pod.name"], "haproxy", "8404"], ":"))transform/metadata_nullify:metric_statements:- context: metricstatements:- set(description, "")- set(unit, "")exporters:otlp_http/newrelic:endpoint: "<YOUR_NEWRELIC_OTLP_ENDPOINT>"headers:api-key: ${env:NR_LICENSE_KEY}service:extensions: [health_check, k8s_observer]pipelines:metrics/haproxy:receivers: [receiver_creator/haproxy]processors: [resourcedetection, batch, resource/cluster, transform/haproxy, transform/metadata_nullify, memory_limiter]exporters: [otlp_http/newrelic]---apiVersion: apps/v1kind: Deploymentmetadata:name: otel-collector-haproxynamespace: newreliclabels:app: otel-collector-haproxyspec:replicas: 1selector:matchLabels:app: otel-collector-haproxytemplate:metadata:labels:app: otel-collector-haproxyspec:serviceAccountName: otel-collector-haproxycontainers:- name: otel-collectorimage: otel/opentelemetry-collector-contrib:latestcommand:- "/otelcol-contrib"- "--config=/conf/collector-config.yaml"env:- name: NR_LICENSE_KEYvalueFrom:secretKeyRef:name: otel-collector-haproxy-secretkey: licenseKey- name: GOGCvalue: "80"ports:- name: healthcontainerPort: 13133protocol: TCPresources:limits:cpu: 500mmemory: 512Mirequests:cpu: 100mmemory: 256MivolumeMounts:- name: configmountPath: /conf- name: machine-idmountPath: /etc/machine-idreadOnly: truevolumes:- name: configconfigMap:name: otel-collector-haproxy-configitems:- key: collector-config.yamlpath: collector-config.yaml- name: machine-idhostPath:path: /etc/machine-idtype: FileImportante
The
/etc/machine-idvolume mount is required for entity synthesis. Theresourcedetectionprocessor reads this file to populate thehost.idresource attribute. Without it, entities won't be created in New Relic.Apply the manifest:
bash$kubectl apply -f otel-collector-haproxy.yamlVerify the collector is running and discovering HAProxy pods:
bash$kubectl get pods -n newrelic -l app=otel-collector-haproxy$kubectl logs -n newrelic -l app=otel-collector-haproxyYou should see log lines showing
starting receiverwith your HAProxy pod IPs.Configuration parameters
Parameter
Description
labels["app"] == "haproxy"Pod label selector used to discover HAProxy pods. Update the rule to match your pod labels.
<YOUR_PORT_NUMBER>The port where HAProxy exposes its stats endpoint.
<YOUR_CLUSTER_NAME>Replace with your Kubernetes cluster name for identification in New Relic.
<YOUR_NEWRELIC_OTLP_ENDPOINT>Your New Relic OTLP endpoint. Use
https://otlp.nr-data.netfor US,https://otlp.eu01.nr-data.netfor EU.<YOUR_NEW_RELIC_LICENSE_KEY>Your New Relic ingest license key.
Use this approach if your HAProxy pods already expose a Prometheus /metrics endpoint (HAProxy 2.0+) and you prefer to scrape Prometheus metrics rather than connect to the CSV stats endpoint.
Sugerencia
If you don't already have the Prometheus endpoint enabled, use the NRDOT collector or OpenTelemetry Collector Contrib tabs instead. They connect directly to the HAProxy stats endpoint without needing Prometheus support.
Importante
This approach requires HAProxy 2.0+ with the built-in Prometheus endpoint enabled. Your HAProxy configuration must include:
frontend stats bind *:8404 http-request use-service prometheus-exporter if { path /metrics }And your HAProxy pods must expose a named port for the metrics endpoint:
ports: - name: metrics containerPort: 8404Complete the base Kubernetes OpenTelemetry Helm installation for nr-k8s-otel-collector before proceeding.
Add the following sections under
deployment.configMap.extraConfigin yourvalues.yaml:Importante
In
deployment.configMap.extraConfig,pipelines:must be defined at the root level — not nested underservice:. The Helm chart template mapsextraConfig.pipelinesintoservice.pipelines, andextraConfig.service.extensionsintoservice.extensions.Extensions:
extensions:k8s_observer:auth_type: serviceAccountobserve_pods: trueobserve_nodes: falseEnvironment variables (add under
deployment.envs):deployment:envs:- name: OTEL_RESOURCE_ATTRIBUTESvalue: "host.id=$(KUBE_NODE_NAME)"Importante
The
host.idresource attribute is required for entity synthesis. Theresourcedetectionprocessor readsOTEL_RESOURCE_ATTRIBUTESto populate it. The Helm chart already setsKUBE_NODE_NAMEfrom the Kubernetes Downward API.Receivers:
receivers:receiver_creator/haproxy_prom:watch_observers: [k8s_observer]receivers:prometheus:rule: type == "port" && pod.labels["app"] == "haproxy" && port == 8404config:config:scrape_configs:- job_name: haproxy_prommetrics_path: /metricsscrape_interval: 30sstatic_configs:- targets:- '`endpoint`'resource_attributes:haproxy.addr: 'http://`endpoint`/metrics'Processors:
processors:resourcedetection:detectors: [env]filter/haproxy:metrics:include:match_type: regexpmetric_names:- "haproxy_frontend_bytes_in_total"- "haproxy_frontend_bytes_out_total"- "haproxy_frontend_requests_denied_total"- "haproxy_frontend_request_errors_total"- "haproxy_frontend_http_requests_total"- "haproxy_frontend_current_sessions"- "haproxy_frontend_max_session_rate"- "haproxy_frontend_http_requests_rate_max"- "haproxy_frontend_connections_rate_max"- "haproxy_backend_bytes_in_total"- "haproxy_backend_bytes_out_total"- "haproxy_backend_connection_errors_total"- "haproxy_backend_http_requests_total"- "haproxy_backend_current_sessions"- "haproxy_backend_max_session_rate"- "haproxy_backend_current_queue"- "haproxy_server_bytes_in_total"- "haproxy_server_bytes_out_total"- "haproxy_server_connection_errors_total"- "haproxy_server_current_sessions"- "haproxy_server_max_session_rate"- "haproxy_server_current_queue"transform/add_labels:metric_statements:- context: datapointstatements:- set(attributes["haproxy.service_name"], "FRONTEND") where IsMatch(metric.name, "^haproxy_frontend_.*")- set(attributes["haproxy.service_name"], "BACKEND") where IsMatch(metric.name, "^haproxy_backend_.*")- set(attributes["haproxy.service_name"], attributes["server"]) where IsMatch(metric.name, "^haproxy_server_.*")- set(attributes["haproxy.proxy_name"], attributes["proxy"]) where attributes["proxy"] != nil- delete_key(attributes, "proxy") where attributes["proxy"] != nil- delete_key(attributes, "server") where attributes["server"] != nilmetricstransform/rename:transforms:- include: haproxy_frontend_bytes_in_totalaction: updatenew_name: haproxy.bytes.input- include: haproxy_backend_bytes_in_totalaction: updatenew_name: haproxy.bytes.input- include: haproxy_server_bytes_in_totalaction: updatenew_name: haproxy.bytes.input- include: haproxy_frontend_bytes_out_totalaction: updatenew_name: haproxy.bytes.output- include: haproxy_backend_bytes_out_totalaction: updatenew_name: haproxy.bytes.output- include: haproxy_server_bytes_out_totalaction: updatenew_name: haproxy.bytes.output- include: haproxy_backend_connection_errors_totalaction: updatenew_name: haproxy.connections.errors- include: haproxy_server_connection_errors_totalaction: updatenew_name: haproxy.connections.errors- include: haproxy_frontend_requests_denied_totalaction: updatenew_name: haproxy.requests.denied- include: haproxy_frontend_request_errors_totalaction: updatenew_name: haproxy.requests.errors- include: haproxy_frontend_http_requests_totalaction: updatenew_name: haproxy.requests.total- include: haproxy_backend_http_requests_totalaction: updatenew_name: haproxy.requests.total- include: haproxy_backend_current_queueaction: updatenew_name: haproxy.requests.queued- include: haproxy_server_current_queueaction: updatenew_name: haproxy.requests.queued- include: haproxy_frontend_current_sessionsaction: updatenew_name: haproxy.sessions.count- include: haproxy_backend_current_sessionsaction: updatenew_name: haproxy.sessions.count- include: haproxy_server_current_sessionsaction: updatenew_name: haproxy.sessions.count- include: haproxy_frontend_http_requests_rate_maxaction: updatenew_name: haproxy.requests.rate- include: haproxy_frontend_connections_rate_maxaction: updatenew_name: haproxy.connections.rate- include: haproxy_frontend_max_session_rateaction: updatenew_name: haproxy.sessions.rate- include: haproxy_backend_max_session_rateaction: updatenew_name: haproxy.sessions.rate- include: haproxy_server_max_session_rateaction: updatenew_name: haproxy.sessions.rateresource/cluster:attributes:- key: k8s.cluster.namevalue: "<YOUR_CLUSTER_NAME>"action: inserttransform/haproxy:metric_statements:- context: resourcestatements:- set(attributes["haproxy.display.name"], Concat(["server", "k8s", attributes["k8s.cluster.name"], attributes["k8s.namespace.name"], "pod", attributes["k8s.pod.name"], "haproxy", "8404"], ":"))transform/metadata_nullify:metric_statements:- context: metricstatements:- set(description, "")- set(unit, "")Service extensions:
service:extensions: [k8s_observer]Pipelines:
pipelines:metrics/haproxy_prom:receivers: [receiver_creator/haproxy_prom]processors: [resourcedetection, filter/haproxy, transform/add_labels, metricstransform/rename, resource/cluster, batch, transform/haproxy, transform/metadata_nullify, memory_limiter]exporters: [otlp_http/newrelic]Configuration parameters
Parameter
Description
labels["app"] == "haproxy"Pod label selector used to discover HAProxy pods. Update the rule to match your pod labels.
<YOUR_PORT_NUMBER>The port where HAProxy exposes its Prometheus endpoint.
<YOUR_CLUSTER_NAME>Replace with your Kubernetes cluster name for identification in New Relic.
scrape_intervalHow often to scrape Prometheus metrics. Default:
30s.Apply the updated values to your running Helm release:
bash$helm upgrade nr-k8s-otel-collector newrelic/nr-k8s-otel-collector \>--namespace newrelic \>--reuse-values \>-f values.yaml
Create a
values.yamlfile with the Prometheus receiver configuration:mode: deploymentimage:repository: otel/opentelemetry-collector-contribclusterRole:create: truerules:- apiGroups: [""]resources: ["pods", "nodes"]verbs: ["get", "list", "watch"]- apiGroups: ["apps"]resources: ["replicasets"]verbs: ["get", "list", "watch"]extraVolumes:- name: machine-idhostPath:path: /etc/machine-idtype: FileextraVolumeMounts:- name: machine-idmountPath: /etc/machine-idreadOnly: trueconfig:extensions:health_check:endpoint: ${env:MY_POD_IP}:13133k8s_observer:auth_type: serviceAccountobserve_pods: trueobserve_nodes: falsereceivers:receiver_creator/haproxy_prom:watch_observers: [k8s_observer]receivers:prometheus:rule: type == "port" && pod.labels["app"] == "haproxy" && port == 8404config:config:scrape_configs:- job_name: haproxy_prommetrics_path: /metricsscrape_interval: 30sstatic_configs:- targets:- '`endpoint`'resource_attributes:haproxy.addr: 'http://`endpoint`/metrics'processors:resourcedetection:detectors: [env, system]system:resource_attributes:host.name:enabled: truehost.id:enabled: trueos.type:enabled: truefilter/haproxy:metrics:include:match_type: regexpmetric_names:- "haproxy_frontend_bytes_in_total"- "haproxy_frontend_bytes_out_total"- "haproxy_frontend_requests_denied_total"- "haproxy_frontend_request_errors_total"- "haproxy_frontend_http_requests_total"- "haproxy_frontend_current_sessions"- "haproxy_frontend_max_session_rate"- "haproxy_backend_bytes_in_total"- "haproxy_backend_bytes_out_total"- "haproxy_backend_connection_errors_total"- "haproxy_backend_http_requests_total"- "haproxy_backend_current_sessions"- "haproxy_backend_max_session_rate"- "haproxy_backend_current_queue"- "haproxy_server_bytes_in_total"- "haproxy_server_bytes_out_total"- "haproxy_server_connection_errors_total"- "haproxy_server_current_sessions"- "haproxy_server_max_session_rate"- "haproxy_server_current_queue"transform/add_labels:metric_statements:- context: datapointstatements:- set(attributes["haproxy.service_name"], "FRONTEND") where IsMatch(metric.name, "^haproxy_frontend_.*")- set(attributes["haproxy.service_name"], "BACKEND") where IsMatch(metric.name, "^haproxy_backend_.*")- set(attributes["haproxy.service_name"], attributes["server"]) where IsMatch(metric.name, "^haproxy_server_.*")- set(attributes["haproxy.proxy_name"], attributes["proxy"]) where attributes["proxy"] != nil- delete_key(attributes, "proxy") where attributes["proxy"] != nil- delete_key(attributes, "server") where attributes["server"] != nilmetricstransform/rename:transforms:- include: haproxy_frontend_bytes_in_totalaction: updatenew_name: haproxy.bytes.input- include: haproxy_backend_bytes_in_totalaction: updatenew_name: haproxy.bytes.input- include: haproxy_server_bytes_in_totalaction: updatenew_name: haproxy.bytes.input- include: haproxy_frontend_bytes_out_totalaction: updatenew_name: haproxy.bytes.output- include: haproxy_backend_bytes_out_totalaction: updatenew_name: haproxy.bytes.output- include: haproxy_server_bytes_out_totalaction: updatenew_name: haproxy.bytes.output- include: haproxy_backend_connection_errors_totalaction: updatenew_name: haproxy.connections.errors- include: haproxy_server_connection_errors_totalaction: updatenew_name: haproxy.connections.errors- include: haproxy_frontend_requests_denied_totalaction: updatenew_name: haproxy.requests.denied- include: haproxy_frontend_request_errors_totalaction: updatenew_name: haproxy.requests.errors- include: haproxy_frontend_http_requests_totalaction: updatenew_name: haproxy.requests.total- include: haproxy_backend_http_requests_totalaction: updatenew_name: haproxy.requests.total- include: haproxy_backend_current_queueaction: updatenew_name: haproxy.requests.queued- include: haproxy_server_current_queueaction: updatenew_name: haproxy.requests.queued- include: haproxy_frontend_current_sessionsaction: updatenew_name: haproxy.sessions.count- include: haproxy_backend_current_sessionsaction: updatenew_name: haproxy.sessions.count- include: haproxy_server_current_sessionsaction: updatenew_name: haproxy.sessions.count- include: haproxy_frontend_max_session_rateaction: updatenew_name: haproxy.sessions.rate- include: haproxy_backend_max_session_rateaction: updatenew_name: haproxy.sessions.rate- include: haproxy_server_max_session_rateaction: updatenew_name: haproxy.sessions.rateresource/cluster:attributes:- key: k8s.cluster.namevalue: "<YOUR_CLUSTER_NAME>"action: insertbatch:send_batch_size: 1024timeout: 30smemory_limiter:check_interval: 1slimit_percentage: 80spike_limit_percentage: 25transform/haproxy:metric_statements:- context: resourcestatements:- set(attributes["haproxy.display.name"], Concat(["server", "k8s", attributes["k8s.cluster.name"], attributes["k8s.namespace.name"], "pod", attributes["k8s.pod.name"], "haproxy", "8404"], ":"))transform/metadata_nullify:metric_statements:- context: metricstatements:- set(description, "")- set(unit, "")exporters:otlp_http/newrelic:endpoint: "<YOUR_NEWRELIC_OTLP_ENDPOINT>"headers:api-key: "<YOUR_NEW_RELIC_LICENSE_KEY>"service:extensions: [health_check, k8s_observer]pipelines:metrics/haproxy_prom:receivers: [receiver_creator/haproxy_prom]processors: [resourcedetection, filter/haproxy, transform/add_labels, metricstransform/rename, resource/cluster, batch, transform/haproxy, transform/metadata_nullify, memory_limiter]exporters: [otlp_http/newrelic]Importante
The
/etc/machine-idvolume mount is required for entity synthesis. Theresourcedetectionprocessor reads this file to populate thehost.idresource attribute. Without it, entities won't be created in New Relic.Configuration parameters
Parameter
Description
labels["app"] == "haproxy"Pod label selector used to discover HAProxy pods. Update the rule to match your pod labels.
<YOUR_PORT_NUMBER>The port where HAProxy exposes its Prometheus endpoint.
<YOUR_CLUSTER_NAME>Replace with your Kubernetes cluster name for identification in New Relic.
<YOUR_NEWRELIC_OTLP_ENDPOINT>Your New Relic OTLP endpoint. Use
https://otlp.nr-data.netfor US,https://otlp.eu01.nr-data.netfor EU.<YOUR_NEW_RELIC_LICENSE_KEY>Your New Relic ingest license key.
scrape_intervalHow often to scrape Prometheus metrics. Default:
30s.Install using the upstream OpenTelemetry Helm chart:
bash$helm repo add open-telemetry https://open-telemetry.github.io/opentelemetry-helm-charts$helm repo update$helm upgrade --install haproxy-prom-collector open-telemetry/opentelemetry-collector \>-f values.yaml \>-n newrelic --create-namespace
View your data in New Relic
After a few minutes, HAProxy metrics appear in New Relic. To find your data:
- Go to one.newrelic.com > All capabilities > All entities.
- Search for your HAProxy entities by name or filter by entity type
HAPROXYINSTANCE. - Select an entity to view the summary page with golden metrics (sessions per second, requests per second, connection errors).
Troubleshooting
- Verify HAProxy pods have the correct label (for example,
app: haproxy):kubectl get pods -l app=haproxy - Check that the stats endpoint responds:
kubectl exec -it <haproxy-pod> -- curl -s http://localhost:8404/stats - Review collector logs for discovery errors:
kubectl logs -n newrelic -l app.kubernetes.io/name=nr-k8s-otel-collector -f(NRDOT) orkubectl logs -n newrelic -l app=otel-collector-haproxy -f(OTel Contrib manifest) - Ensure the collector ServiceAccount has
get,list, andwatchpermissions on pods.
Each HAProxy pod should create a separate entity in New Relic. If you see only one entity:
- Verify the collector has
host.idset. In containerized environments, add this to the collector deployment:env:- name: K8S_NODE_NAMEvalueFrom:fieldRef:fieldPath: spec.nodeName- name: OTEL_RESOURCE_ATTRIBUTESvalue: "host.id=$(K8S_NODE_NAME)" - Confirm different
haproxy.addrvalues per pod by querying:FROM Metric SELECT uniques(haproxy.addr) WHERE metricName LIKE 'haproxy.%' SINCE 10 minutes ago
- The
receiver_creatoruses the pod IP (notlocalhost) to reach HAProxy. Ensure the stats frontend binds to*:8404(all interfaces), not127.0.0.1:8404. - Check network policies that might block traffic between the collector pod and HAProxy pods.
Next steps
- Review the metrics reference for a complete list of collected metrics.
- Learn how to find and query your HAProxy data.
- Set up alerts based on HAProxy golden metrics.