No description
Find a file
2025-12-05 14:35:53 +01:00
haproxy_health Installable package 2025-12-05 12:16:31 +01:00
.gitignore Installable package 2025-12-05 12:16:31 +01:00
.python-version Installable package 2025-12-05 12:16:31 +01:00
justfile Installable package 2025-12-05 12:16:31 +01:00
pyproject.toml Installable package 2025-12-05 12:16:31 +01:00
README.md Streamline README 2025-12-05 14:35:53 +01:00
uv.lock Installable package 2025-12-05 12:16:31 +01:00

HAProxy Health Check API

Health check API for HAProxy frontends and backends, designed for integration with keepalived/IPVS load balancers. Monitors HAProxy stats socket and exposes HTTP health endpoints on port 9300.

Features

  • Frontend and backend health checks via HTTP API
  • HAProxy stats socket integration
  • Dual-stack IPv4/IPv6 support
  • JSON and plain text responses
  • Configurable via CLI args or environment variables

Installation

Requires Python 3.11+ and uv:

# Install uv if needed
curl -LsSf https://astral.sh/uv/install.sh | env UV_INSTALL_DIR=/usr/local/bin sh

# Install from git
uv pip install git+https://nrtn.dev/catalyst/haproxy-health.git

# Or run without installing (recommended for systemd)
uv run --with haproxy-health@git+https://nrtn.dev/catalyst/haproxy-health.git haproxy-health

Configuration

# CLI options
haproxy-health --port 9300 --host "*" --threads 4

# Or use environment variables
export HAPROXY_HEALTH_PORT=9300
export HAPROXY_HEALTH_HOST="*"        # "*" (all), "0.0.0.0" (IPv4), "::" (IPv6)
export HAPROXY_HEALTH_THREADS=4

API Endpoints

# Frontend health check (returns 200/503)
curl http://localhost:9300/health?frontend=http_front
curl http://localhost:9300/health?frontend=https_front

# Backend health check (returns 200/503)
curl http://localhost:9300/health?backend=http_backend

# With JSON summary
curl http://localhost:9300/health?frontend=http_front&summary=true

# Status overview (all frontends/backends)
curl http://localhost:9300/status

Systemd Integration

Create /etc/systemd/system/haproxy-health.service:

[Unit]
Description=HAProxy Health Check API
After=haproxy.service
Requires=haproxy.service

[Service]
Type=simple
ExecStart=/usr/local/bin/uv run --with haproxy-health@git+https://nrtn.dev/catalyst/haproxy-health.git haproxy-health
Restart=always
RestartSec=5
User=haproxy
Group=haproxy
Environment="UV_CACHE_DIR=/var/cache/uv"
Environment="HAPROXY_HEALTH_PORT=9300"

[Install]
WantedBy=multi-user.target

Enable and start:

sudo mkdir -p /var/cache/uv && sudo chown haproxy:haproxy /var/cache/uv
sudo systemctl daemon-reload
sudo systemctl enable --now haproxy-health.service

# Update to latest version
sudo rm -rf /var/cache/uv/* && sudo systemctl restart haproxy-health.service

How It Works

Keepalived → HTTP GET /health?frontend=http_front (port 9300)
             ↓
Health Check API → Query HAProxy stats socket
                   ↓
HAProxy Stats → Parse backend server status
                ↓
Return 200 (healthy) or 503 (unhealthy)

Development

# Clone and run locally
git clone https://nrtn.dev/catalyst/haproxy-health.git
cd haproxy-health
uv run --with . haproxy-health

# Or install in development mode
uv pip install -e .
haproxy-health

# After making changes
# 1. Edit haproxy_health/main.py
# 2. Test locally
# 3. Commit and push
# 4. Update deployments by restarting (clears uv cache)

Package structure:

haproxy-health/
├── pyproject.toml
└── haproxy_health/
    ├── __init__.py
    └── main.py          # Flask app with frontend/backend mapping

Troubleshooting

# Port already in use
sudo lsof -i :9300
sudo systemctl stop haproxy-health
# or use --port 9301

# Check service logs
journalctl -u haproxy-health.service -f

# Check listening sockets (should show both IPv4/IPv6)
ss -tlnp | grep 9300

# Check HAProxy socket permissions
ls -la /var/run/haproxy.sock

# Python version issues (requires 3.11+)
uv python install 3.11
uv python pin 3.11

Notes

  • Port 9300 chosen to avoid conflicts with common exporters
  • No authentication - restrict access via firewall
  • Dual-stack IPv6/IPv4 support automatic
  • HAProxy stats socket must be readable by service user