Funciona localmente mas não depois do deploy: causas comuns

Seu servidor é uma máquina diferente do seu computador, e a aplicação depende de algo que só existe no computador. Os culpados de sempre são variáveis de ambiente, o arquivo .env, versões do runtime, maiúsculas em nomes de arquivo, a porta e dependências faltando.

Atualizado em 8 min de leitura

Objetivos de aprendizado

Depois de ler este artigo, você vai conseguir:

  • Ordenar as causas mais prováveis de uma falha que só aparece no deploy
  • Comparar o ambiente local e o de produção item por item
  • Fazer a aplicação falhar cedo e com uma mensagem clara, em vez de em silêncio
Nesta página

Você rodou a aplicação cem vezes na sua máquina. Fez o deploy e agora ela quebra ao iniciar, devolve erros ou sobe e não faz nada. O código não mudou entre esses dois momentos, então raramente o problema é o código.

O que mudou é tudo em volta dele: o sistema operacional, a versão do runtime, os arquivos presentes, as variáveis de ambiente, a rede e a memória disponível. O seu computador acumula anos de estado que você nem percebe mais. Um servidor começa vazio e só tem o que você deu a ele.

log do deploy: leia o primeiro erroTypeError: Cannot read properties of undefined1Variáveis de ambientedefina as variáveisno servidor2Versõesfixe as versõese use o lockfile3Arquivos e maiúsculasnome exato ecaminho relativo4Porta e hostPORT do ambiente,host 0.0.0.05Recursosmais memória oumenos pico no início
Leia o primeiro erro do log e confira o ambiente nesta ordem, da causa mais comum para a menos comum.

Por onde começar?#

Leia o log desde o início da execução, não o final. O primeiro erro é a causa, e os seguintes costumam ser consequência. Se precisar de ajuda para ler, veja como ler os logs da aplicação.

Depois passe pelas verificações abaixo, nesta ordem. Elas vão da causa mais comum para a menos comum, e cada uma leva poucos minutos.

Faltam variáveis de ambiente?#

Essa é a causa número um. Uma aplicação no estilo twelve-factor guarda configurações como credenciais e URLs em variáveis de ambiente, separadas do código (The Twelve-Factor App). Localmente você provavelmente as tira de um arquivo .env. Esse arquivo está, e deve estar, no .gitignore, então um deploy a partir do Git nunca o leva junto.

Sintomas típicos:

text
TypeError: Cannot read properties of undefined (reading 'split')
DiscordjsError [TokenInvalid]: An invalid token was provided.
connect ECONNREFUSED 127.0.0.1:5432

O último é um clássico: a URL do banco não existe, a biblioteca cai no localhost e nada escuta ali no servidor.

Confira três coisas:

  1. A produção tem todas as variáveis? Liste os nomes que o seu código lê e compare com os nomes definidos no servidor. Os nomes são exatos e diferenciam maiúsculas de minúsculas.
  2. O seu código carrega o arquivo? O process.env só enxerga aquilo com que o processo foi iniciado. Nas versões recentes do Node.js, você pode iniciar com node --env-file=.env index.js (documentação do Node.js). Um node index.js simples não lê arquivo nenhum, e o Python precisa de um carregador como o python-dotenv.
  3. Os valores são strings? Variáveis de ambiente são sempre strings. DEBUG=false é a string "false", que é truthy em JavaScript. O Node.js também converte para string tudo que você atribui ao process.env, então atribuir undefined guarda o texto "undefined" (documentação do Node.js).

Falhe cedo para que a próxima falta fique óbvia:

js
const required = ["DISCORD_TOKEN", "DATABASE_URL"];
const missing = required.filter((name) => !process.env[name]);
if (missing.length > 0) {
  throw new Error(`Missing environment variables: ${missing.join(", ")}`);
}

Para a diferença entre valores lidos no build e em tempo de execução, veja variáveis de build e de runtime. Para o básico, veja o que são variáveis de ambiente.

As versões do runtime e dos pacotes batem?#

O seu computador pode rodar uma versão nova do Node.js enquanto o servidor usa uma mais antiga por padrão, ou o contrário. Uma sintaxe ou API que existe numa versão lança SyntaxError ou TypeError em outra. O mesmo vale para Python: um match ou uma função mais nova da biblioteca padrão falha num interpretador antigo.

  • Imprima a versão nos dois lugares: node --version, python --version.
  • Declare a versão de que a aplicação precisa. No package.json, o campo engines diz com quais versões do Node.js o pacote funciona, mas por padrão ele é só um aviso (documentação do npm), então não barra uma versão errada sozinho. Confira também a configuração de runtime da sua hospedagem.
  • Faça commit do lockfile (package-lock.json, poetry.lock ou um requirements.txt fixado com pip freeze, veja a documentação do pip). Sem ele, o servidor pode resolver versões de dependências mais novas do que as que você testou.

No servidor, instale a partir do lockfile em vez de confiar numa pasta copiada:

bash
npm ci

O npm ci exige um lockfile, apaga qualquer node_modules existente e falha se o package.json e o lockfile não baterem (documentação do npm). Essa falha é útil, porque mostra a diferença na instalação e não em tempo de execução.

Falta alguma dependência ou arquivo?#

Erros como Cannot find module 'express' ou No module named 'discord' significam que o pacote nunca foi instalado onde a aplicação roda. Motivos comuns: ele está em devDependencies mas é usado em tempo de execução, foi instalado globalmente no seu computador ou você envia um node_modules montado em outro sistema operacional. O passo a passo completo está em erros de Cannot find module.

Arquivos se comportam do mesmo jeito. Um arquivo pode faltar porque:

  • Está no .gitignore ou ficou fora do ZIP que você enviou (um config.json, uma pasta de dados, uma fonte).
  • É gerado por uma etapa de build que você rodou localmente mas nunca rodou no servidor. As pastas dist/ do TypeScript são o caso mais comum.
  • Você o lê com um caminho absoluto como C:\Users\eu\projeto\data.json. Use caminhos relativos ao arquivo (path.join(__dirname, "data.json")) para a aplicação não depender de onde ela está.

Uma diferença de maiúsculas ou de caminho quebra a aplicação?#

Os sistemas de arquivos do Windows e do macOS normalmente não diferenciam maiúsculas, então require("./Utils") encontra utils.js. O Linux trata os dois como nomes diferentes e lança MODULE_NOT_FOUND. Servidores são quase todos Linux, o que torna isso a falha clássica do "no meu Windows funciona".

text
Error: Cannot find module './Utils'

Corrija fazendo o import bater exatamente com o nome do arquivo. Se o Git já rastreou o arquivo com as maiúsculas erradas, renomeie com git mv, porque uma renomeação comum pode passar despercebida pelo Git num disco que não diferencia maiúsculas. Algumas armadilhas parecidas:

  • Separadores de caminho: monte caminhos com path.join, não concatenando "\\".
  • Scripts de shell salvos com quebra de linha do Windows (CRLF) podem falhar no Linux com erros de bad interpreter.
  • Módulos nativos (sharp, bcrypt, better-sqlite3) trazem binários compilados. Um binário montado no seu sistema não roda em outro. Instale no servidor para baixar a versão certa.

A porta e o host estão errados?#

A aplicação sobe, o log parece normal, mas a URL dá timeout ou devolve um erro de gateway. Duas causas:

  • Porta fixa no código. Muitas hospedagens dizem à aplicação qual porta usar. Leia do ambiente e mantenha um padrão para o trabalho local: const port = process.env.PORT ?? 3000;. Confira na documentação da sua hospedagem qual é a variável exata.
  • Escutando no endereço errado. Escutar só em localhost ou 127.0.0.1 significa que apenas processos dentro da mesma máquina ou container conseguem conectar. No Node.js, server.listen(port) sem host aceita conexões em todas as interfaces (:: ou 0.0.0.0) (documentação do Node.js); se você passa "localhost" explicitamente, troque por "0.0.0.0". Servidores Python como o servidor de desenvolvimento do Flask (documentação do Flask) e o uvicorn (documentação do uvicorn) escutam em 127.0.0.1 por padrão, então passe --host 0.0.0.0.

Confira também as URLs dentro do seu próprio código. Um front-end chamando http://localhost:4000/api funciona para você e para mais ninguém. Transforme a URL base numa variável.

Os recursos estão acabando?#

O seu computador pode ter 16 GB de RAM e uma CPU rápida. Um plano pequeno de servidor tem um teto de memória rígido. Se o processo morre sem stack trace, ou o log termina com Killed ou JavaScript heap out of memory, provavelmente você passou do limite. Picos de memória na inicialização (carregar um dataset grande, montar caches) são um motivo comum. Veja erros de falta de memória para confirmar e corrigir.

O acesso à rede também muda. Um banco que só aceita conexões do seu IP, uma chave de API restrita ao localhost ou uma lista de permissões que não inclui o endereço do servidor funcionam em casa e são recusados a partir do servidor.

Como evitar que isso se repita?#

Diminua a distância entre os dois ambientes:

  1. Mantenha um .env.example com todos os nomes de variáveis e nenhum segredo, e valide na inicialização como mostrado acima.
  2. Fixe as versões do runtime e das dependências, e instale a partir do lockfile.
  3. Teste um clone limpo. Clone o repositório numa pasta nova, instale do zero, copie só as variáveis que a produção tem e rode. Se quebrar, você achou um arquivo ou variável faltando em dois minutos.
  4. Use um container localmente se a aplicação for complexa, para rodar o mesmo sistema operacional e a mesma imagem base (o que é Docker).
  5. Registre a configuração efetiva na inicialização (nomes e versões, nunca valores secretos), para que as primeiras linhas de todo log de deploy mostrem o que a aplicação enxergou.

Como fazer isso na Square Cloud#

As verificações acima valem em qualquer hospedagem. Na Square Cloud, cada uma tem um lugar certo para conferir.

Defina as variáveis por aplicação no painel, pela API, na CLI ou no VS Code. O painel importa um arquivo .env e exporta as variáveis no formato KEY=VALUE, e uma mudança vale no próximo restart, sem rebuild. Assim, a pergunta "minha variável está definida?" se responde numa tela de configurações, sem palpite.

Acompanhe a inicialização em tempo real pelo painel, pela CLI ou pela extensão do VS Code, e leia o primeiro erro. Cada aplicação roda num container isolado e só enxerga o que você enviou e configurou, como no teste do clone limpo.

Algumas regras da plataforma batem com as causas deste artigo. Na inicialização, ela roda npm install quando não existe node_modules, com NODE_ENV=production, então as devDependencies ficam de fora. Uma aplicação web precisa escutar em 0.0.0.0 na porta da variável PORT. O arquivo squarecloud.app é opcional: o painel o gera no deploy com o runtime, a memória e o arquivo principal, ou você o escreve na raiz do projeto. Como a memória faz parte dessa configuração, um estouro de memória se resolve aumentando esse valor, dentro da RAM do seu plano, e iniciando a aplicação de novo.

Veja como funcionam as variáveis na página de variáveis de ambiente.

Deploy

Envie pelo GitHub, pela CLI ou em ZIP e publique em segundos

Saiba mais

Perguntas frequentes

Por que minha aplicação funciona no localhost mas quebra depois do deploy?

Falta no servidor algo que o seu computador oferece: um arquivo .env, um pacote instalado globalmente, a mesma versão do runtime ou um arquivo cujo nome difere em maiúsculas e minúsculas. O log de inicialização quase sempre diz qual é, então leia o primeiro erro, não o último.

Por que meu arquivo .env não funciona em produção?

O .env normalmente está no .gitignore, então nunca chega ao servidor. A produção precisa das mesmas variáveis definidas nas configurações dela, ou de um carregador explícito que leia o arquivo que você envia.

Por que funciona no Windows mas falha no Linux?

Os sistemas de arquivos do Linux diferenciam maiúsculas de minúsculas, então ./Utils.js e ./utils.js são arquivos diferentes. Separadores de caminho, quebras de linha em scripts de shell e binários nativos compilados para outro sistema causam o mesmo tipo de falha.

Como reproduzir localmente um bug de produção?

Rode a aplicação a partir de um clone limpo, com instalação nova, a versão do runtime de produção e só as variáveis que a produção tem. Rodar num container com a mesma imagem base elimina a maior parte das diferenças que sobram.

É bug no código ou problema de ambiente?

Se o mesmo commit roda numa máquina e falha em outra, a diferença é o ambiente: versões, variáveis, arquivos, rede ou recursos. Compare os ambientes antes de mexer no código.

Próximo artigoAmbientes de desenvolvimento, staging e produçãoDesenvolvimento, staging e produção são três cópias do seu app com config e dados separados. Veja como montar cada uma, promover código e não misturar nada.