When configuring Haproxy to listen on all network interfaces, you might encounter some unexpected behavior with IPv4 and IPv6 binding. The standard approach using bind :80
might not always work as intended, especially when dealing with dual-stack environments.
The most straightforward way to bind to both IPv4 and IPv6 is:
listen web
bind :80 v4v6
bind :::80 v6only
This configuration explicitly creates two separate listeners:
- A dual-stack listener for IPv4 (and potentially IPv6)
- An IPv6-only listener on all IPv6 interfaces
The v4v6
parameter tells Haproxy to create a socket that can accept both IPv4 and IPv6 connections, while v6only
ensures pure IPv6 binding. This combination guarantees coverage of all possible incoming connections.
For a slightly more concise configuration, you could use:
frontend http-in
bind :::80
bind 0.0.0.0:80
However, this doesn't provide exactly the same behavior as the first solution, as it doesn't explicitly handle the dual-stack case.
After applying your configuration, verify it works with:
ss -tulnp | grep haproxy
netstat -tulnp | grep haproxy
You should see both IPv4 and IPv6 listeners on port 80.
When binding to multiple interfaces:
- Each additional bind statement creates a separate file descriptor
- Consider connection handling limits in your system
- Monitor resource usage under load
Binding to all interfaces means your service is accessible from any network:
- Always implement proper access control lists
- Consider using firewall rules for additional protection
- Regularly audit your exposed services
If you encounter problems:
- Verify IPv6 is enabled on your system
- Check for port conflicts with
netstat
orss
- Review Haproxy logs for binding errors
- Test connectivity from both IPv4 and IPv6 clients
Many sysadmins need HAProxy to simultaneously listen on both IPv4 (0.0.0.0) and IPv6 (::) addresses for maximum compatibility. The naive approach of simply using bind :80
often doesn't work as expected in modern environments.
On Linux systems, when you specify:
bind :80
This actually creates a dual-stack socket by default. However, the behavior differs across:
- Kernel versions (pre/post 3.9)
- HAProxy versions (1.5+ vs older)
- Presence of
net.ipv6.bindv6only
sysctl
For explicit control across all environments:
frontend web
bind 0.0.0.0:80
bind :::80 v6only
option socket-stats
Key advantages:
- Clearly separates IPv4 and IPv6 sockets
- Works predictably across all kernel versions
- Allows different settings per protocol if needed
When binding multiple ports, use this pattern:
frontend multi_port
bind 0.0.0.0:80-85
bind :::80-85 v6only
mode http
For high-traffic setups:
global
tune.bufsize 16384
tune.maxrewrite 1024
frontend https
bind 0.0.0.0:443 ssl crt /etc/ssl/certs/
bind :::443 v6only ssl crt /etc/ssl/certs/
tcp-request inspect-delay 5s
Verify your bindings with:
ss -tulnp | grep haproxy
netstat -tulnp | grep haproxy # on older systems
Expected output should show both:
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN
tcp 0 0 :::80 :::* LISTEN