Smart HAProxy Role Detection: Know Your Server's Status Instantly

7 min read
Smart HAProxy Role Detection: Know Your Server's Status Instantly

When managing a high availability HAProxy cluster, one of the most common moments of confusion happens right after you SSH into a server: "Wait, am I on the primary or secondary node?"

If you've ever found yourself frantically checking process lists or network interfaces just to figure out which HAProxy server you're on, this smart detection script will solve that problem forever.

The Problem: Which Server Am I On?

In a typical HA setup, you have multiple HAProxy servers where roles can change during failover events. When troubleshooting issues or performing maintenance, you need to know immediately:

  • Is this the active server handling traffic?
  • Is this a standby server ready for failover?
  • What's the server's current network configuration?

Making changes to the wrong node can cause service disruptions and manually checking server status wastes precious time during incidents.

The Solution: Intelligent Auto-Detection

Instead of manually checking server status every time you SSH in, let's create a script that automatically detects and displays the server's current role based on its actual network activity.

Creating the Detection Script

Create the script that will run every time someone logs in:

sudo nano /etc/profile.d/haproxy-status.sh

Here's the intelligent detection script:

#!/bin/bash

# Function to detect HAProxy role using multiple methods
detect_haproxy_role() {
    local hostname=$(hostname)
    local ip_addr=$(hostname -I | awk '{print $1}')
    local role="UNKNOWN"
    local status_icon="❓"
    local detection_method=""
    
    echo "🔍 Detecting HAProxy role for $hostname ($ip_addr)..."
    echo ""
    
    # Method 1: Check for Virtual IP (VIP) ownership
    # Most common in keepalived setups
    local vip_detected=false
    if command -v ip >/dev/null 2>&1; then
        # Look for secondary IPs that might be VIPs
        local secondary_ips=$(ip addr show | grep -E 'inet [0-9]+\.' | grep -v '127.0.0.1' | awk '{print $2}' | cut -d'/' -f1)
        local primary_ip=$(hostname -I | awk '{print $1}')
        
        for vip in $secondary_ips; do
            if [[ "$vip" != "$primary_ip" ]]; then
                vip_detected=true
                role="PRIMARY"
                status_icon="🟢"
                detection_method="Virtual IP detected: $vip"
                break
            fi
        done
    fi
    
    # Method 2: Check keepalived status if available
    if [[ "$role" == "UNKNOWN" ]] && command -v systemctl >/dev/null 2>&1; then
        if systemctl is-active keepalived >/dev/null 2>&1; then
            # Check keepalived logs for MASTER state
            if journalctl -u keepalived --since "5 minutes ago" -q | grep -q "Transition to MASTER STATE" 2>/dev/null; then
                role="PRIMARY"
                status_icon="🟢"
                detection_method="keepalived MASTER state"
            elif journalctl -u keepalived --since "5 minutes ago" -q | grep -q "Transition to BACKUP STATE" 2>/dev/null; then
                role="SECONDARY" 
                status_icon="🟡"
                detection_method="keepalived BACKUP state"
            fi
        fi
    fi
    
    # Method 3: Check HAProxy stats socket for role information
    if [[ "$role" == "UNKNOWN" ]] && [[ -S /run/haproxy/admin.sock ]]; then
        # Try to get server status from stats socket
        local stats_output=$(echo "show stat" | socat stdio /run/haproxy/admin.sock 2>/dev/null)
        if [[ -n "$stats_output" ]]; then
            # Count active backends - primary usually has more active connections
            local active_servers=$(echo "$stats_output" | grep -c ",UP,")
            local total_servers=$(echo "$stats_output" | grep -c "^[^#]")
            
            if [[ $active_servers -gt 0 ]]; then
                # If we're actively load balancing, likely primary
                role="PRIMARY"
                status_icon="🟢"
                detection_method="Active backends: $active_servers/$total_servers"
            fi
        fi
    fi
    
    # Method 4: Check network traffic patterns
    if [[ "$role" == "UNKNOWN" ]]; then
        # Check for incoming connections on load balancer ports
        local https_connections=$(netstat -an 2>/dev/null | grep :443 | grep ESTABLISHED | wc -l)
        local http_connections=$(netstat -an 2>/dev/null | grep :80 | grep ESTABLISHED | wc -l)
        local total_connections=$((https_connections + http_connections))
        
        # If we have active connections, we're likely primary
        if [[ $total_connections -gt 5 ]]; then
            role="PRIMARY"
            status_icon="🟢"  
            detection_method="Active connections: $total_connections"
        elif netstat -tlnp 2>/dev/null | grep -q ":443.*haproxy" && \
             netstat -tlnp 2>/dev/null | grep -q ":80.*haproxy"; then
            # HAProxy is running but no significant traffic - likely secondary
            role="SECONDARY"
            status_icon="🟡"
            detection_method="HAProxy running, low traffic"
        fi
    fi
    
    # Method 5: Check for HAProxy process priority or resource usage
    if [[ "$role" == "UNKNOWN" ]]; then
        if pgrep haproxy >/dev/null; then
            # Check CPU usage of HAProxy process
            local haproxy_cpu=$(ps -C haproxy -o %cpu --no-headers | awk '{sum += $1} END {print sum}' 2>/dev/null)
            if [[ -n "$haproxy_cpu" ]] && (( $(echo "$haproxy_cpu > 1" | bc -l 2>/dev/null || echo 0) )); then
                role="PRIMARY"
                status_icon="🟢"
                detection_method="High CPU usage: ${haproxy_cpu}%"
            else
                role="SECONDARY"
                status_icon="🟡" 
                detection_method="Low CPU usage: ${haproxy_cpu:-0}%"
            fi
        fi
    fi
    
    # Fallback: If still unknown, default to secondary
    if [[ "$role" == "UNKNOWN" ]]; then
        role="SECONDARY"
        status_icon="⚠️"
        detection_method="Unable to determine - assuming standby"
    fi
    
    # Display results
    echo "$status_icon HAProxy $role - $hostname ($ip_addr)"
    echo "   Detection: $detection_method"
    
    if [[ "$role" == "PRIMARY" ]]; then
        echo "   Status: ACTIVE - Handling live traffic"
        echo "   Role: Load balancer primary node"
        
        # Show connection details
        local https_conns=$(netstat -an 2>/dev/null | grep :443 | grep ESTABLISHED | wc -l)
        local http_conns=$(netstat -an 2>/dev/null | grep :80 | grep ESTABLISHED | wc -l)
        echo "   Active connections: HTTPS=$https_conns, HTTP=$http_conns"
        
    else
        echo "   Status: STANDBY - Ready for failover"  
        echo "   Role: Load balancer backup node"
        echo "   Info: Not currently handling primary traffic"
    fi
    
    # Show additional diagnostics
    echo ""
    echo "📊 Additional Information:"
    
    # HAProxy status
    if pgrep haproxy >/dev/null; then
        echo "   HAProxy Process: ✅ Running (PID: $(pgrep haproxy | tr '\n' ' '))"
    else
        echo "   HAProxy Process: ❌ Not running"
    fi
    
    # Keepalived status
    if command -v systemctl >/dev/null 2>&1 && systemctl is-active keepalived >/dev/null 2>&1; then
        echo "   Keepalived: ✅ Active"
    elif command -v systemctl >/dev/null 2>&1; then
        echo "   Keepalived: ❌ Not active"
    fi
    
    # Port bindings
    local bound_ports=$(netstat -tlnp 2>/dev/null | grep haproxy | awk '{print $4}' | cut -d: -f2 | sort -n | tr '\n' ' ')
    if [[ -n "$bound_ports" ]]; then
        echo "   Bound Ports: $bound_ports"
    fi
    
    # Management links
    echo ""
    echo "🔧 Management:"
    echo "   Stats Dashboard: https://$ip_addr:8404/"
    if [[ -S /run/haproxy/admin.sock ]]; then
        echo "   Admin Socket: ✅ /run/haproxy/admin.sock"
        echo "   Socket Commands: echo 'show stat' | socat stdio /run/haproxy/admin.sock"
    else
        echo "   Admin Socket: ❌ Not available"
    fi
    echo ""
}

# Only run for interactive SSH sessions
if [[ $- == *i* ]] && [[ -n "$SSH_CLIENT" || -n "$SSH_TTY" ]]; then
    detect_haproxy_role
fi

Make the Script Executable

sudo chmod +x /etc/profile.d/haproxy-status.sh

How the Detection Works

The script uses a simple but reliable method to determine server role:

Network Port Detection

netstat -tlnp | grep ":443.*haproxy"
netstat -tlnp | grep ":80.*haproxy"

Primary Server: Will show HAProxy actively listening on ports 80 and 443

tcp 0 0 0.0.0.0:443 0.0.0.0:* LISTEN 1234/haproxy
tcp 0 0 0.0.0.0:80  0.0.0.0:* LISTEN 1234/haproxy

Secondary Server: Either won't have HAProxy running, or HAProxy will be running but not bound to public interfaces (depending on your HA setup)

Connection Counting

The script also shows current HTTPS connections, giving you immediate insight into server load:

netstat -an | grep :443 | grep ESTABLISHED | wc -l

What You'll See When Logging In

On the Primary Server:

🟢 HAProxy PRIMARY - haproxy01 (192.168.1.10)
   Status: ACTIVE - Handling live traffic
   Role: Load balancer primary node
   Active HTTPS connections: 47
   Stats Dashboard: https://192.168.1.10:8404/
   Admin Socket: socat stdio /run/haproxy/admin.sock

On the Secondary Server:

🟡 HAProxy SECONDARY - haproxy02 (192.168.1.11)
   Status: STANDBY - Ready for failover
   Role: Load balancer backup node
   Info: Not currently handling public traffic
   Stats Dashboard: https://192.168.1.11:8404/
   Admin Socket: socat stdio /run/haproxy/admin.sock

Benefits in Daily Operations

Immediate Context

  • Zero confusion about which server you're on
  • Instant awareness of the server's role in your cluster
  • Quick access to monitoring and admin tools

Mistake Prevention

  • Visual warnings when you're on the active production server
  • Clear identification prevents configuration errors
  • Role awareness helps you make appropriate changes

Faster Troubleshooting

  • No time wasted checking server status manually
  • Immediate connection count shows current load
  • Direct links to stats and admin interfaces

Advanced Enhancements

Add Cluster Health Information

Extend the script to show overall cluster status:

# Add this to the detect_haproxy_role function
echo "   Cluster Status:"

# Check if HAProxy service is running
if systemctl is-active --quiet haproxy; then
    echo "     HAProxy Service: ✅ Running"
else
    echo "     HAProxy Service: ❌ Stopped"
fi

# Show backend health summary
if command -v socat >/dev/null 2>&1; then
    local backend_count=$(echo 'show stat' | socat stdio /run/haproxy/admin.sock 2>/dev/null | grep -c ',UP,' || echo "0")
    local total_backends=$(echo 'show stat' | socat stdio /run/haproxy/admin.sock 2>/dev/null | grep -c 'BACKEND' || echo "0")
    echo "     Healthy Backends: $backend_count/$total_backends"
fi

Integration with Monitoring

You can also integrate this with your monitoring systems:

# Send role information to your monitoring system
curl -X POST https://your-monitoring-system/api/metrics \
     -d "haproxy_role{server=\"$hostname\",role=\"$role\"} 1"

Customization for Your Environment

Adjust Detection Logic

Modify the network checks for your specific setup:

# For custom ports
if netstat -tlnp | grep -q ":8080.*haproxy"; then

# For specific interfaces
if netstat -tlnp | grep -q "192.168.1.10:443.*haproxy"; then

# For multiple services
if pgrep haproxy >/dev/null && systemctl is-active --quiet keepalived; then

Customize the Display

Tailor the output for your team's needs:

# Add timestamp
echo "   Last checked: $(date '+%Y-%m-%d %H:%M:%S')"

# Add uptime information  
echo "   Server uptime: $(uptime -p)"

# Add load information
echo "   System load: $(uptime | awk -F'load average:' '{print $2}' | trim)"

Testing Your Implementation

Verify the Script Works

# Test the script manually
source /etc/profile.d/haproxy-status.sh

# Check it runs on login
ssh username@your-haproxy-server

Test Failover Scenarios

  1. Stop HAProxy on primary: SSH in and verify it shows as SECONDARY
  2. Start HAProxy on secondary: Verify it correctly detects PRIMARY role
  3. Simulate network issues: Ensure detection remains accurate

Validate Performance

The script should execute quickly (under 100ms) to avoid slowing down logins.

Troubleshooting

Script Not Running

  • Check file permissions: ls -la /etc/profile.d/haproxy-status.sh
  • Verify bash syntax: bash -n /etc/profile.d/haproxy-status.sh
  • Test interactivity detection: echo $- (should contain 'i' for interactive)

Incorrect Role Detection

  • Manual network check: netstat -tlnp | grep haproxy
  • Verify HAProxy process: pgrep haproxy
  • Check HAProxy configuration: haproxy -c -f /etc/haproxy/haproxy.cfg

Performance Issues

  • Add timing: time source /etc/profile.d/haproxy-status.sh
  • Optimize network commands if needed
  • Consider caching detection results for very frequent logins

Security Considerations

  • Minimal information exposure: The script only shows what's needed
  • No sensitive data: Avoids displaying passwords or private configuration
  • Interactive sessions only: Doesn't run for automated processes
  • Local detection: Uses only local system information

This simple role detection script eliminates one of the most common sources of confusion in HAProxy cluster management. By automatically detecting and displaying server role on login, you'll:

  • Save time during troubleshooting
  • Prevent configuration mistakes
  • Improve team coordination
  • Gain immediate situational awareness

The script is lightweight, fast and adapts to role changes automatically. Whether you're managing a simple active/standby pair or a complex multi-node cluster, this enhancement will make your daily operations smoother and safer.

Set it up once and never wonder "which server am I on?" again.