Corrigindo "Address already in use" - Conflitos de Porta
O que voce vai aprender
- Por que
Address already in use(EADDRINUSE) aparece - Como identificar o processo que esta ocupando a porta e libera-la com seguranca
- Como isolar o caso TIME_WAIT, quando o erro retorna "mesmo depois de parar o servico"
Resumo rapido
- Descubra quem esta usando:
sudo ss -lntp | grep ':PORT '(ousudo lsof -i :PORT) - Pare corretamente:
systemctl stoppara servicos,kill PIDpara processos avulsos - Erro sem processo: e TIME_WAIT -- configure
SO_REUSEADDRna aplicacao ou aguarde ~60s
Pre-requisitos
- SO: Ubuntu (ambiente systemd)
- Publico: qualquer pessoa que encontrou
Address already in useao iniciar um servidor/aplicacao - Os exemplos usam a porta
8080(substitua pela sua)
O que e "Address already in use"?
Conclusao: A porta que sua aplicacao tenta reservar com
bind()ja esta ocupada por outro processo, e o SO recusa a reserva.
Um processo servidor declara "escutar nesta porta" na inicializacao via chamada de sistema bind(). O mesmo par IP/porta nao pode ser reservado duas vezes, entao se ja estiver ocupado, bind() retorna EADDRINUSE.
Mensagens tipicas:
Error: listen EADDRINUSE: address already in use :::8080 bind: Address already in use OSError: [Errno 98] Address already in use
Errno 98 e o numero Linux para EADDRINUSE. A causa e identica em qualquer linguagem ou framework.
Por que ocorre o conflito de porta?
Conclusao: Quase todos os casos se encaixam em tres situacoes: um processo antigo ainda vivo, uma inicializacao duplicada ou TIME_WAIT de um processo recem-encerrado.
- Processo antigo ainda rodando: voce pensou que reiniciou, mas o antigo sobreviveu. Facil de perder com inicializacoes em segundo plano (
&/ nohup / containers) - Inicializacao duplicada: a mesma aplicacao foi iniciada duas vezes, ou outra aplicacao usa a mesma porta
- TIME_WAIT: conexoes do processo recem-encerrado permanecem em TCP
TIME_WAIT, e uma aplicacao semSO_REUSEADDRfalha ao rebind
Os casos 1 e 2 sao resolvidos parando o processo. Apenas o caso 3 nao tem processo para parar e precisa de uma abordagem diferente (abaixo).
Como descubro qual processo usa a porta?
Conclusao:
ss -lntpe a forma mais rapida 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 e o dono-llistening /-nnumerico /-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 tambem mostra se o bind e 127.0.0.1:8080 ou 0.0.0.0:8080. O mesmo numero de porta em 127.0.0.1 versus 0.0.0.0 e tratado separadamente, o que ajuda a avaliar se existe um conflito real.
Como paro o processo e libero a porta?
Conclusao: Use
systemctl stoppara servicos gerenciados ekill PIDpara processos manuais.kill -9e ultimo recurso.
Quando roda como servico
sudo systemctl stop myapp.service
Se voce usar kill diretamente em um processo gerenciado pelo systemd, uma configuracao de auto-restart (Restart=) pode revive-lo e ocupar a porta novamente. Sempre use systemctl stop.
Quando e um processo manual
Direcione o PID encontrado, tentando o sinal normal (SIGTERM) primeiro:
sudo kill 12345
Somente se ele sobreviver apos alguns segundos, force o encerramento (SIGKILL):
sudo kill -9 12345
kill -9 nao da ao processo nenhuma chance de limpeza (fechar conexoes, remover arquivos temporarios) e o encerra instantaneamente. Pode causar corrupcao de dados ou arquivos de lock remanescentes, entao tente kill (SIGTERM) primeiro e use -9 apenas como ultimo recurso.
Em seguida, confirme que a porta esta livre:
sudo ss -lntp | grep ':8080 '
Nenhuma saida significa que foi liberada.
Erro sem processo (TIME_WAIT)?
Conclusao: Conexoes do processo recem-parado permanecem em
TIME_WAIT. ComSO_REUSEADDRa aplicacao pode reiniciar instantaneamente; sem ele, o estado se limpa sozinho em ~60s.
Se ss -lntp nao encontra nenhum listener mas Address already in use aparece, suspeite de TIME_WAIT. Verifique:
ss -tan state time-wait | grep ':8080'
TIME_WAIT e um estado TCP normal que evita que pacotes perdidos sejam interpretados incorretamente, e geralmente se limpa em cerca de 60 segundos. Opcoes:
- Habilitar
SO_REUSEADDRna aplicacao (recomendado, correcao permanente): a maioria das implementacoes de servidor define isso por padrao; um servidor customizado deve configura-lo no socket de escuta - Aguardar ~60s e reiniciar: paliativo
Nao altere casualmente net.ipv4.tcp_tw_reuse ou tcp_tw_recycle. Esses parametros dizem respeito ao reuso de TIME_WAIT no lado de saida, nao sao a correcao para Address already in use em uma porta de escuta. tcp_tw_recycle quebra conexoes atras de NAT e ja foi removido do kernel. Para o lado de escuta, SO_REUSEADDR e a resposta.
Como prevenir recorrencia?
Conclusao: Implemente graceful shutdown e
SO_REUSEADDRna aplicacao, e faca o script de inicializacao parar o processo antigo de forma confiavel.
- Configurar
SO_REUSEADDR: evita falhas de reinicializacao causadas por TIME_WAIT - Graceful shutdown: ao receber SIGTERM, feche conexoes antes de sair; reduz a dependencia de
kill -9 - Delegar gerenciamento de processo ao systemd / containers: inicializacoes manuais com
&facilmente deixam processos antigos ativos - Verificacao pre-inicializacao: no inicio do script de startup, execute
ss -lntp | grep ':PORT 'e pare qualquer ocupante antes de iniciar
O que evitar
Conclusao: "Ir direto para
kill -9", "contornar mudando a porta" e "habilitartcp_tw_recycle" geram recorrencia e novas falhas.
Nao faca: Enviar kill -9 sem encontrar a causa
Se o ocupante e um servico gerenciado pelo systemd, kill apenas dispara um restart. Identifique com ss primeiro, depois systemctl stop.
Nao faca: Contornar mudando o numero da porta
Trocar 8080 por 8081 toda vez que ha conflito destroi a nocao de "o que roda em qual porta". Identifique e libere o ocupante.
Nao faca: Habilitar tcp_tw_recycle
Isso aparece em posts antigos de blog, mas quebra a conectividade atras de NAT e ja foi removido dos kernels atuais. Use SO_REUSEADDR no lado de escuta.
Copiar e colar: da identificacao a liberacao
# 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'