19 min de leitura · Guia técnico
Otimizar Docker Compose e Nginx no mesmo servidor AlmaLinux 9 significa configurar o Nginx instalado diretamente no host como proxy reverso para containers gerenciados pelo Docker Compose, eliminando conflitos de porta e maximizando o uso de recursos. Para colocar os dois funcionando juntos de forma eficiente, siga estes passos:
- Atualize o AlmaLinux 9 e instale o Docker Engine com o plugin Compose.
- Instale o Nginx no host via DNF e configure-o como proxy reverso.
- Crie o arquivo
docker-compose.ymlmapeando portas apenas para127.0.0.1. - Configure blocos
serverno Nginx apontando para cada container viaproxy_pass. - Emita certificados TLS com Certbot e ajuste o Nginx para HTTPS.
- Crie um serviço systemd para subir os containers automaticamente no boot.
Pré-requisitos para otimizar Docker Compose e Nginx no AlmaLinux 9
- Servidor AlmaLinux 9 (mínimo 1 vCPU, 1 GB RAM) com acesso root via SSH.
- Usuário com privilégios
sudoou sessão comoroot. - Portas 80 e 443 liberadas no firewall (
firewalldounftables). - Docker Engine 25+ e plugin
docker-compose-plugin(substitui o binário legado). - Nginx 1.24+ disponível no repositório AppStream do AlmaLinux 9.
- Domínio apontando para o IP do servidor (necessário para TLS com Let's Encrypt).
- Conhecimento básico de linha de comando Linux.
Instalando Docker Engine e Docker Compose no AlmaLinux 9
O primeiro passo para rodar múltiplos serviços em containers é instalar o Docker Engine com suporte nativo ao plugin Compose. O AlmaLinux 9 não inclui o Docker nos repositórios padrão, então é necessário adicionar o repositório oficial da Docker Inc.
- Atualize todos os pacotes do sistema:
sudo dnf update -y
- Instale as dependências e adicione o repositório Docker:
sudo dnf install -y dnf-plugins-core
sudo dnf config-manager --add-repo https://download.docker.com/linux/rhel/docker-ce.repo
- Instale o Docker Engine e o plugin Compose:
sudo dnf install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
- Habilite e inicie o serviço Docker:
sudo systemctl enable --now docker
- Verifique a instalação:
docker --version
docker compose version
Docker version 25.0.5, build 5dc9bcc
Docker Compose version v2.27.0
Se você quiser executar o Docker sem sudo, adicione seu usuário ao grupo docker:
sudo usermod -aG docker $USER
newgrp docker
Para entender melhor como o Docker funciona em servidores VPS Linux, consulte o artigo Dicas de Otimização de Servidores Linux para ajustes complementares no sistema operacional.
Criando o arquivo docker-compose.yml com isolamento de rede
A arquitetura de redes no Docker Compose é o ponto central para evitar conflitos de porta e garantir isolamento entre serviços. Ao mapear portas apenas para 127.0.0.1, você impede que os containers fiquem expostos diretamente à internet — o Nginx no host é o único ponto de entrada externo.
Crie um diretório para o projeto e o arquivo de configuração:
mkdir -p /opt/meuapp
cd /opt/meuapp
nano docker-compose.yml
Exemplo de docker-compose.yml com dois serviços isolados:
version: "3.9"
networks:
frontend:
driver: bridge
backend:
driver: bridge
services:
webapp:
image: node:20-alpine
working_dir: /app
volumes:
- ./app:/app
command: node server.js
ports:
- "127.0.0.1:3000:3000"
networks:
- frontend
restart: unless-stopped
api:
image: python:3.12-slim
working_dir: /api
volumes:
- ./api:/api
command: python app.py
ports:
- "127.0.0.1:5000:5000"
networks:
- frontend
- backend
restart: unless-stopped
db:
image: mariadb:11.4
environment:
MYSQL_ROOT_PASSWORD: senhaforte123
MYSQL_DATABASE: meuapp
volumes:
- db_data:/var/lib/mysql
networks:
- backend
restart: unless-stopped
volumes:
db_data:
Atenção: o serviço db não expõe nenhuma porta para o host — ele só é acessível internamente pela rede backend. Isso impede acesso externo ao banco de dados, o que é uma prática de segurança essencial.
Suba os containers em modo detached:
docker compose up -d
[+] Running 4/4
✔ Network meuapp_frontend Created
✔ Network meuapp_backend Created
✔ Container meuapp-webapp-1 Started
✔ Container meuapp-api-1 Started
✔ Container meuapp-db-1 Started
Confirme que as portas estão escutando apenas em loopback:
ss -tlnp | grep -E '3000|5000'
LISTEN 0 128 127.0.0.1:3000 0.0.0.0:* users:(("docker-proxy",pid=12345,fd=4))
LISTEN 0 128 127.0.0.1:5000 0.0.0.0:* users:(("docker-proxy",pid=12346,fd=4))
Instalando e configurando o Nginx como proxy reverso no host
Com os containers rodando em portas locais, o próximo passo é instalar o Nginx no AlmaLinux 9 e configurá-lo para encaminhar requisições externas para cada serviço. Essa abordagem centraliza o gerenciamento de TLS, logs e cabeçalhos HTTP em um único ponto.
- Instale o Nginx via DNF:
sudo dnf install -y nginx
sudo systemctl enable --now nginx
- Crie um arquivo de configuração para o primeiro domínio:
sudo nano /etc/nginx/conf.d/webapp.conf
server {
listen 80;
server_name app.seudominio.com.br;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 90s;
proxy_connect_timeout 90s;
}
}
- Crie o arquivo para o segundo serviço (API):
sudo nano /etc/nginx/conf.d/api.conf
server {
listen 80;
server_name api.seudominio.com.br;
location / {
proxy_pass http://127.0.0.1:5000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 60s;
}
}
- Teste a sintaxe e recarregue o Nginx:
sudo nginx -t
sudo systemctl reload nginx
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
Libere as portas 80 e 443 no firewall do AlmaLinux 9:
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload
Emitindo certificados TLS com Certbot no AlmaLinux 9
A emissão de certificados Let's Encrypt via Certbot é o método mais direto para habilitar HTTPS nos domínios gerenciados pelo Nginx. O Certbot modifica automaticamente os blocos server existentes, adicionando as diretivas SSL necessárias.
- Instale o Certbot e o plugin para Nginx:
sudo dnf install -y epel-release
sudo dnf install -y certbot python3-certbot-nginx
- Emita o certificado para ambos os domínios:
sudo certbot --nginx -d app.seudominio.com.br -d api.seudominio.com.br
O Certbot solicitará um e-mail para notificações de renovação e atualizará automaticamente os arquivos de configuração do Nginx com os blocos SSL. Após a emissão, os arquivos webapp.conf e api.conf terão diretivas listen 443 ssl e os caminhos para os certificados.
- Verifique a renovação automática:
sudo certbot renew --dry-run
Congratulations, all simulated renewals succeeded:
Para mais detalhes sobre os benefícios do SSL gratuito, veja o artigo Lista Prática: 5 Vantagens de ter SSL gratuito no seu site.
Configurando inicialização automática dos containers com systemd
A diretiva restart: unless-stopped no docker-compose.yml garante que os containers reiniciem após falhas, mas não é suficiente para subir os serviços após um reboot completo do servidor — o Docker precisa estar ativo antes que o Compose tente iniciar os containers. A solução mais robusta é criar um serviço systemd dedicado.
- Crie o arquivo de serviço:
sudo nano /etc/systemd/system/meuapp.service
[Unit]
Description=Docker Compose - MeuApp
Requires=docker.service
After=docker.service network-online.target
[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=/opt/meuapp
ExecStart=/usr/bin/docker compose up -d --remove-orphans
ExecStop=/usr/bin/docker compose down
TimeoutStartSec=300
[Install]
WantedBy=multi-user.target
- Habilite e inicie o serviço:
sudo systemctl daemon-reload
sudo systemctl enable --now meuapp.service
- Verifique o status:
sudo systemctl status meuapp.service
● meuapp.service - Docker Compose - MeuApp
Loaded: loaded (/etc/systemd/system/meuapp.service; enabled)
Active: active (exited) since Mon 2025-01-20 10:00:00 UTC; 5s ago
Após um reboot, o systemd garantirá que o Docker esteja ativo antes de executar o docker compose up -d, eliminando condições de corrida na inicialização.
Ajustes de performance no Nginx para proxy reverso com Docker
Quando o Nginx atua como proxy reverso para múltiplos containers, alguns parâmetros globais impactam diretamente a latência e o throughput. Edite o arquivo principal do Nginx para aplicar otimizações de worker e buffer:
sudo nano /etc/nginx/nginx.conf
Ajuste as seguintes diretivas no bloco http:
worker_processes auto;
worker_rlimit_nofile 65535;
events {
worker_connections 4096;
use epoll;
multi_accept on;
}
http {
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
keepalive_requests 1000;
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 5;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml;
proxy_buffering on;
proxy_buffer_size 16k;
proxy_buffers 8 16k;
proxy_busy_buffers_size 32k;
client_max_body_size 50m;
client_body_timeout 30s;
client_header_timeout 30s;
send_timeout 30s;
}
Aplique as mudanças:
sudo nginx -t && sudo systemctl reload nginx
O parâmetro worker_processes auto detecta automaticamente o número de CPUs disponíveis. O proxy_buffering on evita que o Nginx bloqueie o container enquanto aguarda o cliente receber a resposta completa, o que é especialmente importante para aplicações Node.js e Python com respostas grandes.
Para ajustes adicionais de limites de sistema, como ulimit e parâmetros de kernel, consulte o artigo Configurando um Servidor Linux para Hospedagem de Sites.
Monitorando containers e logs do Nginx em tempo real
O monitoramento contínuo de containers Docker e do proxy Nginx é fundamental para identificar gargalos antes que afetem os usuários. O Docker oferece comandos nativos para inspecionar uso de recursos e logs de cada serviço.
Verifique o consumo de CPU e memória de todos os containers em tempo real:
docker stats
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM %
a1b2c3d4e5f6 meuapp-webapp-1 0.5% 45MiB / 1GiB 4.4%
b2c3d4e5f6a1 meuapp-api-1 0.3% 38MiB / 1GiB 3.7%
c3d4e5f6a1b2 meuapp-db-1 1.2% 120MiB / 1GiB 11.7%
Acompanhe os logs de um serviço específico:
docker compose logs -f webapp
Para os logs do Nginx, use:
sudo tail -f /var/log/nginx/access.log
sudo tail -f /var/log/nginx/error.log
Para definir limites de memória e CPU por container, adicione as diretivas deploy no docker-compose.yml:
services:
webapp:
image: node:20-alpine
deploy:
resources:
limits:
cpus: "0.50"
memory: 256M
reservations:
memory: 128M
Atenção: as diretivas deploy.resources funcionam com o modo Swarm por padrão. Para aplicá-las no modo Compose standalone, use a chave mem_limit e cpus diretamente no serviço (sintaxe da versão 2 do Compose) ou utilize a flag --compatibility ao subir os serviços.
Problemas comuns e como resolver
Sintoma: Nginx retorna erro 502 Bad Gateway ao acessar o domínio
Causa: O container não está rodando ou a porta mapeada no proxy_pass está incorreta. O Nginx não consegue estabelecer conexão com o upstream.
Solução: Verifique se o container está ativo com docker compose ps e confirme a porta com ss -tlnp | grep 3000. Certifique-se de que o proxy_pass aponta para http://127.0.0.1:PORTA e não para o nome do container (que não é resolvível fora da rede Docker). Reinicie o container com docker compose restart webapp se necessário.
Sintoma: Containers não sobem após reboot do servidor
Causa: O serviço systemd não está habilitado ou o Docker não estava ativo quando o Compose tentou iniciar.
Solução: Execute sudo systemctl status meuapp.service para verificar o estado. Se o serviço falhou, cheque os logs com journalctl -u meuapp.service -n 50. Confirme que o Docker está habilitado no boot com sudo systemctl is-enabled docker. Se retornar disabled, execute sudo systemctl enable docker.
Sintoma: Erro "port is already allocated" ao subir o Docker Compose
Causa: Outra instância do container ou processo no host já está usando a porta mapeada.
Solução: Identifique o processo com ss -tlnp | grep PORTA ou fuser PORTA/tcp. Se for um container antigo, remova-o com docker compose down antes de subir novamente. Se for o próprio Nginx no host tentando usar a mesma porta que o container, revise o mapeamento para garantir que o container use apenas portas internas (ex.: 127.0.0.1:8080:80).
Sintoma: SELinux bloqueia o Nginx de conectar ao container
Causa: O AlmaLinux 9 vem com SELinux em modo enforcing por padrão. O Nginx no host pode ser impedido de fazer conexões de rede para o loopback quando o SELinux está ativo.
Solução: Verifique os logs do SELinux com sudo ausearch -m avc -ts recent. Para permitir que o Nginx faça conexões de rede, execute:
sudo setsebool -P httpd_can_network_connect 1
Isso habilita permanentemente a permissão sem desativar o SELinux, mantendo a segurança do servidor.
Sintoma: Certificado SSL não renova automaticamente
Causa: O timer do systemd do Certbot não está ativo ou o Nginx não foi recarregado após a renovação.
Solução: Verifique o timer com sudo systemctl status certbot-renew.timer. Se inativo, habilite com sudo systemctl enable --now certbot-renew.timer. Adicione um hook de pós-renovação para recarregar o Nginx automaticamente:
echo 'post_hook = systemctl reload nginx' | sudo tee /etc/letsencrypt/renewal-hooks/post/reload-nginx.sh
sudo chmod +x /etc/letsencrypt/renewal-hooks/post/reload-nginx.sh
Perguntas frequentes sobre Docker Compose e Nginx no mesmo servidor
É possível rodar Nginx e Docker Compose no mesmo servidor sem conflito de porta?
Sim. O Nginx instalado no host atua como proxy reverso na porta 80/443, enquanto os containers Docker expõem portas internas (ex.: 8080, 3000) apenas na rede Docker. Dessa forma, o Nginx recebe as requisições externas e as encaminha para o container correto, evitando conflito de porta. A chave é mapear as portas dos containers apenas para 127.0.0.1, impedindo acesso direto externo.
Como o Docker Compose se comunica com o Nginx no host?
O container expõe uma porta mapeada para o host (ex.: 127.0.0.1:8080:80 no docker-compose.yml), e o Nginx usa essa porta como upstream no bloco proxy_pass. Usar 127.0.0.1 garante que o container não fique acessível diretamente pela internet, apenas via Nginx. Essa configuração é mais segura do que mapear para 0.0.0.0, que exporia o container em todas as interfaces de rede.
Qual a diferença entre usar Nginx no host e Nginx dentro de um container como proxy reverso?
O Nginx no host tem acesso direto ao sistema de arquivos para certificados TLS e logs, e gerencia múltiplos domínios sem depender de redes Docker. O Nginx em container (ex.: via Traefik ou nginx:alpine) é mais portátil, mas exige configuração de volumes e redes Docker para funcionar corretamente. Para servidores com múltiplos projetos, o Nginx no host costuma ser mais simples de manter, especialmente quando os certificados são gerenciados pelo Certbot.
Como garantir que os containers Docker subam automaticamente após reinicialização do servidor?
Adicione a diretiva restart: unless-stopped ou restart: always em cada serviço no docker-compose.yml. Além disso, crie um serviço systemd que execute docker compose up -d na inicialização, garantindo que o Docker Compose suba os serviços mesmo após reboot do AlmaLinux 9. A combinação das duas abordagens cobre tanto falhas de container quanto reinicializações completas do sistema operacional.
Como isolar serviços Docker para que não se comuniquem entre si desnecessariamente?
Defina redes nomeadas no docker-compose.yml e atribua cada serviço apenas às redes que ele precisa acessar. Por padrão, o Docker Compose cria uma rede bridge compartilhada entre todos os serviços do arquivo; criar redes separadas impede que um container acesse outro sem permissão explícita. No exemplo deste artigo, o serviço db está apenas na rede backend, inacessível diretamente pelo webapp que está na rede frontend.
Conclusão
- Configure o mapeamento de portas para
127.0.0.1: isso garante que nenhum container fique exposto diretamente à internet, centralizando todo o tráfego externo no Nginx com TLS. - Crie um serviço systemd para o Docker Compose: a combinação de
restart: unless-stoppedno Compose com um serviço systemd dedicado elimina qualquer risco de downtime após reboot do servidor. - Habilite o booleano SELinux
httpd_can_network_connect: no AlmaLinux 9, esse ajuste é obrigatório para que o Nginx no host consiga se conectar aos containers sem desativar o SELinux.
Leia também
- Otimizar o Desempenho do Nginx com Cache Avançado em VPS Linux e Servidor Dedicado
- Otimizar o Carregamento de Sites em VPS Linux e Servidor Dedicado com HTTP/2
- Passo a passo para configurar Nginx com PHP-FPM no VPS Linux: Guia Completo
Precisa de ajuda com Docker Compose e Nginx no seu servidor?
Configurar proxy reverso com Nginx e múltiplos containers Docker exige atenção a detalhes de rede, SELinux e inicialização do sistema. Um VPS com recursos dedicados e suporte técnico especializado facilita muito esse processo.
Conheça os planos de VPS da AviraHost e comece a hospedar seus containers hoje