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 que está 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 à 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 HAProxy É Importante Antes de Você Precisar Dele
Imagine um pequeno 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 todos para a mesma camada ausente: um ponto de entrada controlado entre a internet e seu aplicativo. HAProxy fornece essa camada. Os clientes se conectam ao HAProxy primeiro, e o HAProxy decide para onde cada solicitação vai a seguir.
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 ciente 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 com confiança uma primeira implantação do HAProxy. A tabela abaixo cobre os termos que importam neste guia.
| Termo | Significado em linguagem simples |
|---|---|
| 🌐 reverse proxy | Um serviço voltado para o cliente 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 em seguida. |
| ❤️ 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 em seguida. 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 imaginar a sua stack como um edifício de escritórios, HAProxy é a receção: o tráfego chega lá primeiro, é direcionado para a sala certa, e deixa de ser enviado para uma sala que está claramente indisponível.
Neste guia, isso traduz-se em três tarefas relevantes para principiantes:
- aceitar pedidos HTTP recebidos
- encaminhá-los para o backend de demonstração
- monitorizar se esse backend está saudável o suficiente para continuar a receber tráfego
Isto já é útil com um backend porque lhe dá uma borda pública controlada em frente da aplicação.
Mais tarde, o mesmo padrão escala de forma limpa. Pode substituir o backend, adicionar mais backends, introduzir HTTPS, ou deixar HAProxy distribuir tráfego por múltiplos destinos em vez de apenas um. Para manter o primeiro passo ensinável, este guia mantém-se em modo HTTP e intencionalmente ignora terminação TLS, ACLs, rate limiting, stick tables, e pares HA. Todos estes são tópicos reais de HAProxy. Apenas não são o ponto de partida certo para uma primeira implementação funcional.
O Que Você Está Construindo e O Que Você Precisa Primeiro

Antes de criar arquivos, ajuda ver a forma final da stack. O deployment 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 instalação inicial 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 do 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 bind-mounted 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 você tem estes conceitos 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 manter o foco no 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á 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ê ver nginx, apache2, caddy ou outro serviço já escutando lá, corrija isso primeiro. É uma etapa de pré-voo 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 implantaçã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 implantação limpa:
| Configuração do Compose | Por que está aqui |
|---|---|
| hashicorp/http-echo:1.0 | Fornece 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 | Fornece 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 fornece DNS de nome de serviço dentro do projeto, e é 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 do 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 ligação de contentores em vigor, o 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 registo amiga do contentor porque o Docker pode expor stdout facilmente, e mode http em defaults mantém o exemplo inteiro em modo HTTP para que o comportamento do listener e do backend permaneçam consistentes e legíveis.
✏️ NOTA: Um detalhe vale a pena destacar antes da análise da secção: balance roundrobin é definido explicitamente porque as 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 secção:
| Secção | Linhas-chave | O que faz |
|---|---|---|
| global | log stdout format raw local0 | Envia registos para stdout para que o registo 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 liga-o à definição do backend. |
| backend demo_backend | balance roundrobin, server demo1 demo:5678 check | Diz ao HAProxy qual serviço usar e para monitorizar a sua saúde. |
| frontend stats | bind :8404, stats enable, stats uri /stats | Adiciona uma página de validação local opcional para que possa ver o estado de execução mais tarde. |
Pode notar uma coisa em falta: option forwardfor. Esta omissão é intencional no caminho base. Preservar o IP do cliente original é útil mais tarde, mas este primeiro deployment é sobre provar o encaminhamento e a saúde do backend, não sobre ensinar comportamento de cabeçalho com um contentor de demonstração que não torna esse sinal especialmente valioso.
💡 Dica: Sempre valide a configuração do HAProxy antes de iniciar a pilha completa. Como esta configuração se refere ao backend pelo seu nome de serviço Compose (demo), inicie esse backend primeiro para que o HAProxy possa resolvê-lo durante a validação.
Execute a validação a partir 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, já provou que o HAProxy pode analisar o ficheiro corretamente e resolver o alvo do backend antes de qualquer listener ao vivo iniciar.
Inicie a Stack e Prove que o Proxy Funciona
Após a validação da configuração, inicie a stack em modo detached:
Como a etapa de validação já iniciou demo, este comando principalmente traz HAProxy e reconcilia a stack completa de dois serviços:
docker compose up -d
Em seguida, verifique se ambos os containers estão ativos:
docker compose ps
Essa visualização de processo é 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. A próxima requisição verifica o caminho de dados real.
Agora execute o teste de roteamento real a partir do 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 concentre-se na frase do corpo mais do que na formatação exata.
Se você quiser uma prova em nível de navegador, abra http://YOUR_SERVER_IP de outra máquina.

Para uma segunda superfície de validação, verifique a página de estatísticas local apenas do VPS:
curl http://127.0.0.1:8404/statsNa página de estatísticas, 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 última verificação como L4OK in 0ms. Também tenha em mente uma pequena nuance do Docker: depends_on de 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 configuração está errada.
A diferença entre estado de processo e sucesso real é mais fácil de manter em forma de tabela:
| Estado | O que isso lhe diz |
|---|---|
| Containers estão em execução | Docker iniciou os processos. |
| curl -i http://127.0.0.1 retorna 200 OK e a frase de demonstração | HAProxy está realmente roteando tráfego para o backend. |
| Página de estatísticas 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ê altera 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 a 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ê alterou 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: Altere 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 também se aplica 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 no 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: Altere uma coisa de cada vez. Se editar compose.yaml e haproxy.cfg às cegas, torna muito mais difícil dizer 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 funciona, 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 nesta 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 apenas local 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 de entrada limpa na frente de um serviço, sem transformar a primeira configuração em um projeto de operações completo. 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 oferece uma forma mais limpa de crescer a stack em um VPS auto-gerenciado sem perder o controle da configuração.
em todos os serviços de alojamento