← Back to posts
comparison guide self-hosted · · 11 min read

Self-Hosted HAProxy Management: Data Plane API vs Prometheus Exporter vs Runtime API 2026

Compare three approaches to managing HAProxy in production: the official Data Plane API, Prometheus Exporter + Grafana dashboards, and the built-in Runtime API. Includes Docker Compose configs, deployment guides, and a decision matrix.

OS
Editorial Team

HAProxy powers millions of production deployments as the world’s most widely-used open-source load balancer and reverse proxy. But managing it at scale — adding backends, checking stats, rotating SSL certs, and monitoring health — requires more than editing a static haproxy.cfg file and sending SIGHUP.

This guide compares three practical approaches to self-hosted HAProxy management in 2026: the HAProxy Data Plane API for programmatic configuration, the Prometheus HAProxy Exporter paired with Grafana for observability, and the HAProxy Runtime API for low-level socket-based control. Each serves a different operational need, and most production teams use a combination.

Why Self-Host HAProxy Management Tools?

HAProxy ships with a built-in stats page, but it only provides read-only visibility. In production environments with frequent deployments, dynamic scaling, or zero-downtime requirements, you need tools that can:

  • Add or remove backends without reloading the process
  • Monitor frontend/backend health metrics in real time
  • Automate configuration changes through APIs instead of manual file edits
  • Integrate with CI/CD pipelines for blue-green or canary deployments
  • Set up alerting on connection queue depth, error rates, or response times

Self-hosted management tools give you full control over your load balancer’s operational lifecycle without depending on commercial offerings like HAProxy Enterprise or ALOHA.

The Three Management Approaches at a Glance

FeatureData Plane APIPrometheus Exporter + GrafanaRuntime API (Socket)
Primary purposeConfiguration managementMetrics & monitoringLow-level runtime control
Configuration changesREST API (full CRUD)Read-only (no config changes)CLI/Socket commands
Zero-reload updatesYes (transactional)N/AYes (via add server, set weight)
Monitoring/visualizationBasic stats endpointFull Grafana dashboardsRaw stats via show stat
Docker deploymentOfficial image availableOfficial image availableBuilt into HAProxy itself
GitHub stars382 (haproxytech/dataplaneapi)631 (prometheus/haproxy_exporter)N/A (built-in)
LanguageGoGoC (HAProxy core)
Last activeApril 2026March 2023 (stable)Always current
Learning curveModerate (REST API)Low-Moderate (Prometheus ecosystem)High (CLI/socket commands)
Best forDynamic environments, CI/CDObservability teams, alertingScripted automation, debugging

Approach 1: HAProxy Data Plane API

The HAProxy Data Plane API is an official REST API maintained by HAProxy Technologies. It provides programmatic access to HAProxy’s configuration, enabling you to manage frontends, backends, servers, ACLs, and SSL certificates without touching configuration files.

Key Features

  • Full CRUD operations on HAProxy configuration sections
  • Transactional updates — changes are staged and committed atomically
  • Built-in authentication (basic auth, bearer token)
  • OpenAPI 3.0 specification for client generation
  • Hot-reload without dropping connections
  • Integration with Kubernetes Ingress Controller

Docker Compose Deployment

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
version: "3.8"

services:
  haproxy:
    image: haproxy:2.9-alpine
    container_name: haproxy
    ports:
      - "80:80"
      - "443:443"
      - "8404:8404"
    volumes:
      - ./haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro
      - ./certs:/usr/local/etc/haproxy/certs:ro
    networks:
      - haproxy-net
    depends_on:
      - dataplane

  dataplane:
    image: haproxytech/dataplaneapi:latest
    container_name: dataplaneapi
    ports:
      - "5555:5555"
    environment:
      - HAPROXY_BIN=/usr/sbin/haproxy
      - HAPROXY_CONFIG=/usr/local/etc/haproxy/haproxy.cfg
    volumes:
      - ./haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg
      - /var/run/docker.sock:/var/run/docker.sock
    networks:
      - haproxy-net
    restart: unless-stopped

networks:
  haproxy-net:
    driver: bridge

Base HAProxy Configuration

You need to enable the Data Plane API in your haproxy.cfg:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
global
    log stdout format raw local0
    maxconn 4096
    stats socket /var/run/haproxy.sock mode 660 level admin
    stats timeout 30s

    # Data Plane API integration
    program api
        command /usr/local/bin/dataplaneapi \
            --host 0.0.0.0 \
            --port 5555 \
            --haproxy-bin /usr/sbin/haproxy \
            --config-file /usr/local/etc/haproxy/haproxy.cfg \
            --reload-cmd "kill -SIGUSR2 1" \
            --reload-delay 5 \
            --log-level verbose \
            --userlist HAProxyUserList

defaults
    log global
    mode http
    option httplog
    option dontlognull
    timeout connect 5000ms
    timeout client 50000ms
    timeout server 50000ms
    retries 3

frontend http_front
    bind *:80
    default_backend http_back

backend http_back
    balance roundrobin
    option httpchk GET /health
    server web1 10.0.0.1:8080 check
    server web2 10.0.0.2:8080 check

Using the API

Once deployed, you can manage HAProxy programmatically:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
# List all backends
curl -s -u admin:adminpwd \
    http://localhost:5555/v2/services/haproxy/configuration/backends

# Add a new server to an existing backend
curl -s -X POST -u admin:adminpwd \
    -H "Content-Type: application/json" \
    -d '{
        "name": "web3",
        "address": "10.0.0.3",
        "port": 8080,
        "check": "enabled",
        "maxconn": 30
    }' \
    "http://localhost:5555/v2/services/haproxy/configuration/servers?backend=http_back"

# Get server stats
curl -s -u admin:adminpwd \
    http://localhost:5555/v2/services/haproxy/stats

Approach 2: Prometheus HAProxy Exporter + Grafana

The Prometheus HAProxy Exporter scrapes HAProxy’s built-in CSV stats endpoint and converts them into Prometheus metrics. Combined with Grafana dashboards, this provides comprehensive real-time visibility into load balancer performance.

Key Features

  • Exposes 100+ metrics: connections, sessions, bytes in/out, queue depth, response times
  • Native Prometheus service discovery
  • Pre-built Grafana dashboard templates (Dashboard ID 367)
  • Alerting via Prometheus AlertManager
  • Multi-instance support — monitor many HAProxy nodes from one exporter

Docker Compose Deployment

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
version: "3.8"

services:
  haproxy:
    image: haproxy:2.9-alpine
    container_name: haproxy
    ports:
      - "80:80"
      - "443:443"
      - "8404:8404"
    volumes:
      - ./haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro
    networks:
      - monitoring-net

  haproxy-exporter:
    image: prom/haproxy-exporter:v0.15.0
    container_name: haproxy-exporter
    ports:
      - "9101:9101"
    command: >
      --haproxy.scrape-uri="http://haproxy:8404/;csv"
    depends_on:
      - haproxy
    networks:
      - monitoring-net
    restart: unless-stopped

  prometheus:
    image: prom/prometheus:latest
    container_name: prometheus
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
      - prometheus-data:/prometheus
    networks:
      - monitoring-net
    restart: unless-stopped

  grafana:
    image: grafana/grafana:latest
    container_name: grafana
    ports:
      - "3000:3000"
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin
    volumes:
      - grafana-data:/var/lib/grafana
      - ./grafana-datasources.yml:/etc/grafana/provisioning/datasources/datasources.yml:ro
    depends_on:
      - prometheus
    networks:
      - monitoring-net
    restart: unless-stopped

volumes:
  prometheus-data:
  grafana-data:

networks:
  monitoring-net:
    driver: bridge

Prometheus Configuration

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# prometheus.yml
global:
  scrape_interval: 15s
  evaluation_interval: 15s

scrape_configs:
  - job_name: "haproxy"
    static_configs:
      - targets: ["haproxy-exporter:9101"]

  - job_name: "prometheus"
    static_configs:
      - targets: ["localhost:9090"]

HAProxy Stats Configuration

Enable the stats endpoint in your haproxy.cfg:

1
2
3
4
5
6
7
listen stats
    bind *:8404
    stats enable
    stats uri /
    stats refresh 10s
    stats show-legends
    stats show-node

Key Metrics to Monitor

The exporter exposes metrics like:

  • haproxy_frontend_current_sessions — Active sessions per frontend
  • haproxy_backend_current_queue — Requests waiting in backend queue
  • haproxy_server_http_responses_5xx — 5xx error rate per server
  • haproxy_backend_up — Backend health status (1=up, 0=down)
  • haproxy_frontend_bytes_in_total / haproxy_frontend_bytes_out_total — Traffic volume

Example PromQL alert rule:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
groups:
  - name: haproxy-alerts
    rules:
      - alert: HAProxyBackendDown
        expr: haproxy_backend_up == 0
        for: 1m
        labels:
          severity: critical
        annotations:
          summary: "Backend {{ $labels.pxname }} is down"

      - alert: HAProxyHighErrorRate
        expr: |
          sum(rate(haproxy_server_http_responses_5xx[5m])) by (pxname)
          / sum(rate(haproxy_server_http_responses_total[5m])) by (pxname)
          > 0.05
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "High 5xx error rate on backend {{ $labels.pxname }}"

Approach 3: HAProxy Runtime API (Unix Socket)

The HAProxy Runtime API exposes a Unix domain socket that accepts commands for inspecting and modifying HAProxy’s runtime state. It’s the lowest-level management interface — built directly into HAProxy — and requires no additional software.

Key Features

  • Zero external dependencies — part of HAProxy itself
  • Real-time server enable/disable without reload
  • Dynamic weight adjustment for traffic shifting
  • Connection draining with graceful server shutdown
  • Access to internal statistics, counters, and health checks
  • Scriptable via any language that supports socket I/O

Configuration

Enable the socket in haproxy.cfg:

1
2
3
4
5
global
    log stdout format raw local0
    maxconn 4096
    stats socket /var/run/haproxy.sock mode 660 level admin expose-fd listeners
    stats timeout 30s

Mount the socket into your Docker container:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
services:
  haproxy:
    image: haproxy:2.9-alpine
    container_name: haproxy
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro
      - ./haproxy-socket:/var/run/haproxy
    networks:
      - haproxy-net

Common Runtime API Commands

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# Show all stats in CSV format
echo "show stat" | socat stdio /var/run/haproxy/haproxy.sock

# Show info (version, uptime, process info)
echo "show info" | socat stdio /var/run/haproxy/haproxy.sock

# Gracefully disable a server (drains existing connections)
echo "disable server backend_name/web3" | socat stdio /var/run/haproxy/haproxy.sock

# Re-enable a server
echo "enable server backend_name/web3" | socat stdio /var/run/haproxy/haproxy.sock

# Change server weight dynamically (traffic shifting)
echo "set weight backend_name/web3 50%" | socat stdio /var/run/haproxy/haproxy.sock

# Set a server to maintenance mode
echo "set server backend_name/web3 state maint" | socat stdio /var/run/haproxy/haproxy.sock

# Show all ACLs
echo "show acl" | socat stdio /var/run/haproxy/haproxy.sock

# Clear a specific counter
echo "clear counters" | socat stdio /var/run/haproxy/haproxy.sock

# Show SSL session info
echo "show ssl crt" | socat stdio /var/run/haproxy/haproxy.sock

Automating with a Python Script

For scripted deployments, a simple Python wrapper around the socket interface:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import socket
import os

SOCKET_PATH = "/var/run/haproxy/haproxy.sock"

def send_command(cmd):
    """Send a command to HAProxy via Unix socket."""
    with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as s:
        s.connect(SOCKET_PATH)
        s.sendall((cmd + "\n").encode())
        result = b""
        while True:
            chunk = s.recv(4096)
            if not chunk:
                break
            result += chunk
        return result.decode()

def drain_server(backend, server):
    """Gracefully drain a server before maintenance."""
    print(f"Draining {backend}/{server}...")
    print(send_command(f"disable server {backend}/{server}"))

def shift_traffic(backend, server, weight_pct):
    """Shift traffic to a server by adjusting its weight."""
    print(send_command(f"set weight {backend}/{server} {weight_pct}%"))

def get_backend_stats(backend):
    """Get CSV stats for a specific backend."""
    raw = send_command("show stat")
    lines = raw.strip().split("\n")
    header = lines[0].split(",")
    pxname_idx = header.index("pxname")
    for line in lines[1:]:
        fields = line.split(",")
        if fields[pxname_idx] == backend:
            svname = fields[header.index("svname")]
            status = fields[header.index("status")]
            print(f"  {svname}: {status}")

if __name__ == "__main__":
    print("=== Backend Status ===")
    get_backend_stats("http_back")
    
    print("\n=== Draining web3 for maintenance ===")
    drain_server("http_back", "web3")

Which Approach Should You Use?

Use Data Plane API when:

  • You need to programmatically add/remove backends and servers
  • Your infrastructure is dynamic (auto-scaling, container orchestration)
  • You want transactional configuration updates with rollback support
  • CI/CD pipelines need to modify HAProxy config as part of deployments

Use Prometheus Exporter + Grafana when:

  • Observability and monitoring is your primary need
  • You want dashboards, alerting, and historical trend analysis
  • You already use the Prometheus/Grafana stack for other services
  • You need SLA reporting and capacity planning data

Use Runtime API when:

  • You need low-level control without adding external dependencies
  • Simple server enable/disable/weight changes are sufficient
  • You’re building custom automation scripts in bash or Python
  • You need immediate access to internal counters and connection state

Production Recommendation

For most production deployments, combine all three:

  1. Data Plane API handles configuration management (adding backends, updating routes)
  2. Prometheus + Grafana provides monitoring, alerting, and dashboards
  3. Runtime API serves as the emergency escape hatch for quick server toggles during incidents

For related reading, see our HAProxy vs Envoy vs Nginx load balancer comparison, the complete guide to self-hosted load balancers, and our TLS termination proxy guide.

FAQ

What is the HAProxy Data Plane API used for?

The HAProxy Data Plane API is a REST API that allows you to programmatically manage HAProxy configuration — adding backends, servers, ACLs, and SSL certificates — without manually editing configuration files. It supports transactional updates that apply changes without dropping active connections, making it ideal for dynamic environments and CI/CD pipelines.

Can I use Prometheus HAProxy Exporter with HAProxy Enterprise?

Yes. The Prometheus HAProxy Exporter works with any HAProxy version that exposes the stats endpoint (HAProxy 1.5+). It parses the CSV output from the stats URI and converts it into Prometheus-compatible metrics. HAProxy Enterprise users also get additional built-in Prometheus support, but the open-source exporter works equally well.

How do I enable the HAProxy Runtime API in Docker?

Add stats socket /var/run/haproxy.sock mode 660 level admin to the global section of your haproxy.cfg, then mount the socket directory as a Docker volume (./haproxy-socket:/var/run/haproxy). You can then send commands via socat or any socket-capable programming language.

Is the Prometheus HAProxy Exporter still maintained?

The official prom/haproxy-exporter repository at prometheus/haproxy_exporter has been stable since March 2023. While it receives infrequent updates, this is because the HAProxy stats CSV format is well-established and hasn’t changed. The exporter reliably produces 100+ metrics and is used in thousands of production deployments. For newer features, some teams use the haproxy scrape configuration directly in Prometheus without a dedicated exporter.

Can I change server weights without reloading HAProxy?

Yes. Both the Runtime API and Data Plane API support dynamic weight changes without a full reload. Via the Runtime API: set weight backend_name/server_name 50%. Via the Data Plane API: use the PUT /v2/services/haproxy/configuration/servers/{name} endpoint. This is essential for canary deployments and gradual traffic shifting.

How do I set up alerting on HAProxy error rates?

Use the Prometheus HAProxy Exporter to expose metrics, then create AlertManager rules on haproxy_server_http_responses_5xx. A typical alert fires when the 5xx error rate exceeds 5% over a 5-minute window. You can also alert on haproxy_backend_current_queue (backlogged requests) and haproxy_backend_up == 0 (backend down).

What port does the HAProxy stats page use by default?

There is no default — you configure it yourself. Common choices are port 8404 or 9000. Add a listen stats section with bind *:8404 and stats enable. For production, always restrict access with stats auth admin:password or an ACL that limits access to internal IPs only.

Advertise here
Advertise here