---
title: "Pydantic Settings: Configuração Segura em Python"
url: "https://python.dev.br/blog/pydantic-settings-configuracao-python/"
markdown_url: "https://python.dev.br/blog/pydantic-settings-configuracao-python.MD"
description: "Aprenda a usar pydantic-settings para configurar aplicações Python com variáveis de ambiente, .env, validação, testes e boas práticas para produção."
date: "2026-05-31"
author: "Equipe Python Brasil"
---

# Pydantic Settings: Configuração Segura em Python

Aprenda a usar pydantic-settings para configurar aplicações Python com variáveis de ambiente, .env, validação, testes e boas práticas para produção.


Configuração parece um detalhe pequeno até o primeiro deploy falhar porque uma variável de ambiente estava com nome errado, uma URL de banco veio vazia ou um token real foi parar no repositório. Em projetos Python, esse problema aparece em APIs com FastAPI, jobs de dados, automações internas, bots, CLIs, pipelines e aplicações que precisam rodar de forma diferente no notebook, no Docker, no CI e em produção.

O erro comum é espalhar `os.getenv()` pelo código inteiro. No começo funciona. Depois cada módulo passa a ler uma variável diferente, os valores não são validados, o padrão local vira comportamento implícito e ninguém sabe quais configurações são obrigatórias. Quando a aplicação cresce, configuração deixa de ser conveniência e vira contrato.

O **pydantic-settings** resolve esse ponto usando a validação do [Pydantic](/glossario/pydantic/) para carregar variáveis de ambiente, arquivos `.env` e valores tipados em uma classe única. Em vez de descobrir erro em produção, você falha cedo com uma mensagem clara. Para quem trabalha com [FastAPI](/glossario/fastapi/), [Docker em projetos Python](/guias/configurando-docker-python/), [testes com pytest](/guias/testes-com-pytest/) ou [SQLModel com FastAPI](/blog/sqlmodel-fastapi-python/), esse é um padrão simples que melhora segurança, manutenção e confiança no deploy.

## Quando usar pydantic-settings

Use pydantic-settings quando sua aplicação precisa ler configuração externa. Alguns exemplos:

| Cenário | Configurações comuns |
|---|---|
| API FastAPI | URL do banco, ambiente, CORS, chave JWT |
| Job de dados | caminho de entrada, bucket, API externa, timeout |
| Bot ou automação | token, canal, modo debug, limite de retries |
| CLI interna | endpoint, formato de saída, diretório de cache |
| Testes e CI | banco temporário, flags de feature, credenciais falsas |

Para um script descartável de 20 linhas, talvez `os.getenv()` seja suficiente. Para qualquer projeto que vai para GitHub, currículo, cliente, time ou produção, vale centralizar. Isso comunica maturidade: você não está apenas fazendo o código rodar na sua máquina, está preparando o projeto para ambientes diferentes.

## Instalação e primeiro Settings

Instale o pacote:

```bash
uv add pydantic-settings
```

Ou com `pip`:

```bash
pip install pydantic-settings
```

Crie um arquivo `app/settings.py`:

```python
from functools import lru_cache

from pydantic import AnyUrl, Field, PostgresDsn
from pydantic_settings import BaseSettings, SettingsConfigDict


class Settings(BaseSettings):
    model_config = SettingsConfigDict(
        env_file=".env",
        env_file_encoding="utf-8",
        extra="ignore",
    )

    app_name: str = "API Python Brasil"
    environment: str = Field(default="local", alias="APP_ENV")
    debug: bool = False
    database_url: PostgresDsn
    public_url: AnyUrl = "http://localhost:8000"
    request_timeout_seconds: int = Field(default=10, ge=1, le=60)


@lru_cache
def get_settings() -> Settings:
    return Settings()
```

Esse exemplo já mostra a diferença para `os.getenv()`. `database_url` é obrigatório e precisa ter formato de DSN PostgreSQL. `request_timeout_seconds` vira inteiro e só aceita valores entre 1 e 60. `debug` entende valores comuns de booleano vindos do ambiente. Se algo vier inválido, a aplicação falha no início, antes de abrir servidor, worker ou pipeline.

Um `.env.example` seguro poderia ser:

```env
APP_ENV=local
DEBUG=false
DATABASE_URL=postgresql://usuario:senha@localhost:5432/app
PUBLIC_URL=http://localhost:8000
REQUEST_TIMEOUT_SECONDS=10
```

Nunca faça commit do `.env` real. Faça commit apenas de `.env.example`, com nomes de variáveis e valores falsos. Essa prática aparece em qualquer checklist profissional de segurança porque evita vazamento de token, senha de banco, segredo JWT e credencial de API.

## Usando com FastAPI

Em uma API, injete a configuração de forma explícita:

```python
from fastapi import Depends, FastAPI

from app.settings import Settings, get_settings

app = FastAPI()


@app.get("/health")
def health(settings: Settings = Depends(get_settings)) -> dict[str, str]:
    return {
        "app": settings.app_name,
        "environment": settings.environment,
    }
```

Esse padrão evita importar uma instância global mutável em todos os arquivos. O `lru_cache` garante que a classe seja criada uma vez por processo, mas ainda permite sobrescrever a dependência nos testes. Para projetos maiores, você pode passar `settings.database_url` para a criação do engine SQLAlchemy ou SQLModel, configurar CORS a partir de uma lista validada e ajustar integrações externas por ambiente.

## Separando configuração de segredo

Nem toda configuração é segredo. `APP_ENV=production` ou `REQUEST_TIMEOUT_SECONDS=10` podem aparecer em logs e documentação. Já `DATABASE_URL`, `JWT_SECRET_KEY`, `OPENAI_API_KEY`, `SMTP_PASSWORD` e tokens de webhook devem ser tratados com cuidado.

Uma boa regra é:

- configuração operacional pode ir para `.env.example`, README e painel de deploy;
- segredo real deve ficar no gerenciador de secrets do provedor, no CI ou em ferramenta específica;
- logs nunca devem imprimir valores sensíveis;
- testes devem usar credenciais falsas ou bancos temporários.

Se você usa Docker Compose local, pode carregar `.env` para desenvolvimento, mas em produção prefira o mecanismo do provedor: variáveis do Cloud Run, secrets do Kubernetes, GitHub Actions Secrets, Gitea Actions Secrets, AWS Secrets Manager, Infisical, Doppler ou equivalente. O pydantic-settings não substitui um cofre de segredos; ele valida e organiza o que chega ao processo Python.

## Configuração por ambiente

Evite criar muitos arquivos mágicos como `.env.prod`, `.env.staging` e `.env.local` sem documentação. Em geral, o mais simples é manter uma classe única e trocar valores por ambiente:

```python
class Settings(BaseSettings):
    environment: str = Field(default="local", alias="APP_ENV")
    debug: bool = False
    database_url: PostgresDsn
    sentry_dsn: AnyUrl | None = None

    @property
    def is_production(self) -> bool:
        return self.environment == "production"
```

No código, tome decisões explícitas:

```python
settings = get_settings()

if settings.is_production and settings.debug:
    raise RuntimeError("DEBUG não deve ficar ativo em produção")
```

Esse tipo de trava parece simples, mas evita incidentes reais. Configuração é parte da confiabilidade da aplicação. Em APIs e jobs, também vale validar timeouts, limites de lote, URLs externas e flags de feature para não aceitar valores perigosos por engano.

## Testando configurações

Testes precisam provar que a aplicação falha quando configuração obrigatória está ausente e carrega corretamente quando as variáveis existem. Com `pytest`, use `monkeypatch`:

```python
import pytest
from pydantic import ValidationError

from app.settings import Settings


def test_settings_carrega_database_url(monkeypatch):
    monkeypatch.setenv("DATABASE_URL", "postgresql://user:pass@localhost:5432/app")

    settings = Settings()

    assert str(settings.database_url).startswith("postgresql://user:pass@localhost")


def test_settings_exige_database_url(monkeypatch):
    monkeypatch.delenv("DATABASE_URL", raising=False)

    with pytest.raises(ValidationError):
        Settings()
```

Se você usa `get_settings()` com `lru_cache`, limpe o cache entre testes que mudam ambiente:

```python
from app.settings import get_settings


def test_cache(monkeypatch):
    get_settings.cache_clear()
    monkeypatch.setenv("DATABASE_URL", "postgresql://user:pass@localhost:5432/app")
    assert get_settings().environment == "local"
    get_settings.cache_clear()
```

Essa disciplina evita testes intermitentes, especialmente quando a suíte roda no CI com variáveis diferentes da máquina local.

## Boas práticas para projetos reais

Mantenha a classe `Settings` pequena o suficiente para ser entendida. Se ela virar um arquivo enorme, separe por contexto: `DatabaseSettings`, `AuthSettings`, `EmailSettings` e uma configuração principal que compõe tudo. Ainda assim, evite abstração cedo demais; uma classe simples resolve a maior parte dos projetos.

Use nomes claros e estáveis. `DATABASE_URL` é melhor do que `DB`, `APP_ENV` é melhor do que `MODE`, `PUBLIC_URL` é melhor do que `URL`. Documente cada variável obrigatória no README e mantenha `.env.example` sincronizado. Quando remover uma variável, remova também da documentação.

Também vale validar listas e URLs com tipos específicos. Em vez de aceitar qualquer string para CORS, modele uma lista de URLs permitidas. Em vez de aceitar timeout sem limite, use `Field(ge=1, le=60)`. Em vez de aceitar ambiente arbitrário, use `Literal["local", "staging", "production"]` quando fizer sentido.

## Como isso melhora seu portfólio

Para quem busca vaga Python no Brasil, pydantic-settings é um detalhe que diferencia projeto de curso de projeto profissional. Um recrutador técnico ou pessoa entrevistadora percebe quando o README explica variáveis, o `.env.example` existe, os segredos não estão no GitHub, os testes cobrem configuração e a aplicação falha de forma previsível.

Esse padrão combina bem com APIs, automações, pipelines e projetos de dados. Uma API com [FastAPI](/glossario/fastapi/) e [SQLModel](/blog/sqlmodel-fastapi-python/) fica mais convincente quando tem configuração tipada. Um pipeline de [ETL com Python](/blog/etl-python-2026/) fica mais seguro quando URL de API, timeout e destino de carga não estão hardcoded. Um projeto em [Docker](/guias/configurando-docker-python/) fica mais realista quando separa `.env.example`, Compose local e variáveis de produção.

Se o projeto crescer para microsserviços ou ferramentas de infraestrutura, também faz sentido estudar como outras linguagens lidam com configuração. Em serviços Go, por exemplo, é comum combinar variáveis de ambiente, structs de configuração e validação no startup; veja o ecossistema em <a href="https://golang.com.br/" target="_blank" rel="noopener noreferrer" onclick="umami.track('portfolio-site-click', { destination: 'golang.com.br' })">Go Brasil</a> para comparar padrões de backend.

## Checklist final

Antes de publicar um projeto Python, revise:

- existe uma classe central de configuração;
- variáveis obrigatórias são validadas no startup;
- `.env.example` está no repositório;
- `.env` real está no `.gitignore`;
- segredos reais não aparecem em README, testes ou logs;
- testes cobrem pelo menos o caminho feliz e a variável obrigatória ausente;
- Docker, CI e deploy usam os mesmos nomes de variáveis.

Pydantic-settings não é uma ferramenta chamativa, mas resolve uma dor recorrente. Ele transforma configuração em contrato tipado, reduz surpresa entre ambientes e deixa seu projeto mais fácil de operar. Para aplicações Python em 2026, essa é uma das pequenas práticas que mais passam sensação de código pronto para uso real.
