Variáveis de ambiente explicadas: arquivos .env, segredos e configuração bem feitos

  1. Início
  2. Blog
  3. DevOps

DevOps

Variáveis de ambiente explicadas: .arquivos env, segredos e configuração feitos corretamente

As variáveis de ambiente permitem que o mesmo código seja executado em um laptop, em CI e em produção com configurações diferentes. Veja como eles realmente se comportam, com as surpresas que tivemos ao testá-los.

8 min de leitura
Variáveis ​​de ambiente explicadas: arquivos .env, segredos e configuração bem feitos

Principais pontos

  • Variáveis de ambiente são configurações de valores-chave que um processo recebe de seu ambiente.
  • Eles mantêm configurações, como portas e URLs de banco de dados, fora do seu código.
  • Os valores são sempre strings, então “false” é verdadeiro, a menos que você o analise.
  • Nunca confirme .env arquivos com segredos reais e valide as configurações necessárias quando o aplicativo for iniciado.

Seu aplicativo precisa de um URL de banco de dados. No seu laptop é um banco de dados local, no CI é descartável e na produção é um cluster gerenciado. Codificar qualquer um deles significa alterar o código para cada ambiente. Variáveis ​​de ambiente resolvem isso. O código lê DATABASE_URL e cada ambiente fornece seu próprio valor.

Essa ideia é central para o método Twelve-Factor App, que diz que a configuração que varia entre as implantações pertence ao ambiente. Testamos o comportamento abaixo com Python 3.13 no Windows. Cada saída mostrada é dessa execução.

O que são variáveis ​​de ambiente?

Todo processo em execução possui um conjunto de valores de texto nomeados chamados de ambiente. Exemplos familiares são PATH, que informa ao shell onde encontrar programas, e HOME ou USERPROFILE. Seu próprio aplicativo pode usar o mesmo mecanismo para configurações como APP_PORT, LOG_LEVEL ou DATABASE_URL.

Configurando variáveis de ambiente

Concha Para esta sessão Para um comando
Bash/zsh exportar APP_PORT=8080 APP_PORT=8080 python app.py
PowerShell $env:APP_PORT = "8080" Configure, execute e remova-o
cmd do Windows definir APP_PORT=8080 Não embutido

Eles duram apenas para a sessão atual do terminal. Para torná-los permanentes, adicione-os a um perfil de shell como ~/.bashrc ou, no Windows, use as configurações de ambiente do sistema ou setx. Para aplicativos, geralmente é melhor fornecê-los no momento da implantação, em vez de configurá-los em todo o sistema.

Lendo-os em código

import os

port = os.environ.get("APP_PORT", "8000")   # default if missing
print(port)                                  # 8000

db = os.environ["DATABASE_URL_MISSING_DEMO"]
# KeyError: 'DATABASE_URL_MISSING_DEMO'

Use .get() como padrão para configurações opcionais. Use colchetes, que aumentam KeyError, para configurações sem as quais o aplicativo não pode funcionar. Outras linguagens são semelhantes: process.env.APP_PORT em Node.js, System.getenv("APP_PORT") em Java e os.Getenv("APP_PORT") em Go.

Tudo é uma corda

Isso pega pessoas em todos os idiomas. Definimos uma porta e adicionamos “1” a ela:

os.environ["APP_PORT"] = "8080"
port = os.environ["APP_PORT"]
print(type(port).__name__, repr(port), port + "1")
# str '8080' 80801

os.environ["APP_PORT"] = 8080
# TypeError: str expected, not int

O valor voltou como texto e a adição produziu 80801, não 8081. Converta explicitamente, como em int(os.environ["APP_PORT"]). Booleanos são piores:

os.environ["DEBUG"] = "false"
print(bool(os.environ["DEBUG"]))   # True

Qualquer string não vazia é verdadeira em Python, então "false" tornou-se True. O modo de depuração teria sido ativado na produção. Analise booleanos deliberadamente:

def env_bool(name, default=False):
    value = os.environ.get(name)
    if value is None:
        return default
    return value.strip().lower() in {"1", "true", "yes", "on"}

env_bool("DEBUG")                   # False
env_bool("NOT_SET", default=True)   # True

Como os processos os herdam

Um processo filho recebe uma cópia do ambiente de seu pai. Iniciamos processos filhos do Python para ver isso:

child sees: 8080 inherited     # default: full copy of the parent's environment
child sees: 9000 None          # given a custom env: only what we passed
parent sees child var: None    # a child's changes never flow back

Seguem três aulas práticas. As variáveis ​​que você exporta ficam visíveis para todos os programas que você inicia, incluindo scripts que você não escreveu, o que é importante para os segredos. Passar por um ambiente personalizado substitui tudo, então inclua tudo o que a criança precisa. E um script não pode alterar as variáveis ​​de seu shell pai, e é por isso que as ferramentas pedem para você source um arquivo em vez de executá-lo.

Arquivos .env

Digitar muitas variáveis manualmente fica entediante, então a maioria dos projetos mantém os valores de desenvolvimento local em um arquivo .env:

# local development only
APP_PORT=8080
DATABASE_URL="postgres://dev:devonly@localhost:5432/shop"
FEATURE_SEARCH=true

Bibliotecas como python-dotenv e dotenv para Node.js carregam este arquivo na inicialização. O Node.js 20.6 e posterior também pode carregar um nativamente com --env-file. Para entender a ideia, escrevemos um pequeno analisador, que transformou o arquivo acima em:

{'APP_PORT': '8080',
 'DATABASE_URL': 'postgres://dev:devonly@localhost:5432/shop',
 'FEATURE_SEARCH': 'true'}

Use uma biblioteca real na prática. Ele lida com citações, escapes, comentários e valores multilinhas que nossa versão de 10 linhas ignora. Por padrão, a maioria dos carregadores dotenv não substituem variáveis ​​que já estão definidas, então os valores reais do ambiente prevalecem sobre o arquivo. Geralmente é isso que você deseja.

Dica: Adicione .env a .gitignore antes de seu primeiro commit e, em vez disso, envie um .env.example com cada nome de variável e valores de espaço reservado seguros. Novos desenvolvedores o copiam e preenchem seus próprios valores.

Validar configuração na inicialização

Uma variável ausente deve parar o aplicativo imediatamente com uma mensagem clara, e não causar um erro confuso uma hora depois. Verificamos as configurações necessárias ao carregar:

REQUIRED = ["DATABASE_URL", "SECRET_KEY"]

def check_required(env):
    missing = [k for k in REQUIRED if not env.get(k)]
    if missing:
        raise SystemExit(f"Missing required settings: {', '.join(missing)}")

# Missing required settings: SECRET_KEY

Bibliotecas de configuração digitadas vão além. As configurações Pydantic em Python, zod ou envalid em Node.js e as propriedades de configuração do Spring em Java convertem tipos e relatam todos os problemas de uma vez.

Variáveis e segredos de ambiente

Variáveis de ambiente são a maneira comum de passar segredos como chaves de API para um aplicativo e são muito melhores do que codificá-los. Eles têm pontos fracos para conhecer:

  • Os processos filhos os herdam, como mostrou nosso teste.
  • Eles podem aparecer em relatórios de falhas, páginas de depuração e logs que despejam o ambiente.
  • Em alguns sistemas, outros processos executados pelo mesmo usuário podem lê-los.

Para produção, muitas equipes mantêm segredos em um gerenciador dedicado, como HashiCorp Vault, AWS Secrets Manager, Azure Key Vault ou Google Secret Manager, e os injetam em tempo de execução. Alguns preferem montar segredos como arquivos em vez de variáveis. De qualquer forma, nunca imprima a configuração nos logs e gire qualquer segredo que já tenha sido confirmado no Git, mesmo depois de removê-lo do histórico.

No Docker e CI

# Docker (not run for this article)
docker run -e APP_PORT=8080 --env-file .env.production myapp

# GitHub Actions (not run for this article)
env:
  APP_PORT: 8080
  API_KEY: ${{ secrets.API_KEY }}

As plataformas CI armazenam segredos criptografados e os mascaram em logs. Evite incorporar segredos em imagens do Docker com ENV no Dockerfile, porque eles permanecem legíveis nas camadas da imagem. Consulte nosso tutorial do Docker Compose e guia de CI/CD do GitHub Actions para configurações completas.

Melhores práticas

  • Use nomes claros e maiúsculos com um prefixo, como SHOP_DB_URL.
  • Leia todas as configurações em um módulo, converta os tipos lá e valide na inicialização.
  • Forneça padrões seguros apenas para configurações não confidenciais.
  • Mantenha .env fora do Git e mantenha um .env.example.
  • Use um gerenciador de segredos na produção para valores confidenciais.
  • Documente cada variável, seu tipo e um valor de exemplo.

Depurando problemas de variáveis de ambiente

Quando um aplicativo se comporta como se faltasse uma configuração, execute estas verificações na ordem:

  1. Está definido no lugar certo? Uma variável exportada em uma aba do terminal não existe em outra, ou em um serviço iniciado pelo sistema.
  2. O aplicativo foi reiniciado? Um processo em execução mantém o ambiente com o qual foi iniciado. Alterar uma variável posteriormente não terá efeito até a reinicialização.
  3. O nome é exato? Os nomes diferenciam maiúsculas de minúsculas no Linux e no macOS, portanto App_Port e APP_PORT são variáveis ​​diferentes. O Windows os trata da mesma forma.
  4. Um arquivo .env está substituindo-o ou sendo ignorado? Verifique se o seu carregador substitui os valores existentes e qual arquivo ele realmente lê.
  5. Existem aspas ou espaços perdidos? Um valor copiado com um espaço à direita ou aspas extras pode falhar na análise ou na correspondência, de maneiras difíceis de detectar.

Para ver o que um processo realmente recebeu, imprima os nomes das variáveis, nunca os valores dos segredos. No Linux e no macOS, printenv lista o ambiente do shell atual e no PowerShell Get-ChildItem Env: faz o mesmo. Dentro de um contêiner, executar env a docker exec mostra a visualização do contêiner, que geralmente é diferente da do seu próprio shell.

Uma última dica: registre uma linha na inicialização que lista quais configurações opcionais estão usando seus padrões. Não custa nada e responde mais “por que está fazendo isso?” perguntas imediatamente.

Considerações finais

Variáveis ​​de ambiente são simples, mas escondem algumas armadilhas: tudo é uma string, os filhos as herdam e segredos podem vazar através delas. Trate-os como uma entrada para seu aplicativo. Leia-os em um só lugar, converta-os e valide-os antecipadamente e mantenha segredos reais dos arquivos que são confirmados.

Perguntas frequentes

O que é uma variável de ambiente?

É um valor de texto nomeado que um processo recebe de seu ambiente, como APP_PORT=8080. Os aplicativos os utilizam para configurações que mudam entre ambientes.

O que é um arquivo .env?

Um arquivo de texto de linhas KEY=value, carregado na inicialização por uma biblioteca como python-dotenv. É utilizado principalmente para o desenvolvimento local e não deve ser comprometido com segredos reais.

Por que minha variável de ambiente é uma string?

Os sistemas operacionais armazenam valores de ambiente como texto. Converta números com int() e analise booleanos explicitamente, porque a string “false” conta como verdadeira.

As variáveis ​​de ambiente são seguras para segredos?

Eles são melhores que a codificação permanente, mas os processos filhos os herdam e os logs podem expô-los. Os sistemas de produção geralmente usam um gerenciador de segredos dedicado.

Um script pode alterar as variáveis ​​do shell pai?

Não. Um processo filho obtém uma cópia do ambiente, portanto suas alterações não retornam. Para definir variáveis ​​no shell atual, crie o arquivo.

Continue aprendendo

Trabalhe com Stack Quora

Precisa de ajuda com testes, WordPress ou SEO?

Ajudamos as equipes a fornecer software melhor e a expandir seus sites. Conte-nos no que você está trabalhando e sugeriremos um próximo passo claro.

Continue lendo

Mais guias sobre testes, IA e desenvolvimento moderno

Gostou deste artigo? Escolha um tópico e vá direto para nossos tutoriais mais recentes.

Inteligência artificial

Explicação das alucinações de IA: por que os modelos de linguagem inventam as coisas e como reduzi-las

Explicação das alucinações de IA: o que são, por que os modelos de linguagem as produzem, tipos comuns, como reduzi-las com aterramento e instruções…

Leia o artigo →
DevOps

Variáveis de ambiente explicadas: arquivos .env, segredos e configuração bem feitos

Variáveis de ambiente explicadas com exemplos de Python que executamos: leitura e configuração, por que cada valor é uma string, arquivos .env, hera…

Leia o artigo →
Desenvolvimento de software

Explicação da limitação de taxa de API: algoritmos, cabeçalhos e tratamento de erros 429

Limitação de taxa de API explicada com uma simulação que executamos: token bucket, janela fixa, janela deslizante, 429 respostas, cabeçalhos de limi…

Leia o artigo →
Automação de testes

Zombando em Python: unittest.mock, patch e os erros que fazem os testes mentirem

Zombaria em Python com unittest.mock, testado: Mock e return_value, side_effect, patch onde um nome é usado, especificação e autospec, zombaria de d…

Leia o artigo →
Teste de software

Cobertura de código explicada: cobertura de linha versus filial e o que 100% realmente significa

Cobertura de código explicada com uma execução cobertura.py real: cobertura de linha, ramal e condição, leitura de um relatório, por que 100% de cob…

Leia o artigo →
Bancos de dados e SQL

DELETE vs TRUNCATE vs DROP em SQL: diferenças, reversão e quando usar cada um

DELETE vs TRUNCATE vs DROP explicado com SQL testado: o que cada um remove, rollback, aciona, redefinição de identidade, velocidade e como PostgreSQ…

Leia o artigo →
Bancos de dados e SQL

Exibições SQL explicadas: crie, use e evite as armadilhas, com exemplos

Visualizações SQL explicadas com consultas que executamos: criação de uma visualização, resultados ao vivo, ocultação de colunas, desempenho, visual…

Leia o artigo →
Desenvolvimento de software

Interface TypeScript vs tipo: diferenças reais, testadas com o compilador

Interface TypeScript versus tipo, verificada com o compilador real: o que ambos podem fazer, o que apenas um pode fazer, mesclagem de declarações, e…

Leia o artigo →
Desenvolvimento de software

Python *args e **kwargs explicados: funções flexíveis com exemplos claros

Python *args e **kwargs explicados com o código que executamos: empacotamento e descompactação, ordem dos parâmetros, argumentos somente de palavras…

Leia o artigo →
Desenvolvimento de software

Funções Python Lambda: sintaxe, usos reais e quando não usá-los

Funções lambda do Python explicadas com o código que executamos: sintaxe, classificação com chave, mapa e filtro, fechamentos, armadilha de ligação …

Leia o artigo →
Inteligência artificial

O que são agentes de IA? Como eles funcionam, com um loop de agente simples que você pode executar

O que são agentes de IA? Um guia simples para o loop do agente, ferramentas, memória e proteções, com um pequeno loop Python que executamos, riscos …

Leia o artigo →
DevOps

Monitoramento versus observabilidade: a verdadeira diferença, com logs, métricas e rastreamentos

Explicação do monitoramento versus observabilidade: falhas conhecidas e desconhecidas, logs, métricas e rastreamentos, um passo a passo do incidente…

Leia o artigo →
DevOps

Docker Volumes vs Bind Mounts: persista os dados do contêiner da maneira certa

Volumes Docker versus montagens de ligação explicadas: por que os dados do contêiner desaparecem, volumes nomeados, montagens de ligação, tmpfs, exe…

Leia o artigo →
Automação de testes

TDD vs BDD: principais diferenças, um recurso criado de ambas as maneiras e quando usar cada um

TDD vs BDD explicado com um recurso de desconto construído em ambos os sentidos: refatoração vermelho-verde com o código que executamos, cenários da…

Leia o artigo →
Desenvolvimento de software

Git merge vs rebase: diferenças, exemplos e quando usar cada um

Git merge vs rebase explicado com saída real do log, uma tabela comparativa, a regra de ouro do rebase e um fluxo de trabalho seguro para equipes.

Leia o artigo →
Desenvolvimento de software

List comprehension em Python: sintaxe, exemplos e quando evitar

List comprehension em Python explicada: sintaxe, filtros, if-else, laços aninhados, tempos medidos e quando um laço for comum é a melhor escolha.

Leia o artigo →
Bancos de dados e SQL

SQL JOIN explicado com exemplos (Inner, Left, Right, Full)

Entenda os joins SQL passo a passo com tabelas e consultas de exemplo: INNER, LEFT, RIGHT, FULL, CROSS e SELF, além de erros comuns e dicas de desem…

Leia o artigo →