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
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.
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:
TypeError: Cannot read properties of undefined (reading 'split')
DiscordjsError [TokenInvalid]: An invalid token was provided.
connect ECONNREFUSED 127.0.0.1:5432O ú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:
- 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.
- O seu código carrega o arquivo? O
process.envsó enxerga aquilo com que o processo foi iniciado. Nas versões recentes do Node.js, você pode iniciar comnode --env-file=.env index.js(documentação do Node.js). Umnode index.jssimples não lê arquivo nenhum, e o Python precisa de um carregador como opython-dotenv. - 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 aoprocess.env, então atribuirundefinedguarda o texto"undefined"(documentação do Node.js).
Falhe cedo para que a próxima falta fique óbvia:
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 campoenginesdiz 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.lockou umrequirements.txtfixado compip 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:
npm ciO 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
.gitignoreou ficou fora do ZIP que você enviou (umconfig.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".
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
localhostou127.0.0.1significa 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 (::ou0.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 em127.0.0.1por 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:
- Mantenha um
.env.examplecom todos os nomes de variáveis e nenhum segredo, e valide na inicialização como mostrado acima. - Fixe as versões do runtime e das dependências, e instale a partir do lockfile.
- 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.
- Use um container localmente se a aplicação for complexa, para rodar o mesmo sistema operacional e a mesma imagem base (o que é Docker).
- 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
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.
Mais em Deploy
Ver todos os 33 artigos- O que são variáveis de ambiente (e por que usar)Variáveis de ambiente são pares chave-valor que o sistema entrega ao processo. Veja como ler no Node.js e Python e por que tokens não ficam no código.
- O que é deploy (e o que acontece quando você faz um)Deploy é o processo de levar o código de uma aplicação para um servidor, prepará-lo e colocá-lo no ar. Veja as etapas, os métodos e como voltar atrás.
- Quanta RAM e CPU minha aplicação precisa?Dimensione RAM e CPU medindo o pico real da aplicação e somando uma folga de 30%. Veja como medir em Node.js, Python e Java e o que fazer quando faltar.
- Deploy automático com GitHub ActionsDeploy automático com GitHub Actions envia seu código para o servidor a cada push. Veja o workflow passo a passo, secrets, testes antes do deploy e erros comuns.