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.
- 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
.envarquivos 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
.envfora 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:
- 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.
- 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.
- O nome é exato? Os nomes diferenciam maiúsculas de minúsculas no Linux e no macOS, portanto
App_PorteAPP_PORTsão variáveis diferentes. O Windows os trata da mesma forma. - Um arquivo .env está substituindo-o ou sendo ignorado? Verifique se o seu carregador substitui os valores existentes e qual arquivo ele realmente lê.
- 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
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.
Mais guias sobre testes, IA e desenvolvimento moderno
Gostou deste artigo? Escolha um tópico e vá direto para nossos tutoriais mais recentes.
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 →DevOpsVariá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 softwareExplicaçã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 testesZombando 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 softwareCobertura 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 SQLDELETE 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 SQLExibiçõ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 softwareInterface 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 softwarePython *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 softwareFunçõ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 artificialO 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 →DevOpsMonitoramento 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 →DevOpsDocker 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 testesTDD 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 softwareGit 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 softwareList 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 SQLSQL 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 →