Tutorial: configurar HAProxy com Let's Encrypt e healthchecks para balanceamento de tráfego entre containers Docker em VPS (passo a passo)

Redes & Segurança · Por · Publicado em · 8 min de leitura

Guindastes portuários metálicos com cabines e braços contra o céu nublado
Metáfora visual para balanceamento de tráfego e infraestrutura em serviços Foto: NoName_13 / Pixabay

Este é o tutorial: configurar HAProxy com Let's Encrypt e healthchecks para balanceamento de tráfego entre containers Docker em VPS. Você vai montar a arquitetura, emitir certificados com ACME HTTP-01, ativar HTTPS, checar saúde dos backends e automatizar a renovação. O passo a passo usa Docker Compose e arquivos prontos para adaptar ao seu domínio.

Visão geral e pré-requisitos

Vamos criar um balanceador HAProxy em Docker, terminando TLS e distribuindo requisições entre dois containers de aplicação. A emissão de certificados usará o desafio HTTP‑01, atendido por um container dedicado ao path de validação.

Pré-requisitos práticos:

- VPS Linux com acesso root ou sudo. Abra as portas 80 e 443 no firewall. Isso expõe o serviço publicamente.

- Um domínio apontando para a VPS (registros A/AAAA). Propagação DNS pode levar tempo.

- Docker e Docker Compose instalados. Siga este guia se precisar: instalar Docker e Docker Compose.

- Firewall configurado para liberar 80/tcp e 443/tcp. Veja: firewall UFW no Ubuntu.

- Boas práticas de segurança na VPS: SSH, atualizações e backups. Confira o checklist de segurança.

Arquitetura e portas (o que vamos subir)

A solução tem quatro serviços: haproxy (porta 80/443), acme (servidor estático para o desafio HTTP‑01), certbot (emissão/renovação) e dois apps de exemplo. Todos compartilham uma rede Docker para descoberta por nome.

Fluxo de tráfego:

  • HTTP 80: atende /.well-known/acme-challenge/ (ACME)
  • HTTP 80: redireciona todo o resto para HTTPS
  • HTTPS 443: frontend principal do site
  • Backends: app1 e app2 em 8080 com healthcheck

Compose do projeto: serviços, rede e volumes

Crie um diretório de projeto, salve o arquivo abaixo como docker-compose.yml e ajuste quando necessário. Ele define HAProxy, um servidor estático para o desafio ACME, o container do certbot e dois apps de exemplo. Não esqueça de mapear seu domínio depois no haproxy.cfg.

version: "3.8"

services:
  haproxy:
    image: haproxy:2.9-alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro
      - letsencrypt:/etc/letsencrypt:ro
    depends_on:
      - app1
      - app2
      - acme
    networks:
      - edge
    restart: unless-stopped
    command: ["haproxy","-W","-db","-f","/usr/local/etc/haproxy/haproxy.cfg"]

  # Servidor estático que entrega o desafio HTTP-01 do Let's Encrypt
  acme:
    image: nginx:alpine
    networks:
      - edge
    volumes:
      - acme-challenge:/usr/share/nginx/html:ro
    restart: unless-stopped

  # Container utilitário para emitir/renovar certificados
  certbot:
    image: certbot/certbot:latest
    networks:
      - edge
    volumes:
      - letsencrypt:/etc/letsencrypt
      - acme-challenge:/var/www/certbot
    entrypoint: ["sh","-c"]
    command: "echo 'Use: docker compose run --rm certbot ...' && sleep infinity"
    restart: unless-stopped

  # Apps de exemplo (substitua pelos seus serviços)
  app1:
    image: hashicorp/http-echo:1.0
    command: ["-text=app1","-listen=:8080"]
    networks:
      - edge
    restart: unless-stopped

  app2:
    image: hashicorp/http-echo:1.0
    command: ["-text=app2","-listen=:8080"]
    networks:
      - edge
    restart: unless-stopped

networks:
  edge:

volumes:
  letsencrypt:
  acme-challenge:

Configurar HAProxy: HTTPS, redirecionamento e healthchecks

Crie haproxy.cfg ao lado do docker-compose.yml. Substitua EXEMPLO.COM pelo seu domínio. Usamos um backend para o desafio ACME, redirecionamento HTTP→HTTPS e checagem de saúde (/healthz) nos backends. O arquivo haproxy.pem será criado após emitir o certificado.

Dica para a primeira emissão: se ainda não existir o haproxy.pem, comente temporariamente o frontend HTTPS (porta 443), suba apenas com a porta 80 para atender o HTTP‑01 e, depois de gerar o certificado, reative o 443 e recrie o container.

global
  log stdout format raw local0
  maxconn 2000
  tune.ssl.default-dh-param 2048

defaults
  log global
  mode http
  option httplog
  option dontlognull
  timeout connect 5s
  timeout client  30s
  timeout server  30s
  retries 2

frontend fe_http
  bind *:80
  acl is_acme path_beg /.well-known/acme-challenge/
  use_backend be_acme if is_acme
  http-request redirect scheme https code 301 unless is_acme

frontend fe_https
  bind *:443 ssl crt /etc/letsencrypt/live/EXEMPLO.COM/haproxy.pem
  http-response set-header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
  default_backend be_apps

backend be_acme
  server acme acme:80 check

backend be_apps
  balance roundrobin
  option httpchk GET /healthz
  http-check expect status 200
  server app1 app1:8080 check inter 2s fall 3 rise 2
  server app2 app2:8080 check inter 2s fall 3 rise 2

Emitir certificados com Let's Encrypt (ACME HTTP-01)

Com o HAProxy (porta 80 ativa) e o container acme no ar, emita o certificado usando webroot. Depois, gere o haproxy.pem concatenando fullchain.pem e privkey.pem no volume compartilhado. Execute os comandos com um usuário que tenha permissão para controlar o Docker. Para entender mais sobre a ACME/LE, veja também como gerar SSL grátis.

# 1) Ajuste seu domínio e e-mail
export DOMAIN=seu-dominio.com.br
export [email protected]

# 2) Suba o servidor do desafio ACME e o HAProxy (porta 80 ativa)
# Se for a primeira emissão, deixe a seção HTTPS comentada no haproxy.cfg
docker compose up -d acme haproxy

# 3) Emita o certificado (HTTP-01 via webroot)
docker compose run --rm certbot certonly \
  --webroot -w /var/www/certbot \
  -d "$DOMAIN" --email "$EMAIL" --agree-tos --no-eff-email

# 4) Gere o PEM combinado esperado pelo HAProxy (no volume LE)
docker compose run --rm certbot sh -lc \
  "cat /etc/letsencrypt/live/$DOMAIN/fullchain.pem \
       /etc/letsencrypt/live/$DOMAIN/privkey.pem \
    > /etc/letsencrypt/live/$DOMAIN/haproxy.pem"

# 5) Recrie o HAProxy para carregar o certificado (reative o 443 se estava comentado)
docker compose up -d --no-deps --force-recreate haproxy

Verificar balanceamento e healthchecks

Teste o HTTPS, o balanceamento e a remoção automática de nós do pool. Você deve ver as respostas alternando entre app1 e app2. Se a porta 80/443 estiver bloqueada no firewall ou DNS não resolver para a VPS, a emissão e testes falham.

# Teste o certificado e o redirecionamento
curl -I http://$DOMAIN
curl -skI https://$DOMAIN

# Observe alternância entre backends
for i in {1..6}; do curl -sk https://$DOMAIN/; done

# Simule falha de um backend e confira alta disponibilidade
docker compose stop app1
for i in {1..4}; do curl -sk https://$DOMAIN/; done

# Volte com o backend e veja o pool normalizar
docker compose up -d app1

Renovação automática com systemd (opcional)

Automatize a renovação com systemd timers no host. O serviço executa o renew, reconstrói haproxy.pem e recria o container para aplicar o novo certificado. A renovação só ocorre quando o certificado está próximo do vencimento; rodar todo dia é seguro. Ajuste o caminho do WorkingDirectory.

# /etc/systemd/system/letsencrypt-renew.service
[Unit]
Description=Renew Let's Encrypt certs via Docker Compose
Wants=network-online.target
After=network-online.target
WorkingDirectory=/opt/edge

[Service]
Type=oneshot
ExecStart=/usr/bin/docker compose -f docker-compose.yml run --rm certbot renew --webroot -w /var/www/certbot
ExecStartPost=/usr/bin/docker compose -f docker-compose.yml run --rm certbot sh -lc 'cat /etc/letsencrypt/live/seu-dominio.com.br/fullchain.pem /etc/letsencrypt/live/seu-dominio.com.br/privkey.pem > /etc/letsencrypt/live/seu-dominio.com.br/haproxy.pem'
ExecStartPost=/usr/bin/docker compose -f docker-compose.yml up -d --no-deps --force-recreate haproxy

# /etc/systemd/system/letsencrypt-renew.timer
[Unit]
Description=Run LE renew twice daily

[Timer]
OnCalendar=*-*-* 03,15:00:00
Persistent=true

[Install]
WantedBy=timers.target

Solução de problemas comuns

Se algo não funcionar, verifique estes pontos antes de depurar configs complexas. Muitas falhas na emissão e no balanceamento são causadas por DNS, firewall e nomes de serviço incorretos.

  • DNS não aponta para a VPS ou não propagou ainda. Confirme com dig ou nslookup.
  • Firewall/Cloud bloqueando 80/443. Libere as portas públicas e teste de fora da VPS.
  • Caminho do certificado no haproxy.cfg não corresponde ao seu domínio.
  • Compose não criou a rede ou nomes de serviço divergentes (app1/app2/acme).
  • Certificado gerado, mas HAProxy não foi recriado. Reaplique o container.
  • Relógio da VPS fora de sincronia. Ative NTP para evitar falhas no ACME.

Perguntas frequentes

Posso usar vários domínios com o mesmo HAProxy?

Sim. Emita certificados para cada domínio e gere um haproxy.pem por domínio. Você pode apontar o bind para um diretório com vários .pem e usar SNI. Depois, recarregue o HAProxy ao adicionar um novo certificado.

É possível usar IPv6 com esta configuração?

Sim. Crie um registro AAAA no DNS para o domínio e garanta que as portas 80 e 443 estão abertas no IPv6 do servidor. O HAProxy pode escutar em IPv4 e IPv6 ao configurar os binds adequadamente.

Como reduzir downtime ao recarregar o HAProxy?

Inicie o HAProxy em modo master-worker (-W) e faça reload via reexecução controlada. Em Docker, recriar o serviço é simples e costuma ser rápido. Para zero‑downtime, avalie técnicas de blue‑green: veja deploy blue‑green.

Posso usar DNS-01 em vez de HTTP-01?

Pode, se seu DNS oferecer API. Troque o fluxo de webroot por um plugin DNS do certbot. Isso permite emitir sem expor a porta 80, mas exige credenciais do provedor e configuração específica.

Onde configuro regras por caminho ou host?

No frontend HTTPS, use ACLs como hdr(host) e path_beg para rotear por domínio e caminho. Crie backends dedicados e associe com use_backend conforme as ACLs. Essa abordagem escala para múltiplos serviços.

Precisa adaptar o setup ao seu cenário?

Este guia cobre o essencial para HAProxy, Let's Encrypt e healthchecks no Docker. Se você quer discutir ajustes para sua aplicação, stack e domínio, entre em contato.

Falar sobre a sua arquitetura

Veja também