Configure HAProxy with Consul for dynamic service discovery and automatic backend updates

Intermediate 35 min Apr 02, 2026 364 views
Ubuntu 24.04 Debian 12 AlmaLinux 9 Rocky Linux 9

Set up HAProxy with Consul integration for automatic service discovery, health checking, and dynamic backend updates. This tutorial covers consul-template configuration for zero-downtime scaling and failover in microservices architectures.

Prerequisites

  • Root access to server
  • Basic understanding of load balancing concepts
  • Familiarity with systemd services

What this solves

Manual HAProxy backend management becomes complex in dynamic environments where services frequently scale up or down. This tutorial integrates HAProxy with Consul for automatic service discovery, allowing your load balancer to dynamically update backends based on service registration and health checks. You'll eliminate manual configuration changes and achieve zero-downtime scaling for microservices deployments.

Step-by-step configuration

Update system packages and install dependencies

Start by updating your system and installing required packages for HAProxy and Consul integration.

sudo apt update && sudo apt upgrade -y
sudo apt install -y haproxy consul unzip curl wget
sudo dnf update -y
sudo dnf install -y haproxy consul unzip curl wget

Install consul-template

Download and install consul-template, which will automatically update HAProxy configuration based on Consul service changes.

CONSUL_TEMPLATE_VERSION="0.34.0"
wget https://releases.hashicorp.com/consul-template/${CONSUL_TEMPLATE_VERSION}/consul-template_${CONSUL_TEMPLATE_VERSION}_linux_amd64.zip
unzip consul-template_${CONSUL_TEMPLATE_VERSION}_linux_amd64.zip
sudo mv consul-template /usr/local/bin/
sudo chmod +x /usr/local/bin/consul-template
consul-template --version

Configure Consul server

Set up Consul with a basic configuration for service discovery. This creates a single-node setup suitable for development or small environments.

datacenter = "dc1"
data_dir = "/opt/consul"
log_level = "INFO"
server = true
bootstrap_expect = 1
bind_addr = "0.0.0.0"
client_addr = "0.0.0.0"
retry_join = ["127.0.0.1"]
ui_config {
  enabled = true
}
connect {
  enabled = true
}
ports {
  grpc = 8502
}

Create Consul data directory and set permissions

Create the required directories and set proper ownership for the Consul service.

sudo mkdir -p /opt/consul
sudo useradd --system --home /etc/consul --shell /bin/false consul
sudo chown -R consul:consul /etc/consul /opt/consul
sudo chmod 755 /etc/consul /opt/consul

Create Consul systemd service

Configure Consul to run as a systemd service with proper security settings.

[Unit]
Description=Consul
Documentation=https://www.consul.io/
Requires=network-online.target
After=network-online.target
ConditionFileNotEmpty=/etc/consul/consul.hcl

[Service]
Type=notify
User=consul
Group=consul
ExecStart=/usr/bin/consul agent -config-dir=/etc/consul
ExecReload=/bin/kill -HUP $MAINPID
KillMode=process
Restart=on-failure
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target

Start and enable Consul

Start the Consul service and verify it's running correctly.

sudo systemctl daemon-reload
sudo systemctl enable --now consul
sudo systemctl status consul
consul members

Configure HAProxy base configuration

Create the base HAProxy configuration that will be extended by consul-template.

global
    log stdout local0
    chroot /var/lib/haproxy
    stats socket /run/haproxy/admin.sock mode 660 level admin
    stats timeout 30s
    user haproxy
    group haproxy
    daemon

defaults
    mode http
    log global
    option httplog
    option dontlognull
    option http-server-close
    option forwardfor except 127.0.0.0/8
    option redispatch
    retries 3
    timeout http-request 10s
    timeout queue 1m
    timeout connect 10s
    timeout client 1m
    timeout server 1m
    timeout http-keep-alive 10s
    timeout check 10s
    maxconn 3000

frontend stats
    bind *:8404
    stats enable
    stats uri /stats
    stats refresh 30s

frontend web_frontend
    bind *:80
    default_backend web_servers

Backend will be dynamically generated by consul-template

Create consul-template configuration

Configure consul-template to monitor Consul services and update HAProxy backend configuration automatically.

consul {
  address = "127.0.0.1:8500"
  retry {
    enabled = true
    attempts = 12
    backoff = "250ms"
    max_backoff = "1m"
  }
}

template {
  source = "/etc/consul-template/haproxy.ctmpl"
  destination = "/etc/haproxy/haproxy.cfg"
  create_dest_dirs = true
  command = "systemctl reload haproxy"
  command_timeout = "60s"
  error_on_missing_key = false
  perms = 0644
  backup = true
  left_delimiter  = "{{"
  right_delimiter = "}}"
  wait {
    min = "5s"
    max = "10s"
  }
}

Create HAProxy template for dynamic backends

Create the template file that consul-template will use to generate HAProxy configuration with dynamic backends.

global
    log stdout local0
    chroot /var/lib/haproxy
    stats socket /run/haproxy/admin.sock mode 660 level admin
    stats timeout 30s
    user haproxy
    group haproxy
    daemon

defaults
    mode http
    log global
    option httplog
    option dontlognull
    option http-server-close
    option forwardfor except 127.0.0.0/8
    option redispatch
    retries 3
    timeout http-request 10s
    timeout queue 1m
    timeout connect 10s
    timeout client 1m
    timeout server 1m
    timeout http-keep-alive 10s
    timeout check 10s
    maxconn 3000

frontend stats
    bind *:8404
    stats enable
    stats uri /stats
    stats refresh 30s

frontend web_frontend
    bind *:80
    default_backend web_servers

backend web_servers
    balance roundrobin
    option httpchk GET /health
{{range service "web"}}
    server {{.Name}}-{{.ID}} {{.Address}}:{{.Port}} check inter 5s fall 3 rise 2{{end}}

Create consul-template directories and set permissions

Create necessary directories and set proper permissions for consul-template operation.

sudo mkdir -p /etc/consul-template
sudo chown -R haproxy:haproxy /etc/consul-template
sudo chmod 755 /etc/consul-template
sudo chmod 644 /etc/consul-template/haproxy.hcl /etc/consul-template/haproxy.ctmpl

Create consul-template systemd service

Configure consul-template to run as a systemd service that monitors Consul for service changes.

[Unit]
Description=Consul Template
Documentation=https://github.com/hashicorp/consul-template
Requires=network-online.target consul.service
After=network-online.target consul.service
ConditionFileNotEmpty=/etc/consul-template/haproxy.hcl

[Service]
Type=notify
User=haproxy
Group=haproxy
ExecStart=/usr/local/bin/consul-template -config=/etc/consul-template/haproxy.hcl
ExecReload=/bin/kill -HUP $MAINPID
KillMode=process
Restart=on-failure
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target

Enable and start services

Start HAProxy and consul-template services, ensuring they start automatically on boot.

sudo systemctl daemon-reload
sudo systemctl enable --now haproxy consul-template
sudo systemctl status haproxy consul-template

Register a test service in Consul

Create a sample service registration to test the integration. This simulates a web service that HAProxy will load balance.

{
  "ID": "web-1",
  "Name": "web",
  "Tags": ["primary", "v1"],
  "Address": "203.0.113.10",
  "Port": 8080,
  "Check": {
    "HTTP": "http://203.0.113.10:8080/health",
    "Interval": "10s",
    "Timeout": "3s"
  }
}
consul services register /tmp/web-service.json

Configure firewall rules

Open necessary ports for HAProxy, Consul, and service communication.

sudo ufw allow 80/tcp comment "HAProxy HTTP"
sudo ufw allow 8404/tcp comment "HAProxy Stats"
sudo ufw allow 8500/tcp comment "Consul HTTP API"
sudo ufw allow 8301/tcp comment "Consul Serf LAN"
sudo ufw allow 8301/udp comment "Consul Serf LAN"
sudo ufw reload
sudo firewall-cmd --permanent --add-port=80/tcp --add-port=8404/tcp --add-port=8500/tcp --add-port=8301/tcp --add-port=8301/udp
sudo firewall-cmd --reload

Verify your setup

Check that all services are running and the integration is working correctly.

# Verify Consul is running and services are registered
consul catalog services
consul catalog nodes

Check HAProxy configuration was updated

sudo systemctl status haproxy consul-template grep -A 10 "backend web_servers" /etc/haproxy/haproxy.cfg

Test HAProxy stats page

curl -s http://localhost:8404/stats

Verify Consul web UI access

curl -s http://localhost:8500/ui/
Note: The HAProxy configuration file should now contain dynamically generated backend servers based on services registered in Consul. Any changes to service registration will trigger automatic updates.

Advanced configuration options

Configure service health check integration

Enhance the template to use Consul health check status for HAProxy backend availability.

backend web_servers
    balance roundrobin
    option httpchk GET /health
{{range service "web"}}
    {{if .Tags | contains "primary"}}
    server {{.Name}}-{{.ID}} {{.Address}}:{{.Port}} check inter 5s fall 3 rise 2 weight 100{{else}}
    server {{.Name}}-{{.ID}} {{.Address}}:{{.Port}} check inter 5s fall 3 rise 2 weight 50 backup{{end}}{{end}}

{{if service "api"}}
backend api_servers
    balance leastconn
    option httpchk GET /api/health
{{range service "api"}}
    server {{.Name}}-{{.ID}} {{.Address}}:{{.Port}} check inter 10s fall 2 rise 3{{end}}
{{end}}

Add service tagging for routing

Configure advanced routing based on Consul service tags for blue-green deployments.

{
  "ID": "web-2",
  "Name": "web",
  "Tags": ["secondary", "v2"],
  "Address": "203.0.113.11",
  "Port": 8080,
  "Check": {
    "HTTP": "http://203.0.113.11:8080/health",
    "Interval": "10s",
    "Timeout": "3s"
  }
}
consul services register /tmp/web-service-v2.json

Common issues

SymptomCauseFix
consul-template not updating HAProxyPermission denied on config filesudo chown haproxy:haproxy /etc/haproxy/haproxy.cfg
HAProxy reload failsInvalid configuration syntaxCheck template with haproxy -f /etc/haproxy/haproxy.cfg -c
Services not appearing in HAProxyConsul service name mismatchVerify service name in template matches registration
Health checks failingConsul can't reach service endpointsCheck network connectivity and health check URLs
consul-template service fails to startMissing Consul connectionVerify Consul is running: consul members
Never use chmod 777. It gives every user on the system full access to your files. Instead, fix ownership with chown and use minimal permissions like 644 for config files.

Next steps

Automated install script

Run this to automate the entire setup

Need help?

Don't want to manage this yourself?

We handle private cloud infrastructure for businesses that depend on uptime. From initial setup to ongoing operations.