Objetivos de aprendizado
Depois de ler este artigo, você vai conseguir:
- Reconhecer o erro ECONNREFUSED em Node.js, Python e no psql
- Diagnosticar a causa com testes de porta e de endereço
- Corrigir servidor parado, host errado, bind local e ordem de inicialização
Você sobe a aplicação, ela tenta abrir a conexão com o banco e para na hora com ECONNREFUSED. É um dos erros mais comuns de quem começa a usar PostgreSQL, MySQL, MongoDB ou Redis, e aparece tanto na primeira configuração local quanto no primeiro deploy.
A boa notícia é que o erro é específico. Ele não fala de senha, de permissão nem de consulta: diz que, no endereço e na porta que o seu código usou, nenhum programa aceitou a conexão. Resta descobrir por quê.
Como o erro aparece?#
No Node.js, com os drivers pg, mysql2 e redis, a mensagem traz o IP e a porta tentados:
Error: connect ECONNREFUSED 127.0.0.1:5432No driver oficial do MongoDB, o mesmo problema vem embrulhado no erro de seleção de servidor:
MongoServerSelectionError: connect ECONNREFUSED 127.0.0.1:27017Se o host for localhost, o Node.js, nas versões recentes, tenta o IPv6 e o IPv4. Quando os dois falham, o erro vira um AggregateError com o código ECONNREFUSED, e cada tentativa fica dentro dele:
AggregateError [ECONNREFUSED]:
connect ECONNREFUSED ::1:5432
connect ECONNREFUSED 127.0.0.1:5432No PostgreSQL, o psql mostra o texto abaixo, e os drivers de Python baseados na libpq (como o psycopg) repetem a mesma mensagem dentro de um OperationalError:
psql: error: connection to server at "127.0.0.1", port 5432 failed: Connection refused
Is the server running on that host and accepting TCP/IP connections?A documentação do Node.js define o código assim: nenhuma conexão pôde ser feita porque a máquina de destino a recusou ativamente, o que costuma acontecer ao tentar falar com um serviço inativo no host. A do PostgreSQL reforça: "Connection refused" não quer dizer que o servidor recebeu o pedido e o rejeitou; quer dizer que não havia servidor escutando ali.
O que diferencia ECONNREFUSED de outros erros de conexão?#
Identificar o código certo poupa tempo, porque cada um aponta para uma camada diferente:
| Erro | O que aconteceu | Onde olhar |
|---|---|---|
ECONNREFUSED |
O host respondeu, mas a porta está fechada | Banco parado, porta errada, bind local |
ETIMEDOUT |
Nenhuma resposta dentro do prazo | Firewall, rede, IP inalcançável |
ENOTFOUND |
O nome do host não resolveu no DNS | Nome do host digitado errado ou inexistente |
password authentication failed / Access denied |
Conectou, mas a credencial falhou | Usuário e senha |
too many connections |
Conectou, mas o limite de conexões acabou | Pool e conexões abertas |
Se o seu erro é o último, o caminho é outro: veja too many connections.
Quais são as causas mais comuns?#
Da mais à menos frequente:
- O banco não está rodando. O serviço não foi iniciado, caiu por falta de memória ou de disco, ou o container dele parou.
- O host está errado. O código usa
localhost, mas o banco está em outra máquina ou em outro container. É a causa número um de "funciona local, quebra em produção". - A porta está errada. As portas padrão são 5432 (PostgreSQL), 3306 (MySQL), 27017 (MongoDB) e 6379 (Redis). Um banco configurado em outra porta recusa a padrão.
- O banco só escuta em localhost. O processo está de pé, mas aceita conexões apenas da própria máquina.
- A aplicação subiu antes do banco. Em Docker Compose ou num servidor que acabou de reiniciar, o app tenta conectar enquanto o banco ainda inicializa.
- IPv6 contra IPv4.
localhostresolve para::1, mas o banco escuta só em127.0.0.1(ou o contrário).
Como diagnosticar a causa?#
Primeiro, confirme que a porta está aberta, sem envolver o seu código. No Linux e no macOS, o nc testa qualquer porta, e cada banco tem o seu próprio teste:
nc -vz 127.0.0.1 5432
pg_isready -h 127.0.0.1 -p 5432
redis-cli -h 127.0.0.1 -p 6379 ping
mongosh "mongodb://127.0.0.1:27017" --eval "db.runCommand({ ping: 1 })"No Windows, use o PowerShell:
Test-NetConnection 127.0.0.1 -Port 5432Se a porta está fechada, veja se o processo existe e em qual endereço ele escuta. No Linux:
sudo ss -ltnp | grep -E '5432|3306|27017|6379'A coluna de endereço local mostra o bind: 127.0.0.1:5432 aceita só conexões locais; 0.0.0.0:5432 ou *:5432 aceita de qualquer interface. Se nada aparece, o banco está parado: olhe o log dele (journalctl -u postgresql, docker logs <container>) para saber por que caiu.
Por último, imprima o que o seu código está usando de fato. Uma variável de ambiente vazia ou com valor antigo explica muitos casos:
node -e "console.log(new URL(process.env.DATABASE_URL).host)"Como corrigir cada causa?#
Banco parado. Inicie o serviço (sudo systemctl start postgresql, docker start <container>) e descubra por que ele caiu. Se faltou memória ou disco, ele cai de novo enquanto isso não mudar.
Host errado em produção ou em container. Dentro de um container, localhost é o próprio container, não a sua máquina. No Docker Compose, use o nome do serviço como host (db, por exemplo). Em produção, use o host que o provedor do banco informa, sempre por variável de ambiente, nunca fixo no código.
Bind só em localhost. Cada banco tem o seu ajuste, e todos exigem reiniciar o serviço:
- PostgreSQL:
listen_addressesvalelocalhostpor padrão. Para aceitar conexões externas, ajuste empostgresql.confe libere o acesso empg_hba.conf. - MongoDB:
mongodescuta em localhost por padrão. Ajustenet.bindIpnomongod.conf. - Redis: o
redis.confpadrão trazbind 127.0.0.1 -::1e o protected mode ligado. - MySQL: o padrão de
bind_addressé*(todas as interfaces), mas confira o arquivo de configuração: o pacotemysql-serverdo Ubuntu, por exemplo, definebind-address = 127.0.0.1em/etc/mysql/mysql.conf.d/mysqld.cnf.
Um exemplo no postgresql.conf:
listen_addresses = '10.0.0.5' # IP da interface privada, não '*' sem firewallAbrir o banco para a internet sem firewall, senha forte e TLS é um risco sério. Prefira escutar só na rede privada e liberar apenas os IPs da aplicação.
Aplicação antes do banco. No Docker Compose, um depends_on simples só espera o container iniciar, não ficar pronto. Use um healthcheck:
services:
db:
image: postgres:18
environment:
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 3s
retries: 10
app:
build: .
environment:
DATABASE_URL: postgresql://postgres:${POSTGRES_PASSWORD}@db:5432/postgres
depends_on:
db:
condition: service_healthyFora do Compose, faça o próprio código tentar de novo com espera crescente. Com pg:
import pg from "pg";
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
async function connectWithRetry(tries = 5) {
for (let i = 1; ; i++) {
const client = new pg.Client({ connectionString: process.env.DATABASE_URL });
try {
await client.connect();
return client;
} catch (err) {
if (i >= tries || err.code !== "ECONNREFUSED") throw err;
const wait = 1000 * 2 ** (i - 1); // 1 s, 2 s, 4 s, 8 s
console.error(`Banco indisponível (tentativa ${i}/${tries}), nova tentativa em ${wait} ms`);
await sleep(wait);
}
}
}
const db = await connectWithRetry();
const { rows } = await db.query("select now()");
console.log(rows[0]);
await db.end();O código só repete em ECONNREFUSED: erro de senha ou de host inexistente não melhora esperando. Um pg.Client que falhou não pode ser reaproveitado, por isso o laço cria um novo a cada tentativa.
IPv6 contra IPv4. Troque localhost por 127.0.0.1 na string de conexão, ou faça o banco escutar nos dois endereços.
Como evitar que o erro volte?#
- Guarde host, porta, usuário e senha numa única variável de conexão, definida em cada ambiente. Veja o que é um arquivo .env.
- Faça a aplicação falhar com uma mensagem clara se a variável não existir, em vez de cair para
localhost. - Configure um healthcheck do banco e reinício automático para ele e para o app.
- Monitore memória e disco do servidor do banco: a falta de um deles é a causa mais comum de queda silenciosa.
- Se você não quer cuidar de bind, firewall e reinícios, considere uma hospedagem de banco de dados separada da sua máquina.
Como fazer isso na Square Cloud#
A Square Cloud oferece hospedagem de MongoDB, PostgreSQL, MySQL e Redis a partir do plano Standard. Você cria o banco no painel, e ele é provisionado em segundos no mesmo datacenter das aplicações, com latência média de cerca de 1 ms. O plano Hobby não inclui bancos.
O banco não roda dentro do container da aplicação: ele tem host e porta próprios, mostrados no painel junto com a URL de conexão completa. Por isso o erro mais comum ao migrar é a aplicação ainda apontar para localhost ou para a porta padrão do motor. Guarde a URL do painel numa variável de ambiente da aplicação, configurada no painel, na CLI ou na API; a mudança vale no próximo restart, sem rebuild.
O TLS é obrigatório: baixe o certificado no painel e aponte o driver para a CA (no PostgreSQL e no MongoDB, o cliente também apresenta o certificado .pem; no Redis, use rediss://). Como o banco aceita conexões de fora, você pode rodar o teste de porta e o cliente do motor na sua máquina, com o mesmo host, porta e certificado, e separar problema de rede de problema no código.
Você tira snapshots manuais do banco quando quiser, e todo plano com bancos inclui também um snapshot automático diário. Veja os detalhes em hospedagem de bancos de dados.
Hospedagem de bancos
MongoDB, PostgreSQL, MySQL e Redis no mesmo datacenter do app
Perguntas frequentes
ECONNREFUSED é erro de senha?
Não. A recusa acontece antes de qualquer autenticação: nada está escutando naquela porta. Senha errada gera outra mensagem, como password authentication failed no PostgreSQL ou Access denied no MySQL.
Por que aparece ::1 no erro se eu configurei localhost?
Porque localhost pode resolver para o IPv6 ::1 e para o IPv4 127.0.0.1. O Node.js tenta os dois e, se ambos falham, junta as falhas num AggregateError. Se o banco só escuta em 127.0.0.1, use esse IP no lugar de localhost.
Qual a diferença entre ECONNREFUSED e ETIMEDOUT?
ECONNREFUSED é uma recusa imediata: a máquina existe, mas a porta está fechada. ETIMEDOUT é falta de resposta, comum quando um firewall descarta os pacotes ou o host não é alcançável.
Funciona no meu computador e falha em produção. Por quê?
Quase sempre a variável de conexão em produção ainda aponta para localhost, ou não foi definida e o código usou um valor padrão. Em produção, o banco fica em outro host, que precisa estar na string de conexão.
Mais em Bancos de dados
Ver todos os 24 artigos- Quanto custa um banco de dados na nuvem?Um banco de dados na nuvem custa de zero a centenas de dólares por mês. Veja o que define o preço: instância, disco, tráfego, snapshots e como estimar o seu.
- Como hospedar um banco de dados (e conectar sua aplicação)Para hospedar um banco de dados, escolha o motor, crie a instância, guarde a string de conexão em variável de ambiente e teste a conexão. Veja o passo a passo.
- O que é Redis e para que serveRedis é um banco de dados em memória que guarda pares chave-valor e estruturas de dados. Veja como funciona, para que serve e quando não usar.
- Como fazer backup do banco de dados (MySQL, PostgreSQL, MongoDB)Faça backup de MySQL, PostgreSQL e MongoDB com mysqldump, pg_dump e mongodump, e restaure o dump. Veja os comandos, como verificar e os erros comuns.