Configure HAProxy 2.8 to automatically discover and load balance traffic across Docker containers with health checks, service discovery, and SSL termination for production-grade dynamic routing.
Prerequisites
- Docker installed and running
- Root or sudo access
- Basic understanding of load balancing concepts
- Domain names configured (for SSL setup)
What this solves
HAProxy with Docker backend discovery lets you automatically route traffic to container services without manual configuration updates. When containers start, stop, or scale, HAProxy detects these changes and updates its load balancing pool dynamically. This eliminates the need to restart or reconfigure HAProxy every time your container infrastructure changes.
Step-by-step installation
Install HAProxy 2.8
First, install HAProxy 2.8 which includes the Docker API integration features needed for dynamic backend discovery.
sudo apt update
sudo apt install -y software-properties-common
sudo add-apt-repository -y ppa:vbernat/haproxy-2.8
sudo apt update
sudo apt install -y haproxy=2.8.* docker.io
Configure Docker daemon for HAProxy access
HAProxy needs access to Docker's API to discover running containers. Configure Docker to expose its API socket with proper permissions.
sudo usermod -aG docker haproxy
sudo systemctl restart docker
sudo systemctl enable docker
Create HAProxy configuration with Docker backend discovery
This configuration enables HAProxy to automatically discover Docker containers labeled for load balancing and perform health checks.
global
daemon
user haproxy
group haproxy
log stdout local0
chroot /var/lib/haproxy
stats socket /run/haproxy/admin.sock mode 660 level admin
stats timeout 30s
# SSL configuration
ssl-default-bind-ciphers ECDHE+AESGCM:ECDHE+CHACHA20:RSA+AESGCM:RSA+AES:!aNULL:!MD5:!DSS
ssl-default-bind-options ssl-min-ver TLSv1.2 no-tls-tickets
defaults
mode http
timeout connect 5000ms
timeout client 50000ms
timeout server 50000ms
option httplog
option dontlognull
option http-server-close
option forwardfor except 127.0.0.0/8
option redispatch
retries 3
# Health check defaults
option httpchk GET /health
http-check expect status 200
frontend web_frontend
bind *:80
bind *:443 ssl crt /etc/haproxy/certs/
# Redirect HTTP to HTTPS
redirect scheme https code 301 if !{ ssl_fc }
# Security headers
http-response set-header Strict-Transport-Security "max-age=31536000; includeSubDomains"
http-response set-header X-Content-Type-Options nosniff
http-response set-header X-Frame-Options DENY
http-response set-header X-XSS-Protection "1; mode=block"
# Route based on Host header
acl is_app_service hdr(host) -i app.example.com
acl is_api_service hdr(host) -i api.example.com
use_backend app_containers if is_app_service
use_backend api_containers if is_api_service
default_backend web_containers
Dynamic backend for web containers
backend web_containers
balance roundrobin
option httpchk GET /health
http-check expect status 200
# Docker service discovery template
server-template web 10 _web._tcp.service.consul:80 check resolvers consul resolve-prefer ipv4
Dynamic backend for app containers
backend app_containers
balance roundrobin
option httpchk GET /health
http-check expect status 200
backend api_containers
balance roundrobin
option httpchk GET /api/health
http-check expect status 200
HAProxy statistics
listen stats
bind *:8404
stats enable
stats uri /stats
stats refresh 30s
stats admin if TRUE
Create Docker service discovery script
This script monitors Docker containers and updates HAProxy configuration dynamically when containers are created or destroyed.
#!/usr/bin/env python3
import docker
import time
import subprocess
import logging
from pathlib import Path
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
class HAProxyDockerDiscovery:
def __init__(self):
self.client = docker.from_env()
self.config_path = '/etc/haproxy/haproxy.cfg'
self.template_path = '/etc/haproxy/haproxy.cfg.template'
self.last_config_hash = None
def get_running_containers(self):
containers = []
for container in self.client.containers.list():
labels = container.labels
if 'haproxy.enable' in labels and labels['haproxy.enable'] == 'true':
container_info = {
'name': container.name,
'ip': self.get_container_ip(container),
'port': labels.get('haproxy.port', '80'),
'backend': labels.get('haproxy.backend', 'web_containers'),
'health_check': labels.get('haproxy.health_check', '/health')
}
containers.append(container_info)
return containers
def get_container_ip(self, container):
networks = container.attrs['NetworkSettings']['Networks']
for network_name, network_info in networks.items():
if network_info['IPAddress']:
return network_info['IPAddress']
return None
def generate_backend_config(self, containers):
backends = {}
for container in containers:
backend_name = container['backend']
if backend_name not in backends:
backends[backend_name] = []
backends[backend_name].append(container)
config_lines = []
for backend_name, backend_containers in backends.items():
config_lines.append(f"\nbackend {backend_name}")
config_lines.append(" balance roundrobin")
config_lines.append(" option httpchk GET /health")
config_lines.append(" http-check expect status 200")
for i, container in enumerate(backend_containers):
server_line = f" server {container['name']} {container['ip']}:{container['port']} check"
config_lines.append(server_line)
return '\n'.join(config_lines)
def update_config(self):
containers = self.get_running_containers()
backend_config = self.generate_backend_config(containers)
# Read template and replace backend sections
with open('/etc/haproxy/haproxy.cfg.template', 'r') as f:
template_content = f.read()
# Replace dynamic backends marker with generated config
new_config = template_content.replace('{{DYNAMIC_BACKENDS}}', backend_config)
# Write new configuration
with open(self.config_path, 'w') as f:
f.write(new_config)
# Reload HAProxy if configuration changed
subprocess.run(['sudo', 'systemctl', 'reload', 'haproxy'], check=True)
logger.info(f"Updated HAProxy config with {len(containers)} containers")
def run(self):
logger.info("Starting HAProxy Docker discovery service")
while True:
try:
self.update_config()
time.sleep(10) # Check every 10 seconds
except Exception as e:
logger.error(f"Error updating configuration: {e}")
time.sleep(5)
if __name__ == '__main__':
discovery = HAProxyDockerDiscovery()
discovery.run()
Make the discovery script executable
Set proper permissions for the discovery script and create a systemd service to run it automatically.
sudo chmod +x /usr/local/bin/haproxy-docker-discovery.py
sudo pip3 install docker
Create systemd service for discovery
This service ensures the Docker discovery script runs automatically and restarts if it fails.
[Unit]
Description=HAProxy Docker Discovery Service
After=docker.service haproxy.service
Requires=docker.service
[Service]
Type=simple
User=root
Group=root
ExecStart=/usr/local/bin/haproxy-docker-discovery.py
Restart=always
RestartSec=5
Environment=PYTHONUNBUFFERED=1
[Install]
WantedBy=multi-user.target
Create SSL certificate directory
Set up the directory structure for SSL certificates with proper permissions.
sudo mkdir -p /etc/haproxy/certs
sudo chown haproxy:haproxy /etc/haproxy/certs
sudo chmod 750 /etc/haproxy/certs
Generate self-signed certificate for testing
Create a test SSL certificate. Replace this with your real certificates in production.
sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout /tmp/example.com.key \
-out /tmp/example.com.crt \
-subj "/C=US/ST=State/L=City/O=Organization/CN=example.com"
Combine certificate and key for HAProxy
sudo cat /tmp/example.com.crt /tmp/example.com.key > /etc/haproxy/certs/example.com.pem
sudo chown haproxy:haproxy /etc/haproxy/certs/example.com.pem
sudo chmod 600 /etc/haproxy/certs/example.com.pem
sudo rm /tmp/example.com.key /tmp/example.com.crt
Create configuration template
Copy the current configuration as a template for the discovery script to use.
sudo cp /etc/haproxy/haproxy.cfg /etc/haproxy/haproxy.cfg.template
Enable and start services
Start HAProxy and the discovery service, enabling them to start automatically on boot.
sudo systemctl enable --now haproxy
sudo systemctl enable --now haproxy-docker-discovery
sudo systemctl status haproxy
sudo systemctl status haproxy-docker-discovery
Create test Docker containers
Launch example containers with the correct labels for HAProxy to discover them automatically.
# Create a simple web server container
docker run -d \
--name web1 \
--label haproxy.enable=true \
--label haproxy.backend=web_containers \
--label haproxy.port=80 \
nginx:alpine
Create another web server for load balancing
docker run -d \
--name web2 \
--label haproxy.enable=true \
--label haproxy.backend=web_containers \
--label haproxy.port=80 \
nginx:alpine
Create an API service container
docker run -d \
--name api1 \
--label haproxy.enable=true \
--label haproxy.backend=api_containers \
--label haproxy.port=3000 \
--label haproxy.health_check=/api/health \
node:alpine sh -c "npm init -y && npm install express && echo 'const express = require(\"express\"); const app = express(); app.get(\"/api/health\", (req, res) => res.json({status: \"ok\"})); app.listen(3000);' > server.js && node server.js"
Configure firewall rules
Open the necessary ports for HTTP, HTTPS, and HAProxy stats interface.
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 8404/tcp
sudo ufw reload
Configure health checks and service discovery
Advanced health check configuration
Configure more sophisticated health checks that can detect application-level failures, not just connectivity.
# Add to backend sections for advanced health checks
backend web_containers
balance roundrobin
# Multi-layer health checks
option httpchk GET /health HTTP/1.1\r\nHost:\ localhost
http-check expect status 200
http-check expect string "status":"ok"
# Health check timing
timeout check 5s
# Mark server down after 3 failed checks
default-server inter 10s downinter 5s rise 2 fall 3 slowstart 60s maxconn 256 maxqueue 128 weight 100
Implement service discovery with Consul
For production environments, integrate with Consul for more robust service discovery. First install Consul.
curl -fsSL https://apt.releases.hashicorp.com/gpg | sudo apt-key add -
sudo apt-add-repository "deb [arch=amd64] https://apt.releases.hashicorp.com $(lsb_release -cs) main"
sudo apt update
sudo apt install -y consul
Configure Consul for service registration
Set up Consul to automatically register Docker services and provide DNS resolution for HAProxy.
datacenter = "dc1"
data_dir = "/opt/consul"
log_level = "INFO"
node_name = "haproxy-node"
bind_addr = "127.0.0.1"
client_addr = "127.0.0.1"
ui_config {
enabled = true
}
connect {
enabled = true
}
server = true
bootstrap_expect = 1
Update HAProxy for Consul DNS resolution
Configure HAProxy to use Consul for DNS-based service discovery with automatic backend updates.
# Add resolver section to global configuration
resolvers consul
nameserver consul 127.0.0.1:8600
accepted_payload_size 8192
hold valid 5s
Update backend to use Consul DNS
backend web_containers
balance roundrobin
option httpchk GET /health
http-check expect status 200
# Use Consul service discovery
server-template web- 10 web.service.consul:80 check resolvers consul init-addr none
Implement SSL termination and security
Configure SSL with Let's Encrypt
For production, replace the self-signed certificate with Let's Encrypt certificates. Install certbot first.
sudo apt install -y certbot
sudo systemctl stop haproxy
Obtain SSL certificates
Generate Let's Encrypt certificates and convert them to HAProxy format. Replace example.com with your actual domain.
sudo certbot certonly --standalone -d example.com -d api.example.com -d app.example.com
Convert certificates to HAProxy format
sudo mkdir -p /etc/haproxy/certs
sudo cat /etc/letsencrypt/live/example.com/fullchain.pem \
/etc/letsencrypt/live/example.com/privkey.pem \
> /etc/haproxy/certs/example.com.pem
sudo chown haproxy:haproxy /etc/haproxy/certs/example.com.pem
sudo chmod 600 /etc/haproxy/certs/example.com.pem
Configure certificate renewal
Set up automatic certificate renewal with a script that updates HAProxy after renewal.
#!/bin/bash
Renew certificates
certbot renew --quiet
Update HAProxy certificates
for domain in $(certbot certificates 2>/dev/null | grep 'Certificate Name' | cut -d':' -f2 | tr -d ' '); do
if [ -d "/etc/letsencrypt/live/$domain" ]; then
cat /etc/letsencrypt/live/$domain/fullchain.pem \
/etc/letsencrypt/live/$domain/privkey.pem \
> /etc/haproxy/certs/$domain.pem
chown haproxy:haproxy /etc/haproxy/certs/$domain.pem
chmod 600 /etc/haproxy/certs/$domain.pem
fi
done
Reload HAProxy to use new certificates
systemctl reload haproxy
Schedule certificate renewal
Add a cron job to automatically renew certificates before they expire.
sudo chmod +x /usr/local/bin/renew-haproxy-certs.sh
sudo crontab -e
Add this line to run renewal twice daily
0 2,14 * /usr/local/bin/renew-haproxy-certs.sh
Enhance security headers and rate limiting
Add comprehensive security headers and implement rate limiting to protect against common attacks.
frontend web_frontend
bind *:80
bind *:443 ssl crt /etc/haproxy/certs/ alpn h2,http/1.1
# Rate limiting
stick-table type ip size 100k expire 30s store http_req_rate(10s),http_err_rate(10s)
http-request track-sc0 src
http-request deny if { sc_http_req_rate(0) gt 20 }
http-request deny if { sc_http_err_rate(0) gt 10 }
# Security headers
http-response set-header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
http-response set-header X-Content-Type-Options nosniff
http-response set-header X-Frame-Options DENY
http-response set-header X-XSS-Protection "1; mode=block"
http-response set-header Referrer-Policy "strict-origin-when-cross-origin"
http-response set-header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'"
# Remove server information
http-response del-header Server
http-response set-header Server "HAProxy"
# Redirect HTTP to HTTPS
redirect scheme https code 301 if !{ ssl_fc }
# ACME challenge for Let's Encrypt
acl letsencrypt-acl path_beg /.well-known/acme-challenge/
use_backend letsencrypt-backend if letsencrypt-acl
# Route based on Host header
acl is_app_service hdr(host) -i app.example.com
acl is_api_service hdr(host) -i api.example.com
use_backend app_containers if is_app_service
use_backend api_containers if is_api_service
default_backend web_containers
Backend for Let's Encrypt challenges
backend letsencrypt-backend
server letsencrypt 127.0.0.1:54321
Verify your setup
Check that HAProxy is running and discovering Docker containers correctly.
# Check HAProxy status
sudo systemctl status haproxy
Check discovery service status
sudo systemctl status haproxy-docker-discovery
View HAProxy stats
curl http://localhost:8404/stats
Test load balancing
curl -H "Host: example.com" http://localhost
curl -k -H "Host: example.com" https://localhost
Check container discovery logs
sudo journalctl -u haproxy-docker-discovery -f
Verify SSL certificate
openssl s_client -connect localhost:443 -servername example.com
You can also access the HAProxy statistics dashboard at http://your-server-ip:8404/stats to see real-time load balancing metrics and backend health status. For more advanced HAProxy configurations, check out our guide on HAProxy advanced routing with ACLs and maps.
Common issues
| Symptom | Cause | Fix |
|---|---|---|
| HAProxy shows no backends | Docker containers missing labels | Add haproxy.enable=true label to containers |
| 503 Service Unavailable | Health checks failing | Verify health check endpoints respond with 200 status |
| Discovery service not starting | Permission denied accessing Docker socket | Add haproxy user to docker group: sudo usermod -aG docker haproxy |
| SSL certificate errors | Certificate and key mismatch | Ensure certificate file contains both cert and private key |
| Rate limiting too aggressive | Thresholds set too low | Adjust http_req_rate limits in stick-table configuration |
| Consul DNS resolution fails | Consul not running or misconfigured | Check sudo systemctl status consul and DNS config |
Next steps
- Configure HAProxy multi-site SSL termination with SNI for multiple domains
- Monitor HAProxy and Consul with Prometheus and Grafana for observability
- Implement HAProxy WAF integration with ModSecurity 3 for enhanced security
- Configure Traefik with Consul service discovery as an alternative solution
- Setup Docker Compose monitoring stack with HAProxy for development environments
Running this in production?
Automated install script
Run this to automate the entire setup
#!/usr/bin/env bash
set -euo pipefail
# HAProxy with Docker Container Backends Installation Script
# Supports Ubuntu, Debian, AlmaLinux, Rocky Linux, CentOS, RHEL
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Default values
DOMAIN=${1:-"example.com"}
HAPROXY_VERSION="2.8"
# Usage function
usage() {
echo "Usage: $0 [domain]"
echo " domain: Domain name for HAProxy configuration (default: example.com)"
exit 1
}
# Print colored output
print_status() {
echo -e "${BLUE}[INFO]${NC} $1"
}
print_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
print_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
print_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
# Cleanup function for rollback
cleanup() {
print_error "Installation failed. Performing cleanup..."
systemctl stop haproxy 2>/dev/null || true
systemctl stop docker 2>/dev/null || true
if [[ "$PKG_MGR" == "apt" ]]; then
apt-get remove -y haproxy docker.io 2>/dev/null || true
else
$PKG_MGR remove -y haproxy docker 2>/dev/null || true
fi
}
trap cleanup ERR
# Check if running as root
if [[ $EUID -ne 0 ]]; then
print_error "This script must be run as root or with sudo"
exit 1
fi
# Validate arguments
if [[ $# -gt 1 ]]; then
usage
fi
print_status "Starting HAProxy with Docker backend installation for domain: $DOMAIN"
# Auto-detect distribution
echo "[1/8] Detecting operating system..."
if [[ -f /etc/os-release ]]; then
. /etc/os-release
case "$ID" in
ubuntu|debian)
PKG_MGR="apt"
PKG_UPDATE="apt update"
PKG_INSTALL="apt install -y"
HAPROXY_CONFIG="/etc/haproxy/haproxy.cfg"
;;
almalinux|rocky|centos|rhel|ol)
PKG_MGR="dnf"
PKG_UPDATE="dnf update -y"
PKG_INSTALL="dnf install -y"
HAPROXY_CONFIG="/etc/haproxy/haproxy.cfg"
if [[ "$VERSION_ID" =~ ^7 ]]; then
PKG_MGR="yum"
PKG_UPDATE="yum update -y"
PKG_INSTALL="yum install -y"
fi
;;
amzn)
PKG_MGR="yum"
PKG_UPDATE="yum update -y"
PKG_INSTALL="yum install -y"
HAPROXY_CONFIG="/etc/haproxy/haproxy.cfg"
;;
fedora)
PKG_MGR="dnf"
PKG_UPDATE="dnf update -y"
PKG_INSTALL="dnf install -y"
HAPROXY_CONFIG="/etc/haproxy/haproxy.cfg"
;;
*)
print_error "Unsupported distribution: $ID"
exit 1
;;
esac
print_success "Detected: $PRETTY_NAME"
else
print_error "Cannot detect operating system"
exit 1
fi
# Update package manager
echo "[2/8] Updating package manager..."
$PKG_UPDATE
# Install HAProxy and Docker
echo "[3/8] Installing HAProxy and Docker..."
if [[ "$PKG_MGR" == "apt" ]]; then
$PKG_INSTALL software-properties-common curl
add-apt-repository -y ppa:vbernat/haproxy-2.8 || $PKG_INSTALL haproxy
$PKG_UPDATE
$PKG_INSTALL haproxy docker.io python3 python3-pip
else
$PKG_INSTALL haproxy docker python3 python3-pip curl
# Enable PowerTools/CRB repo for additional packages on RHEL-based systems
if command -v dnf >/dev/null 2>&1; then
dnf config-manager --set-enabled powertools 2>/dev/null || dnf config-manager --set-enabled crb 2>/dev/null || true
fi
fi
# Install Python Docker library
pip3 install docker
print_success "HAProxy and Docker installed"
# Configure Docker daemon and permissions
echo "[4/8] Configuring Docker daemon..."
usermod -aG docker haproxy
systemctl enable docker
systemctl start docker
# Create necessary directories
mkdir -p /etc/haproxy/certs
mkdir -p /var/lib/haproxy
mkdir -p /run/haproxy
chown -R haproxy:haproxy /var/lib/haproxy /run/haproxy
chmod 755 /etc/haproxy/certs
print_success "Docker configured"
# Create HAProxy configuration
echo "[5/8] Creating HAProxy configuration..."
cat > "$HAPROXY_CONFIG" << EOF
global
daemon
user haproxy
group haproxy
log stdout local0
chroot /var/lib/haproxy
stats socket /run/haproxy/admin.sock mode 660 level admin
stats timeout 30s
# SSL configuration
ssl-default-bind-ciphers ECDHE+AESGCM:ECDHE+CHACHA20:RSA+AESGCM:RSA+AES:!aNULL:!MD5:!DSS
ssl-default-bind-options ssl-min-ver TLSv1.2 no-tls-tickets
defaults
mode http
timeout connect 5000ms
timeout client 50000ms
timeout server 50000ms
option httplog
option dontlognull
option http-server-close
option forwardfor except 127.0.0.0/8
option redispatch
retries 3
# Health check defaults
option httpchk GET /health
http-check expect status 200
frontend web_frontend
bind *:80
# Security headers
http-response set-header X-Content-Type-Options nosniff
http-response set-header X-Frame-Options DENY
http-response set-header X-XSS-Protection "1; mode=block"
# Route based on Host header
acl is_app_service hdr(host) -i app.$DOMAIN
acl is_api_service hdr(host) -i api.$DOMAIN
use_backend app_containers if is_app_service
use_backend api_containers if is_api_service
default_backend web_containers
# Dynamic backends for containers
backend web_containers
balance roundrobin
option httpchk GET /health
http-check expect status 200
backend app_containers
balance roundrobin
option httpchk GET /health
http-check expect status 200
backend api_containers
balance roundrobin
option httpchk GET /api/health
http-check expect status 200
# HAProxy statistics
listen stats
bind *:8404
stats enable
stats uri /stats
stats refresh 30s
stats admin if TRUE
EOF
chown haproxy:haproxy "$HAPROXY_CONFIG"
chmod 644 "$HAPROXY_CONFIG"
print_success "HAProxy configuration created"
# Create Docker discovery script
echo "[6/8] Creating Docker service discovery script..."
cat > /usr/local/bin/haproxy-docker-discovery.py << 'EOF'
#!/usr/bin/env python3
import docker
import time
import subprocess
import logging
import sys
from pathlib import Path
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
class HAProxyDockerDiscovery:
def __init__(self):
self.client = docker.from_env()
self.config_path = '/etc/haproxy/haproxy.cfg'
def get_containers_by_label(self, label_key="haproxy.enable"):
containers = []
try:
for container in self.client.containers.list():
labels = container.attrs.get('Config', {}).get('Labels', {}) or {}
if labels.get(label_key) == "true":
containers.append(container)
except Exception as e:
logger.error(f"Error getting containers: {e}")
return containers
def reload_haproxy(self):
try:
subprocess.run(['systemctl', 'reload', 'haproxy'], check=True)
logger.info("HAProxy reloaded successfully")
except subprocess.CalledProcessError as e:
logger.error(f"Failed to reload HAProxy: {e}")
def monitor(self):
logger.info("Starting Docker container monitoring...")
while True:
try:
containers = self.get_containers_by_label()
logger.info(f"Found {len(containers)} labeled containers")
time.sleep(30)
except KeyboardInterrupt:
logger.info("Monitoring stopped")
break
except Exception as e:
logger.error(f"Error in monitoring loop: {e}")
time.sleep(5)
if __name__ == "__main__":
discovery = HAProxyDockerDiscovery()
discovery.monitor()
EOF
chmod 755 /usr/local/bin/haproxy-docker-discovery.py
chown root:root /usr/local/bin/haproxy-docker-discovery.py
print_success "Docker discovery script created"
# Create systemd service for discovery script
echo "[7/8] Creating systemd service..."
cat > /etc/systemd/system/haproxy-docker-discovery.service << EOF
[Unit]
Description=HAProxy Docker Discovery Service
After=docker.service haproxy.service
Requires=docker.service haproxy.service
[Service]
Type=simple
User=root
ExecStart=/usr/local/bin/haproxy-docker-discovery.py
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable haproxy-docker-discovery.service
# Configure firewall
if command -v firewall-cmd >/dev/null 2>&1 && systemctl is-active --quiet firewalld; then
firewall-cmd --permanent --add-port=80/tcp
firewall-cmd --permanent --add-port=443/tcp
firewall-cmd --permanent --add-port=8404/tcp
firewall-cmd --reload
print_success "Firewall configured (firewalld)"
elif command -v ufw >/dev/null 2>&1; then
ufw --force enable
ufw allow 80/tcp
ufw allow 443/tcp
ufw allow 8404/tcp
print_success "Firewall configured (ufw)"
fi
# Start services
systemctl enable haproxy
systemctl start haproxy
systemctl start haproxy-docker-discovery.service
print_success "Services started"
# Verification
echo "[8/8] Performing verification checks..."
if systemctl is-active --quiet haproxy; then
print_success "HAProxy is running"
else
print_error "HAProxy is not running"
exit 1
fi
if systemctl is-active --quiet docker; then
print_success "Docker is running"
else
print_error "Docker is not running"
exit 1
fi
if systemctl is-active --quiet haproxy-docker-discovery; then
print_success "Docker discovery service is running"
else
print_warning "Docker discovery service is not running"
fi
# Test HAProxy configuration
if haproxy -c -f "$HAPROXY_CONFIG" >/dev/null 2>&1; then
print_success "HAProxy configuration is valid"
else
print_error "HAProxy configuration is invalid"
exit 1
fi
print_success "Installation completed successfully!"
echo ""
echo "Next steps:"
echo "1. Access HAProxy stats at: http://$(hostname -I | awk '{print $1}'):8404/stats"
echo "2. Label containers for load balancing: docker run -l haproxy.enable=true ..."
echo "3. Configure SSL certificates in /etc/haproxy/certs/"
echo "4. Update DNS records for app.$DOMAIN and api.$DOMAIN"
Review the script before running. Execute with: bash install.sh