Resposta curta#
Variáveis de ambiente são valores de configuração que ficam fora do seu código e que o sistema operacional entrega para a sua aplicação quando o processo inicia. Elas permitem que a mesma base de código funcione localmente, em staging e em produção sem mudar uma linha: só os valores mudam.
DATABASE_URL é a convenção mais difundida para guardar a string de conexão com o banco de dados em uma única variável. A regra de ouro: nunca escreva credenciais no código nem suba o seu arquivo .env para o repositório. Declare as variáveis no ambiente de cada plataforma e leia-as com process.env no Node ou os.environ no Python.
Neste guia você vai ver como a sua aplicação lê essas variáveis, o que significa cada parte da DATABASE_URL, como conectar com TLS verificado em produção sem quebrar o ambiente local, onde guardar os secrets e o que fazer se algum vazar.
Documentação: The Twelve-Factor App · III. Config ↗
O princípio de fundo: configuração não fica no código#
Antes de mexer na DATABASE_URL, vale entender por que as variáveis de ambiente existem. A ideia, popularizada pela metodologia Twelve-Factor App, é separar duas coisas que costumam se misturar:
- O código: o que a sua aplicação faz. É idêntico no seu notebook, em staging e em produção.
- A configuração: os valores que mudam entre ambientes, como a URL do banco de dados, as chaves de API ou o modo de execução.
Se você coloca a configuração dentro do código, precisa editar e fazer um novo deploy toda vez que um valor muda, e acaba com credenciais de produção escritas em arquivos que qualquer pessoa com acesso ao repositório pode ler. As variáveis de ambiente resolvem os dois problemas: o código pergunta qual é a URL do banco de dados e o ambiente responde com o valor correto conforme onde ele está rodando.
Um teste útil: se você pudesse publicar o seu repositório hoje sem expor nenhuma credencial, a sua configuração está bem separada.
Documentação: The Twelve-Factor App · III. Config ↗
Como a sua aplicação lê uma variável de ambiente#
O sistema operacional expõe as variáveis ao processo da sua aplicação, e cada linguagem tem a sua forma de lê-las: no Node.js pelo objeto process.env e no Python pelo mapeamento os.environ.
// Node.js
const dbUrl = process.env.DATABASE_URL;
const port = Number(process.env.PORT) || 3000;
# Python
import os
db_url = os.environ.get("DATABASE_URL") # None se não existir
port = int(os.environ.get("PORT", "3000"))
secret = os.environ["API_KEY"] # KeyError se não existir: falha rápidoRepare no padrão com valor padrão para PORT: lê o valor do ambiente e, se ele não existir, usa um fixo. Isso importa porque muitas plataformas atribuem a porta dinamicamente pela variável PORT, e a sua aplicação precisa escutar nessa porta, não em uma fixa.
Para valores obrigatórios como DATABASE_URL ou uma chave de API, um valor padrão é má ideia: é melhor a aplicação falhar ao iniciar com uma mensagem clara do que subir pela metade e falhar na primeira consulta. No Python, os.environ com colchetes lança KeyError se a variável não existir; no Node, faça essa checagem você mesmo na inicialização.
Para desenvolvimento local, as versões recentes do Node conseguem carregar um arquivo .env com a opção --env-file, e no Python é comum usar python-dotenv. Em produção você não precisa desse arquivo: a plataforma injeta as variáveis diretamente.
Documentação: Node.js · process.env ↗ · Python · os.environ ↗
Anatomia da DATABASE_URL#
A DATABASE_URL condensa todos os dados de conexão em uma única string no formato de URI. Desmontá-la é a melhor forma de entendê-la:
postgresql://app_user:s3cr%[email protected]:5432/app_db?sslmode=verify-full
└─ esquema ─┘ └usuário┘ └─ senha ──┘ └──── host ────┘ └─porta┘ └─ banco─┘ └─ parâmetros ─┘- Esquema: o motor do banco de dados, por exemplo postgresql://, mysql:// ou mongodb://.
- Usuário e senha: as credenciais, separadas por dois-pontos e terminadas em @.
- Host: o domínio ou IP do servidor de banco de dados.
- Porta: onde o motor escuta; 5432 é a padrão do PostgreSQL e 3306 a do MySQL.
- Nome do banco de dados: o que vem depois da barra.
- Parâmetros: opções extras depois do sinal ?, como sslmode para controlar a criptografia.
Um erro frequente: se a senha tiver caracteres com significado especial em uma URI, como @, :, / ou #, você precisa codificá-los com percent-encoding; senão, o driver interpreta errado onde termina cada parte. Muitos painéis de provedores já entregam a URL codificada, mas se você montar na mão, confira.
A vantagem de ter tudo em uma variável é que mover a aplicação entre ambientes ou provedores exige mudar apenas um valor.
Documentação: PostgreSQL · URIs de conexão (libpq) ↗
O mesmo código local e em produção, com TLS verificado#
O desafio prático é que as condições mudam entre o ambiente local e a produção, principalmente a criptografia. Em desenvolvimento, você costuma se conectar a um banco local sem TLS; em produção, muitos provedores gerenciados exigem conexões criptografadas. A forma limpa de lidar com isso é condicionar a configuração de TLS ao ambiente, usando a mesma DATABASE_URL:
const { Pool } = require("pg");
const connectionString = process.env.DATABASE_URL;
if (!connectionString) {
throw new Error("DATABASE_URL não está definida");
}
const isProduction = process.env.NODE_ENV === "production";
const pool = new Pool({
// Não coloque sslmode nesta URL: o node-postgres substituiria o objeto ssl
connectionString,
ssl: isProduction
? {
rejectUnauthorized: true, // verifica o certificado do servidor
ca: process.env.DATABASE_CA_CERT, // PEM do provedor, se ele usar uma CA própria
}
: false,
});Uma correção importante em relação à versão anterior deste guia: muitos tutoriais usam rejectUnauthorized: false para fazer a conexão funcionar. Isso criptografa o tráfego, mas aceita qualquer certificado, então não protege contra alguém se passando pelo seu servidor. O correto é verificar o certificado e, se o seu provedor usa a própria autoridade certificadora, passar o certificado raiz dele em ca.
Outro detalhe do node-postgres: se a URL incluir sslmode, sslrootcert ou opções semelhantes, o objeto ssl que você passa na configuração é substituído pelo que a URL disser. Escolha um único lugar para configurar o TLS.
Na libpq, a biblioteca cliente oficial do PostgreSQL, sslmode=require criptografa a conexão, mas, a menos que exista um certificado raiz configurado, não verifica a identidade do servidor; verify-full verifica a cadeia de certificados e se o nome do host confere. Para produção, verify-full é a opção segura.
E o erro inverso: deixar o TLS ativo localmente quando o seu banco de desenvolvimento não tem suporte faz a conexão falhar com um erro de SSL. Por isso ele é condicionado com isProduction.
Documentação: node-postgres · SSL ↗ · PostgreSQL · Suporte a SSL (sslmode) ↗
Onde guardar os secrets (e onde nunca guardar)#
Nem todo lugar para guardar uma variável é igual. Esta é a hierarquia:
| Lugar | Usar? | Para quê |
|---|---|---|
| Painel de variáveis ou secrets da sua plataforma de deploy | Sim | Secrets de produção, fora do repositório |
| Gerenciador de secrets dedicado (AWS Secrets Manager, Vault etc.) | Sim | Times grandes, rotação e auditoria |
| Arquivo .env local não versionado | Sim | Só desenvolvimento na sua máquina |
| .env.example versionado sem valores | Sim | Documentar quais variáveis precisam ser definidas |
| Valores escritos diretamente no código | Nunca | Ficam no repositório e no histórico dele |
| .env real enviado para o repositório | Nunca | Qualquer pessoa com acesso ao repo vê as credenciais |
| Variáveis sensíveis impressas em logs | Nunca | Logs são guardados, compartilhados e indexados |
Três regras que evitam a maioria dos vazamentos:
- Adicione .env ao seu .gitignore desde o primeiro commit. Uma credencial que chega ao histórico do Git é difícil de apagar por completo.
- Não imprima variáveis sensíveis em logs. Um secret em um log é um secret exposto.
- Versione um .env.example com os nomes das variáveis, mas sem valores, para que o seu time saiba o que definir sem expor nada.
# .env.example (é versionado, sem valores reais)
DATABASE_URL=
DATABASE_CA_CERT=
API_KEY=
NODE_ENV=development
# .gitignore
.env
.env.*
!.env.exampleSe você usa Kubernetes, lembre que os objetos Secret são armazenados sem criptografia no etcd por padrão; a documentação oficial recomenda ativar a criptografia em repouso e restringir o acesso com RBAC.
Para se aprofundar: Conectar GitHub à AWS sem chaves de acesso (OIDC)
Documentação: Git · gitignore ↗ · AWS · O que é o AWS Secrets Manager ↗ · Kubernetes · Secrets ↗ · OWASP · Secrets Management Cheat Sheet ↗
Se um secret vazou#
Se o seu arquivo .env aparece no git log ou um secret chegou a um log compartilhado, considere essas credenciais comprometidas. Apagar o arquivo em um novo commit não basta: o valor continua no histórico e em qualquer clone ou fork.
- Rotacione primeiro: gere novas credenciais no banco de dados ou no provedor e atualize o valor no painel da sua plataforma.
- Revogue as credenciais antigas assim que o deploy estiver usando as novas.
- Depois, se necessário, limpe o histórico. O GitHub documenta o processo com git-filter-repo; o BFG Repo-Cleaner é outra ferramenta comum.
- Ative a varredura de secrets da sua plataforma de código, como o secret scanning do GitHub, para detectar vazamentos futuros.
A ordem importa: reescrever o histórico sem rotacionar deixa a credencial válida nas mãos de quem já a copiou.
Documentação: GitHub · Removendo dados sensíveis de um repositório ↗ · GitHub · Sobre o secret scanning ↗
Como verificar se a sua aplicação lê as variáveis certas#
Quando algo não conecta, antes de mexer no código confirme o que a sua aplicação está recebendo de fato:
- Imprima temporariamente se a variável existe, não o valor dela, por exemplo com Boolean(process.env.DATABASE_URL).
- Verifique se o nome confere exatamente: no Linux, DATABASE_URL e Database_Url são variáveis diferentes.
- Confira se a variável está declarada no ambiente certo: build-time ou runtime, e no serviço certo se você tiver vários.
- Confirme que não há espaços nem aspas acidentais em volta do valor ao colá-lo no painel.
- Se a senha tiver caracteres especiais, confira se eles estão codificados na URL.
Documentação: Node.js · process.env ↗
Variáveis de build-time e de runtime#
As variáveis de build-time ficam disponíveis enquanto a aplicação é compilada; as de runtime, enquanto ela está rodando. Algumas plataformas injetam só umas ou outras, então confirme em qual fase a sua aplicação precisa de cada variável.
Em frameworks de frontend essa diferença é crítica. No Next.js, as variáveis com o prefixo NEXT_PUBLIC_ são embutidas no JavaScript do navegador durante o build; no Vite, só as que têm o prefixo VITE_ são expostas ao cliente. Qualquer valor exposto assim é público: nunca coloque a DATABASE_URL nem outro secret em uma variável com esses prefixos.
Para se aprofundar: Por que seu build funciona local, mas falha no deploy
Documentação: Next.js · Variáveis de ambiente ↗ · Vite · Variáveis de ambiente e modos ↗
Perguntas frequentes#
Por que não devo escrever a senha do banco de dados no código? Porque ela fica exposta a qualquer pessoa com acesso ao repositório e ao histórico dele, e obriga você a fazer um novo deploy para trocá-la. Com variáveis de ambiente, você pode rotacioná-la sem mexer no código.
O que significa sslmode=require no final da DATABASE_URL? Obriga a conexão a ser criptografada. No PostgreSQL, sozinho ele não verifica a identidade do servidor; para isso, use verify-full com o certificado raiz do provedor.
Por que a minha conexão falha em produção, mas funciona localmente? A causa mais comum é o TLS: o seu banco local não exige e o provedor de produção exige, ou o certificado não pode ser verificado. Condicione a configuração de TLS ao ambiente e confira o certificado raiz.
Devo subir o meu arquivo .env para o repositório? Não. Adicione-o ao .gitignore e versione no lugar dele um .env.example com os nomes das variáveis, sem valores.
Resumo: DATABASE_URL sem expor credenciais#
Gerenciar a DATABASE_URL em produção se resume a três ações: manter a string de conexão fora do código, declará-la no ambiente da sua plataforma e validar na inicialização que a aplicação a recebe e consegue conectar com TLS verificado.
- Valide que a variável existe na inicialização. Uma falha cedo, com uma mensagem clara, economiza depuração.
- Separe ambientes por valor, não por código. Uma DATABASE_URL para o ambiente local, outra para staging e outra para produção; o código só lê a variável.
- Rotacione as credenciais depois de qualquer vazamento. Apagar o arquivo não basta.
A maioria das plataformas modernas de deploy, incluindo as principais nuvens, injeta variáveis de ambiente de forma nativa, então você não precisa de arquivos .env em produção. O seu código continua igual localmente, em staging e em produção; só muda o valor que o ambiente entrega.
Documentação: The Twelve-Factor App · III. Config ↗
Fontes e escopo
Documentação consultada em 25 de setembro de 2026. Os exemplos e critérios de decisão são propostas editoriais; adapte-os ao contrato da sua aplicação e valide-os no seu ambiente de testes autorizado.
- The Twelve-Factor App · III. Config ↗
- Node.js · process.env ↗
- Python · os.environ ↗
- PostgreSQL · URIs de conexão (libpq) ↗
- node-postgres · SSL ↗
- PostgreSQL · Suporte a SSL (sslmode) ↗
- Git · gitignore ↗
- AWS · O que é o AWS Secrets Manager ↗
- Kubernetes · Secrets ↗
- OWASP · Secrets Management Cheat Sheet ↗
- GitHub · Removendo dados sensíveis de um repositório ↗
- GitHub · Sobre o secret scanning ↗
- Next.js · Variáveis de ambiente ↗
- Vite · Variáveis de ambiente e modos ↗
Compare opções de nuvem
Confira preços, limites, condições e fontes de cada opção.
Abrir comparador