Contents

HAProxy Dynamic Socket: Runtime Backend Management via Stats Socket

HAProxy Dynamic Socket: Runtime Backend Management via Stats Socket

haproxy-dynamic-socket provides shell scripts for dynamically managing HAProxy configuration at runtime through the stats socket — adding/removing servers, draining backends, and changing weights without reloading HAProxy.

HAProxy Stats Socket

HAProxy exposes a Unix socket (or TCP socket) for runtime management. Commands sent to it take effect immediately without a reload.

Enable it in haproxy.cfg:

global
    stats socket /run/haproxy/admin.sock mode 660 level admin expose-fd listeners
    stats timeout 30s

Core Interface

All interaction goes through socat (or nc):

haproxy_cmd() {
    echo "$1" | socat stdio /run/haproxy/admin.sock
}

Server Management Scripts

Add a Server to a Backend

#!/bin/bash
# add-server.sh <backend> <name> <host:port>

BACKEND="$1"
SERVER_NAME="$2"
SERVER_ADDR="$3"

if [[ -z "$BACKEND" || -z "$SERVER_NAME" || -z "$SERVER_ADDR" ]]; then
    echo "Usage: $0 <backend> <name> <host:port>"
    exit 1
fi

echo "add server ${BACKEND}/${SERVER_NAME} ${SERVER_ADDR}" \
    | socat stdio /run/haproxy/admin.sock

echo "enable server ${BACKEND}/${SERVER_NAME}" \
    | socat stdio /run/haproxy/admin.sock

echo "Server ${SERVER_NAME} added to backend ${BACKEND}"

Drain and Remove a Server

#!/bin/bash
# drain-server.sh <backend> <name>

BACKEND="$1"
SERVER_NAME="$2"
DRAIN_WAIT="${3:-30}"

# Set to drain mode — no new connections
echo "set server ${BACKEND}/${SERVER_NAME} state drain" \
    | socat stdio /run/haproxy/admin.sock

echo "Draining ${SERVER_NAME} (waiting ${DRAIN_WAIT}s)..."
sleep "$DRAIN_WAIT"

# Check current sessions
SESSIONS=$(echo "show servers conn ${BACKEND}" \
    | socat stdio /run/haproxy/admin.sock \
    | awk -v srv="${SERVER_NAME}" '$1==srv {print $NF}')

if [[ "${SESSIONS:-0}" -gt 0 ]]; then
    echo "Warning: ${SESSIONS} sessions still active on ${SERVER_NAME}"
fi

# Remove the server
echo "del server ${BACKEND}/${SERVER_NAME}" \
    | socat stdio /run/haproxy/admin.sock

echo "Server ${SERVER_NAME} removed from backend ${BACKEND}"

Adjust Server Weight

#!/bin/bash
# set-weight.sh <backend> <name> <weight>

BACKEND="$1"
SERVER_NAME="$2"
WEIGHT="$3"

echo "set server ${BACKEND}/${SERVER_NAME} weight ${WEIGHT}" \
    | socat stdio /run/haproxy/admin.sock

Status and Monitoring

#!/bin/bash
# show-backend.sh <backend>

BACKEND="${1:-}"

if [[ -n "$BACKEND" ]]; then
    echo "show servers state ${BACKEND}" | socat stdio /run/haproxy/admin.sock
else
    echo "show stat" | socat stdio /run/haproxy/admin.sock | column -t -s ','
fi

Health Check Override

Temporarily disable/enable health checks without removing a server:

# Disable health checks (manual state management)
echo "disable health ${BACKEND}/${SERVER_NAME}" | socat stdio /run/haproxy/admin.sock

# Force server into UP state regardless of health check
echo "set server ${BACKEND}/${SERVER_NAME} health up" | socat stdio /run/haproxy/admin.sock

# Re-enable automatic health checks
echo "enable health ${BACKEND}/${SERVER_NAME}" | socat stdio /run/haproxy/admin.sock

Use Case: Zero-Downtime Deployment

#!/bin/bash
# rolling-deploy.sh — replace servers one at a time

BACKEND="web_backend"
NEW_SERVERS=("web3:8080" "web4:8080")
OLD_SERVERS=("web1" "web2")

for i in "${!OLD_SERVERS[@]}"; do
    OLD="${OLD_SERVERS[$i]}"
    NEW_NAME="new_web$((i+1))"
    NEW_ADDR="${NEW_SERVERS[$i]}"

    # Add new server
    bash add-server.sh "$BACKEND" "$NEW_NAME" "$NEW_ADDR"

    # Drain and remove old
    bash drain-server.sh "$BACKEND" "$OLD" 60

    echo "Rotated $OLD$NEW_NAME"
done

Conclusion

HAProxy’s stats socket provides a powerful, zero-downtime management interface. These scripts wrap the raw socket commands into reusable tools for common operations like rolling deployments, weight adjustments, and backend health overrides — all without touching HAProxy’s config file or triggering a reload.

Source: GitHub