Como Instalar HAProxy com Docker Compose em Ubuntu VPS
Um serviço web num VPS é fácil de expor diretamente — até que queira uma porta pública limpa, a liberdade de trocar o backend mais tarde, ou uma forma mais segura de parar de enviar tráfego para algo avariado. É nesse ponto que um proxy deixa de parecer “algo para grandes equipas de infraestrutura” e começa a parecer prático.

HAProxy encaixa bem nesse papel. Pense nele como o gestor de tráfego sentado em frente da sua aplicação: os pedidos chegam primeiro ao HAProxy, e o HAProxy decide para onde devem ir a seguir. Não precisa de um grande cluster para beneficiar disso. Mesmo num único VPS Ubuntu 24.04, oferece-lhe uma separação mais limpa entre a internet e o serviço que está realmente a executar.
Este guia mantém a primeira implementação intencionalmente estruturada: um VPS Ubuntu 24.04, Docker Compose, um contentor HAProxy, um backend de demonstração, e prova de que o encaminhamento realmente funciona.
Por que o HAProxy é Importante Antes de Você Precisar Dele
Imagine uma pequena VPS executando um aplicativo perfeitamente hoje. Ele responde em uma porta, o site carrega e tudo parece bom. O atrito começa quando você quer um ponto de entrada público estável, a opção de substituir o backend posteriormente sem alterar o endereço público, ou uma camada frontal que possa parar de enviar tráfego para um serviço com falha. Expor o aplicativo diretamente começa a parecer frágil surpreendentemente rápido.

Esses requisitos apontam para a mesma camada ausente: um ponto de entrada controlado entre a internet e seu aplicativo. O HAProxy fornece essa camada. Os clientes se conectam ao HAProxy primeiro, e o HAProxy decide para onde cada solicitação vai em seguida.
Essa separação é útil mesmo antes de você ter vários servidores. Ela oferece uma borda pública mais limpa agora e um caminho mais seguro para mudanças posteriores, como substituição de backend, roteamento com reconhecimento de saúde e HTTPS. O resto do guia mostra esse padrão em sua forma de funcionamento mais simples e o verifica com um caminho de solicitação real.
Termos Rápidos do HAProxy Que Facilitam o Resto deste Guia

Você só precisa de um pequeno conjunto de vocabulário para seguir uma primeira implantação do HAProxy com confiança. A tabela abaixo cobre os termos que importam neste guia.
| Termo | Significado em linguagem simples |
|---|---|
| 🌐 reverse proxy | Um serviço voltado para o público que recebe solicitações primeiro e as passa para outro serviço interno. |
| ⚖️ load balancer | Uma camada frontal que pode distribuir solicitações entre mais de um alvo de backend. |
| 🚪 frontend | O lugar onde os clientes se conectam ao HAProxy. |
| 🧩 backend | O serviço ou servidor para o qual o HAProxy envia a solicitação a seguir. |
| ❤️ health check | Uma forma de o HAProxy perceber se um backend deve continuar recebendo tráfego. |
| 🐳 image | Um modelo de aplicação empacotado usado para criar contêineres. |
| 📦 container | Uma instância em execução de uma imagem. |
Para este guia, reverse proxy é o primeiro modelo mental a manter em mente. O HAProxy fica na frente de algo e controla a transferência. Load balancing é a capacidade estendida que se torna útil quando você adiciona vários servidores de backend posteriormente.
Os dois termos que mais importam quando você abre a configuração são frontend e backend. O frontend é onde o cliente chega. O backend é para onde o HAProxy envia a solicitação a seguir. Um health check importa porque permite que o HAProxy perceba quando um alvo deve parar de receber tráfego.
O Que HAProxy Faz Bem — e O Que Este Guia Intencionalmente Ignora

Se você imaginar sua stack como um prédio de escritórios, HAProxy é a recepção: o tráfego chega lá primeiro, é direcionado para a sala certa, e para de ser enviado para uma sala que está claramente indisponível.
Neste guia, isso se traduz em três trabalhos relevantes para iniciantes:
- aceitar requisições HTTP recebidas
- encaminhá-las para o backend de demonstração
- monitorar se esse backend está saudável o suficiente para continuar recebendo tráfego
Isso já é útil com um backend porque oferece uma borda pública controlada na frente da aplicação.
Mais tarde, o mesmo padrão escala de forma limpa. Você pode substituir o backend, adicionar mais backends, introduzir HTTPS, ou deixar HAProxy distribuir tráfego entre múltiplos destinos em vez de apenas um. Para manter o primeiro passo ensinável, este guia permanece em modo HTTP e intencionalmente ignora terminação TLS, ACLs, rate limiting, stick tables e pares HA. Todos esses são tópicos reais de HAProxy. Eles apenas não são o ponto de partida certo para uma primeira implantação funcional.
O Que Você Está Construindo e O Que Precisa Primeiro

Antes de criar arquivos, é útil ver a forma final da stack. A implantação neste guia se parece com isto:
Client browser or curl
|
v
HAProxy frontend (:80)
|
v
demo backend service (demo:5678)
Optional local-only validation:
HAProxy stats frontend (127.0.0.1:8404/stats)Docker Compose é o caminho principal aqui porque mantém a primeira instalação reproduzível, visível e fácil de editar. Em vez de construir uma imagem personalizada no primeiro dia, você mantém a configuração HAProxy no host, monta-a no container e inicia toda a stack a partir de um arquivo. Em um VPS Ubuntu auto-gerenciado — por exemplo, um VPS AlexHost — isso é um ajuste limpo porque o layout permanece fácil de inspecionar.
💡 Dica: Este guia usa Docker Compose mais um haproxy.cfg montado por bind propositalmente. É o caminho de primeira instalação mais transparente porque você pode editar a configuração do proxy diretamente sem adicionar uma etapa de construção de imagem.
Antes de começar, certifique-se de que tem estes itens básicos em vigor:
- Ubuntu 24.04 VPS
- Docker Engine instalado
- Docker Compose v2 disponível através de docker compose
- Acesso ao terminal e permissão para executar Docker
- Porta 80 disponível no host
- HTTP de entrada permitido se você usar UFW ou regras de firewall do lado do provedor
Primeiro, verifique sua versão do Ubuntu
lsb_release -a
Em seguida, confirme que Docker e Compose moderno estão disponíveis:
docker --version
docker compose version
Se ambos os comandos retornarem informações de versão, o lado do runtime do container está pronto e você pode se manter focado em HAProxy em vez de fazer um desvio para a instalação do Docker.
Em seguida, certifique-se de que a porta 80 não está já em uso, depois verifique se UFW está ativo e se HTTP já está permitido:
sudo ss -tlnp | grep -E ':(80)s' || true
sudo ufw status
sudo ufw allow 80/tcp
✏️ NOTA: Nenhuma saída da verificação ss geralmente significa que a porta 80 está livre. Se você vir nginx, apache2, caddy ou outro serviço já escutando lá, corrija isso primeiro. É uma etapa de verificação prévia de dez segundos que economiza muita confusão depois.
No exemplo acima, sudo ufw status mostra Status: active, e 80/tcp já está presente na lista de permissões. É por isso que sudo ufw allow 80/tcp retorna Skipping adding existing rule em vez de adicionar uma nova. Essa saída é normal e simplesmente significa que a regra de firewall já estava em vigor.
Criar a Pasta do Projeto e o Arquivo Compose
Comece criando uma pequena pasta de projeto para os dois arquivos que esta primeira implementação precisa:
mkdir -p ~/haproxy-docker
cd ~/haproxy-docker
Depois disso, o layout deve ser o menor possível:
~/haproxy-docker/
├── compose.yaml
└── haproxy.cfgAgora crie compose.yaml e use este conteúdo exato:
services:
demo:
image: hashicorp/http-echo:1.0
command: ["-listen=:5678", "-text=Hello from the HAProxy demo backend"]
restart: unless-stopped
haproxy:
image: haproxy:3.4.1
depends_on:
- demo
ports:
- "80:80"
- "127.0.0.1:8404:8404"
volumes:
- ./haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro
sysctls:
net.ipv4.ip_unprivileged_port_start: "0"
restart: unless-stoppedEste arquivo conecta os containers, mas ainda não define a lógica de requisição do HAProxy. Ele diz ao Docker quais imagens executar, quais portas publicar e de onde o arquivo de configuração do HAProxy será montado no host.
As seguintes configurações são as que mais importam para uma primeira implementação limpa:
| Configuração do Compose | Por que está aqui |
|---|---|
| hashicorp/http-echo:1.0 | Oferece um backend de demonstração minúsculo e previsível sem ensinar um segundo servidor web ao mesmo tempo. |
| haproxy:3.4.1 | Usa uma tag estável fixada em vez de latest, o que mantém o guia menos frágil ao longo do tempo. |
| depends_on | Inicia o serviço demo antes do HAProxy, o que é útil para a ordem de primeira execução. |
| 80:80 | Publica o listener HTTP principal na porta web padrão que os leitores esperam. |
| 127.0.0.1:8404:8404 | Mantém a página de estatísticas disponível para validação local sem expô-la publicamente por padrão. |
| ./haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro | Monta seu arquivo de configuração visível no lado do host na imagem oficial do HAProxy como somente leitura. |
| sysctls com net.ipv4.ip_unprivileged_port_start: “0” | Permite que o container HAProxy não-root se vincule a portas baixas como 80. |
| restart: unless-stopped | Oferece um padrão VPS prático: reiniciar após falha ou reinicialização, mas respeitar uma parada manual intencional. |
Um detalhe a mais importa aqui: não há rede Docker personalizada neste arquivo porque o Docker Compose cria uma rede padrão automaticamente. Isso oferece DNS de nome de serviço dentro do projeto, é por isso que o HAProxy será capaz de alcançar o backend como demo:5678 sem fiação extra.
⚠️ Aviso: A porta 80 é uma porta privilegiada, portanto a linha sysctls não é decorativa. Alterar o mapeamento de host para 8080:80 não remove o requisito de porta privilegiada dentro do container se o HAProxy ainda se vincular a :80 internamente.
Escrever e Validar um haproxy.cfg Mínimo
Com a fiação do container em vigor, HAProxy ainda precisa de instruções sobre onde o tráfego chega, para onde deve ir e como a saúde do backend é verificada. Crie haproxy.cfg a seguir:
global
log stdout format raw local0
defaults
mode http
timeout connect 5s
timeout client 30s
timeout server 30s
frontend http
bind :80
default_backend demo_backend
backend demo_backend
balance roundrobin
server demo1 demo:5678 check
frontend stats
bind :8404
stats enable
stats refresh 10s
stats uri /statsEsta é uma configuração mínima, mas não é uma configuração descartável. log stdout format raw local0 é a escolha de logging amigável ao container porque Docker pode expor stdout facilmente, e mode http em defaults mantém todo o exemplo em modo HTTP para que o comportamento do listener e do backend permaneçam consistentes e legíveis.
✏️ NOTA: Um detalhe vale a pena ser destacado antes da análise da seção: balance roundrobin é definido explicitamente porque versões mais recentes do HAProxy alteraram o algoritmo padrão do backend para random, e roundrobin é mais fácil de ensinar de forma previsível numa primeira passagem.
Aqui está a análise em linguagem simples de cada seção:
| Seção | Linhas-chave | O que faz |
|---|---|---|
| global | log stdout format raw local0 | Envia logs para stdout para que o logging do Docker permaneça direto. |
| defaults | mode http, timeouts | Estabelece comportamento HTTP de base e valores de timeout sensatos. |
| frontend http | bind :80, default_backend demo_backend | Cria o listener público e o conecta à definição do backend. |
| backend demo_backend | balance roundrobin, server demo1 demo:5678 check | Diz ao HAProxy qual serviço usar e para monitorar sua saúde. |
| frontend stats | bind :8404, stats enable, stats uri /stats | Adiciona uma página de validação local opcional para que você possa ver o status em tempo de execução mais tarde. |
Você pode notar uma coisa ausente: option forwardfor. Essa omissão é intencional no caminho base. Preservar o IP do cliente original é útil mais tarde, mas este primeiro deployment é sobre provar roteamento e saúde do backend, não ensinar comportamento de cabeçalho com um container de demonstração que não torna esse sinal especialmente valioso.
💡 Dica: Sempre valide a configuração do HAProxy antes de iniciar a stack completa. Como esta configuração se refere ao backend pelo seu nome de serviço Compose (demo), inicie esse backend primeiro para que HAProxy possa resolvê-lo durante a validação.
Execute a validação do mesmo diretório do projeto:
docker compose up -d demo
docker compose run --rm --no-deps haproxy haproxy -V -c -f /usr/local/etc/haproxy/haproxy.cfg
Se o segundo comando terminar com Configuration file is valid, você já provou que HAProxy pode analisar o arquivo corretamente e resolver o alvo do backend antes de qualquer listener ao vivo iniciar.
Inicie a Stack e Prove que o Proxy Funciona
Assim que a config for validada, inicie a stack em modo detached:
Como o passo de validação já iniciou demo, este comando principalmente traz HAProxy e reconcilia a stack completa de dois serviços:
docker compose up -d
Depois verifique se ambos os containers estão vivos:
docker compose ps
Essa visualização de processos é apenas o primeiro checkpoint. Confirma que Docker iniciou os containers, mas ainda não que HAProxy está roteando tráfego com sucesso para o backend. O próximo request verifica o caminho de dados real.
Agora execute o teste de roteamento real a partir da VPS:
curl -i http://127.0.0.1
O sinal de sucesso é HTTP/1.1 200 OK mais o corpo da resposta contendo Hello from the HAProxy demo backend. Algumas compilações de http-echo envolvem esse texto em uma pequena resposta HTML, então foque na frase do corpo mais do que na formatação exata.
Se quiser uma prova em nível de navegador, abra http://YOUR_SERVER_IP a partir de outra máquina.

Para uma segunda superfície de validação, verifique a página de stats somente local a partir da VPS:
curl http://127.0.0.1:8404/statsNa página de stats, os sinais mais úteis são um frontend nomeado http, um backend nomeado demo_backend, uma linha de servidor nomeada demo1, status mostrado como UP, e geralmente um valor de last-check como L4OK in 0ms. Também mantenha em mente uma pequena nuance do Docker: depends_on com sintaxe curta controla a ordem de inicialização, mas não aguarda um serviço ficar saudável. Se o primeiro curl falhar uma vez logo após a inicialização, aguarde alguns segundos e tente novamente antes de assumir que a config está errada.
A diferença entre estado de processo e sucesso real é mais fácil de manter clara em forma de tabela:
| Estado | O que isso lhe diz |
|---|---|
| Containers estão running | Docker iniciou os processos. |
| curl -i http://127.0.0.1 retorna 200 OK e a frase demo | HAProxy está realmente roteando tráfego para o backend. |
| Página de stats mostra demo1 como UP | HAProxy vê o backend como saudável. |
Erros Comuns na Primeira Execução e Correções Rápidas

Se a configuração não funcionar imediatamente, resista ao impulso de reescrever ambos os ficheiros de uma vez. A maioria das falhas na primeira execução nesta stack são previsíveis, e ficam muito mais fáceis de corrigir quando você muda uma variável de cada vez.
Use esta matriz como camada de diagnóstico rápido:
HAProxy sai imediatamente
Causa provável: haproxy.cfg está em falta.
Correção rápida: Certifique-se de que haproxy.cfg existe junto a compose.yaml.
Por que acontece: A imagem oficial não vem com uma configuração pronta para usar.
Erro diz que não consegue abrir /usr/local/etc/haproxy/haproxy.cfg
Causa provável: Caminho de bind-mount incorreto.
Correção rápida: Verifique ./haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro exatamente.
Por que acontece: HAProxy não consegue iniciar sem um ficheiro de configuração válido.
Erro diz Permission denied na porta 80
Causa provável: Problema de ligação a porta privilegiada.
Correção rápida: Mantenha net.ipv4.ip_unprivileged_port_start: “0” no Compose, ou mude tanto HAProxy como a porta publicada para 8080.
Por que acontece: O contentor é executado como utilizador não-root haproxy.
Você mudou o mapeamento para 8080:80 e ainda recebe um erro de ligação
Causa provável: HAProxy ainda se liga a :80 dentro do contentor.
Correção rápida: Mude tanto o mapeamento do anfitrião como a linha bind interna se se afastar da porta 80.
Por que acontece: A regra de porta privilegiada aplica-se também dentro do contentor.
Porta 80 já está em uso
Causa provável: Outro serviço é proprietário da porta do anfitrião.
Correção rápida: Execute novamente a verificação ss e pare ou mude o serviço em conflito.
Por que acontece: Apenas um processo pode escutar na mesma porta do anfitrião.
Verificação de sintaxe relata unknown keyword ou erros específicos de linha
Causa provável: Erro de digitação na configuração HAProxy.
Correção rápida: Execute novamente a verificação de sintaxe e corrija a linha exata que relata.
Por que acontece: O analisador de HAProxy é rigoroso, o que é útil quando o usa intencionalmente.
Contentores estão ativos, mas curl não retorna a resposta de demonstração
Causa provável: Caminho de encaminhamento está errado.
Correção rápida: Verifique novamente default_backend demo_backend, server demo1 demo:5678 check, e o nome do serviço demo.
Por que acontece: Um contentor em execução não é prova de um caminho frontend-para-backend correto.
curl local funciona mas o site é inacessível de fora
Causa provável: Firewall ou regra de segurança do fornecedor.
Correção rápida: Abra a porta 80 em UFW e em qualquer firewall do lado do fornecedor.
Por que acontece: A publicação local pode funcionar mesmo quando o acesso público ainda está bloqueado.
⚠️ Aviso: Mude uma coisa de cada vez. Se editar compose.yaml e haproxy.cfg cegamente, fica muito mais difícil determinar se a falha é um problema de caminho de ficheiro, um problema de porta, ou um problema de encaminhamento.
Quando precisa de evidência rápida, mantenha estes comandos à mão:
docker compose logs haproxy
docker compose ps
docker compose up -d demo
docker compose run --rm --no-deps haproxy haproxy -V -c -f /usr/local/etc/haproxy/haproxy.cfg
sudo ss -tlnp | grep -E ':(80|8404)s' || trueEstes são os três padrões de alerta mais dignos de reconhecer à primeira vista:
[ALERT] ... Cannot open configuration file /usr/local/etc/haproxy/haproxy.cfg : No such file or directory
[ALERT] ... Starting frontend http: cannot bind socket (Permission denied) [0.0.0.0:80]
[ALERT] ... parsing [/usr/local/etc/haproxy/haproxy.cfg:12] : unknown keyword 'chekc'; did you mean 'check' maybe?Essa é a parte tranquilizadora de uma pequena implementação inicial: as formas de falha são geralmente pequenas também. Você não precisa começar do zero. Você precisa identificar qual camada está reclamando e corrigir essa uma coisa primeiro.
Para Onde Ir Após a Instalação
Assim que a demo com um backend funcionar, a arquitetura já é útil. O próximo passo real é substituir o container de demo pela sua aplicação real, mantendo a mesma estrutura HAProxy. Depois disso, adicione HTTPS/TLS como um passo de acompanhamento dedicado, e trate roteamento baseado em domínio mais ACLs como tópicos separados em vez de apressá-los para esta primeira instalação.

Quando estiver pronto para mais de um backend, o mesmo padrão se torna visivelmente significativo:
backend app_backend
balance roundrobin
server app1 app1:8080 check
server app2 app2:8080 checkPara edições seguras de uma config bind-mounted, valide primeiro e depois recarregue HAProxy gracefully:
docker compose up -d demo
docker compose run --rm --no-deps haproxy haproxy -V -c -f /usr/local/etc/haproxy/haproxy.cfg
docker compose kill -s HUP haproxy📝 Nota: A página de stats é intencionalmente local-only neste guia. Se alguma vez a expuser publicamente, adicione autenticação e controles de acesso primeiro.
Isso o traz de volta ao problema original: você queria uma porta frontal limpa na frente de um serviço, sem transformar a primeira configuração em um projeto de operações completo. Você agora tem esse caminho funcionando. Mais importante ainda, você também tem o modelo mental correto: HAProxy recebe o tráfego primeiro, o encaminha para onde pertence, e lhe dá uma forma mais limpa de crescer a stack em uma VPS auto-gerenciada sem perder o controle da configuração.
em todos os serviços de alojamento