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.

javascript
// 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ápido
Leitura de variáveis em Node.js e Python. As variáveis de ambiente sempre chegam como texto: converta os números explicitamente.

Repare 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:

text
postgresql://app_user:s3cr%[email protected]:5432/app_db?sslmode=verify-full
└─ esquema ─┘ └usuário┘ └─ senha ──┘ └──── host ────┘ └─porta┘ └─ banco─┘ └─ parâmetros ─┘
Exemplo de URI de conexão do PostgreSQL. A senha real é s3cr@to; o @ vai codificado como %40.
  • 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:

javascript
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,
});
Exemplo com node-postgres: TLS com verificação de certificado em produção e sem TLS localmente. Se o seu provedor usa uma CA pública, você pode omitir ca.

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:

LugarUsar?Para quê
Painel de variáveis ou secrets da sua plataforma de deploySimSecrets de produção, fora do repositório
Gerenciador de secrets dedicado (AWS Secrets Manager, Vault etc.)SimTimes grandes, rotação e auditoria
Arquivo .env local não versionadoSimSó desenvolvimento na sua máquina
.env.example versionado sem valoresSimDocumentar quais variáveis precisam ser definidas
Valores escritos diretamente no códigoNuncaFicam no repositório e no histórico dele
.env real enviado para o repositórioNuncaQualquer pessoa com acesso ao repo vê as credenciais
Variáveis sensíveis impressas em logsNuncaLogs 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.
bash
# .env.example (é versionado, sem valores reais)
DATABASE_URL=
DATABASE_CA_CERT=
API_KEY=
NODE_ENV=development

# .gitignore
.env
.env.*
!.env.example
Template versionado e regras do .gitignore: ignora qualquer .env, exceto o exemplo.

Se 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.

Do design à decisão

Compare opções de nuvem

Confira preços, limites, condições e fontes de cada opção.

Abrir comparador