Corrigindo "Address already in use" - Conflitos de Porta
O que você vai aprender
- Por que
Address already in use(EADDRINUSE) aparece - Como identificar o processo que está ocupando a porta e liberá-la com segurança
- Como isolar o caso TIME_WAIT, quando o erro retorna "mesmo depois de parar o serviço"
Resumo rápido
- Descubra quem está usando:
sudo ss -lntp | grep ':PORT '(ousudo lsof -i :PORT) - Pare corretamente:
systemctl stoppara serviços,kill PIDpara processos avulsos - Erro sem processo: é TIME_WAIT -- configure
SO_REUSEADDRna aplicação ou aguarde ~60s
Pré-requisitos
- SO: Ubuntu (ambiente systemd)
- Público: qualquer pessoa que encontrou
Address already in useao iniciar um servidor/aplicação - Os exemplos usam a porta
8080(substitua pela sua)
O que é "Address already in use"?
Conclusão: A porta que sua aplicação tenta reservar com
bind()já está ocupada por outro processo, e o SO recusa a reserva.
Um processo servidor declara "escutar nesta porta" na inicialização via chamada de sistema bind(). O mesmo par IP/porta não pode ser reservado duas vezes, então se já estiver ocupado, bind() retorna EADDRINUSE.
Mensagens típicas:
Error: listen EADDRINUSE: address already in use :::8080 bind: Address already in use OSError: [Errno 98] Address already in use
Errno 98 é o número Linux para EADDRINUSE. A causa é idêntica em qualquer linguagem ou framework.
Por que ocorre o conflito de porta?
Conclusão: Quase todos os casos se encaixam em três situações: um processo antigo ainda vivo, uma inicialização duplicada ou TIME_WAIT de um processo recem-encerrado.
- Processo antigo ainda rodando: você pensou que reiniciou, mas o antigo sobreviveu. Fácil de perder com inicializações em segundo plano (
&/ nohup / containers) - Inicialização duplicada: a mesma aplicação foi iniciada duas vezes, ou outra aplicação usa a mesma porta
- TIME_WAIT: conexões do processo recem-encerrado permanecem em TCP
TIME_WAIT, e uma aplicação semSO_REUSEADDRfalha ao rebind
Os casos 1 e 2 são resolvidos parando o processo. Apenas o caso 3 não tem processo para parar e precisa de uma abordagem diferente (abaixo).
Como descubro qual processo usa a porta?
Conclusão:
ss -lntpé a forma mais rápida de obter o listener e seu PID.lsof/fusermostram os mesmos dados de formas diferentes.
Encontrar o listener com ss (recomendado)
sudo ss -lntp | grep ':8080 '
LISTEN 0 511 0.0.0.0:8080 0.0.0.0:* users:(("node",pid=12345,fd=18))
users:(("node",pid=12345,...))-- pid=12345 é o dono-llistening /-nnumérico /-tTCP /-pprocesso (requersudo)
Confirmar com lsof
sudo lsof -i :8080
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME node 12345 hide 18u IPv4 98765 0t0 TCP *:8080 (LISTEN)
Obter apenas o PID com fuser
sudo fuser 8080/tcp
8080/tcp: 12345
ss também mostra se o bind é 127.0.0.1:8080 ou 0.0.0.0:8080. O mesmo número de porta em 127.0.0.1 versus 0.0.0.0 é tratado separadamente, o que ajuda a avaliar se existe um conflito real.
Como paro o processo e libero a porta?
Conclusão: Use
systemctl stoppara serviços gerenciados ekill PIDpara processos manuais.kill -9é último recurso.
Quando roda como serviço
sudo systemctl stop myapp.service
Se você usar kill diretamente em um processo gerenciado pelo systemd, uma configuração de auto-restart (Restart=) pode revivê-lo e ocupar a porta novamente. Sempre use systemctl stop.
Quando é um processo manual
Direcione o PID encontrado, tentando o sinal normal (SIGTERM) primeiro:
sudo kill 12345
Somente se ele sobreviver após alguns segundos, force o encerramento (SIGKILL):
sudo kill -9 12345
kill -9 não dá ao processo nenhuma chance de limpeza (fechar conexões, remover arquivos temporários) e o encerra instantaneamente. Pode causar corrupção de dados ou arquivos de lock remanescentes, então tente kill (SIGTERM) primeiro e use -9 apenas como último recurso.
Em seguida, confirme que a porta está livre:
sudo ss -lntp | grep ':8080 '
Nenhuma saída significa que foi liberada.
Erro sem processo (TIME_WAIT)?
Conclusão: Conexões do processo recem-parado permanecem em
TIME_WAIT. ComSO_REUSEADDRa aplicação pode reiniciar instantaneamente; sem ele, o estado se limpa sozinho em ~60s.
Se ss -lntp não encontra nenhum listener mas Address already in use aparece, suspeite de TIME_WAIT. Verifique:
ss -tan state time-wait | grep ':8080'
TIME_WAIT é um estado TCP normal que evita que pacotes perdidos sejam interpretados incorretamente, e geralmente se limpa em cerca de 60 segundos. Opções:
- Habilitar
SO_REUSEADDRna aplicação (recomendado, correção permanente): a maioria das implementações de servidor define isso por padrão; um servidor customizado deve configurá-lo no socket de escuta - Aguardar ~60s e reiniciar: paliativo
Não altere casualmente net.ipv4.tcp_tw_reuse ou tcp_tw_recycle. Esses parâmetros dizem respeito ao reuso de TIME_WAIT no lado de saída, não são a correção para Address already in use em uma porta de escuta. tcp_tw_recycle quebra conexões atrás de NAT e já foi removido do kernel. Para o lado de escuta, SO_REUSEADDR é a resposta.
Como prevenir recorrência?
Conclusão: Implemente graceful shutdown e
SO_REUSEADDRna aplicação, e faça o script de inicialização parar o processo antigo de forma confiável.
- Configurar
SO_REUSEADDR: evita falhas de reinicialização causadas por TIME_WAIT - Graceful shutdown: ao receber SIGTERM, feche conexões antes de sair; reduz a dependência de
kill -9 - Delegar gerenciamento de processo ao systemd / containers: inicializações manuais com
&facilmente deixam processos antigos ativos - Verificação pré-inicialização: no início do script de startup, execute
ss -lntp | grep ':PORT 'e pare qualquer ocupante antes de iniciar
O que evitar
Conclusão: "Ir direto para
kill -9", "contornar mudando a porta" e "habilitartcp_tw_recycle" geram recorrência e novas falhas.
Não faça: Enviar kill -9 sem encontrar a causa
Se o ocupante é um serviço gerenciado pelo systemd, kill apenas dispara um restart. Identifique com ss primeiro, depois systemctl stop.
Não faça: Contornar mudando o número da porta
Trocar 8080 por 8081 toda vez que há conflito destrói a noção de "o que roda em qual porta". Identifique e libere o ocupante.
Não faça: Habilitar tcp_tw_recycle
Isso aparece em posts antigos de blog, mas quebra a conectividade atrás de NAT e já foi removido dos kernels atuais. Use SO_REUSEADDR no lado de escuta.
Copiar e colar: da identificação à liberação
# 1. Identificar o ocupante sudo ss -lntp | grep ':8080 ' sudo lsof -i :8080 # 2-a. Se e um servico sudo systemctl stop <service> # 2-b. Se e um processo manual (SIGTERM -> ultimo recurso SIGKILL) sudo kill <PID> sudo kill -9 <PID> # 3. Confirmar liberacao sudo ss -lntp | grep ':8080 ' # Erro sem processo (TIME_WAIT) ss -tan state time-wait | grep ':8080'