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
- Stop HAProxy on primary: SSH in and verify it shows as SECONDARY
- Start HAProxy on secondary: Verify it correctly detects PRIMARY role
- 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.