Короткий ответ
Секреты передают через переменные окружения процесса (или через специализированный секрет-менеджер — Vault, AWS Secrets Manager, k8s Secrets), которые приложение читает при старте; в репозиторий попадает только шаблон/пример конфигурации без реальных значений (.env.example), а не сами секреты.
Как это работает подробнее
Приложение при старте читает значения через os.environ/pydantic-settings/django-environ, ожидая, что реальные значения будут подставлены средой выполнения (CI/CD, оркестратор контейнеров, systemd-unit), а не захардкожены. Секрет-менеджеры дополнительно дают возможность централизованной ротации и аудита доступа к секретам.
Пример кода
example.yaml
# docker-compose.yml (пример, без реальных секретов)
services:
web:
image: myapp:latest
environment:
- DATABASE_URL=${DATABASE_URL}
- SECRET_KEY=${SECRET_KEY}
# Реальные значения DATABASE_URL/SECRET_KEY задаются в .env,
# который не попадает в git (указан в .gitignore), либо приходят
# из секрет-хранилища оркестратора при деплое.Что использовать на практике
- Практика. Для разных окружений (dev/staging/prod) используют разные наборы секретов с разным уровнем доступа; в CI важно не печатать секреты в логах и явно помечать переменные как secret/masked. Ротация ключа не должна требовать пересборки образа приложения — значение должно подхватываться через переменные окружения/секрет-хранилище при перезапуске.
Подводные камни
- Риск. Коммитят
.envс реальными значениями «один раз, временно»; хранят одинаковый секрет для всех окружений, из-за чего компрометация dev-окружения автоматически угрожает production; логируют полный объект настроек целиком (например, при ошибке конфигурации), случайно выводя секреты в лог-агрегатор.