Переменные окружения в Python: с чего начать новичку
Как убрать пароли и ключи из кода и хранить их в переменных окружения. Разбор os.environ, файла .env и библиотеки python-dotenv за 20 минут.

К концу этого гайда вы уберёте пароль от базы и API-ключ из кода Python в отдельный файл, который не попадёт в Git. Настроите чтение переменных через os.environ и через файл .env. На всё уйдёт минут 20, если Python уже установлен. Проверено на Python 3.11 и 3.12, но подойдёт любая версия начиная с 3.8.
Что нужно иметь до начала
- Python 3.8 или новее. Проверьте командой
python --version(на некоторых системахpython3 --version). - Терминал: PowerShell или командная строка на Windows, Terminal на macOS и Linux.
- Любой текстовый редактор — VS Code, Sublime, даже Блокнот.
- Для файла
.envпонадобится пакетpython-dotenv, его поставим на шаге 4.
Права администратора не нужны — всё делается в рамках вашего проекта.
Зачем это вообще
Пароли, токены и ключи, вписанные прямо в код, — самая частая причина утечек. Стоит один раз запушить такой файл в публичный репозиторий, и ключ окажется у чужих людей за минуты: боты сканируют GitHub автоматически. Переменные окружения решают проблему просто — секрет живёт вне кода, а код читает его по имени.
Правило, которое стоит запомнить с первого дня: если значение отличается между вашим ноутбуком и сервером или его нельзя показывать посторонним — оно должно жить в переменной окружения, а не в коде.
Шаг 1. Читаем переменную окружения из кода
Начнём с чтения. Модуль os встроен в Python, ставить ничего не надо. Создайте файл main.py:
import os
# os.environ ведёт себя как словарь с переменными окружения
database_url = os.environ.get("DATABASE_URL")
print("DATABASE_URL =", database_url)
Запустите: python main.py. Скорее всего увидите DATABASE_URL = None — переменной пока нет, и метод get вернул None вместо ошибки. Это нормально, значение появится на следующем шаге.
Почему get, а не os.environ["DATABASE_URL"]: обращение через квадратные скобки к несуществующему ключу выбросит KeyError и уронит программу. get вернёт None — с этим проще работать.
Шаг 2. Задаём переменную в терминале
Теперь передадим значение. Синтаксис зависит от системы.
macOS и Linux (bash, zsh):
export DATABASE_URL="postgres://user:pass@localhost:5432/mydb"
python main.py
Windows PowerShell:
$env:DATABASE_URL = "postgres://user:pass@localhost:5432/mydb"
python main.py
Windows, классическая командная строка (cmd):
set DATABASE_URL=postgres://user:pass@localhost:5432/mydb
python main.py
Теперь вывод должен показать вашу строку вместо None. Если снова None — значит переменную задали в одном окне терминала, а запускаете в другом. Задавать и запускать нужно в одной сессии.
Важный нюанс: такая переменная живёт только до закрытия окна терминала. Это неудобно, если переменных много. Отсюда переходим к файлу .env.
Шаг 3. Создаём файл .env
В корне проекта создайте файл с именем ровно .env (с точкой в начале, без расширения). Внутри — пары «имя=значение», по одной на строку:
DATABASE_URL=postgres://user:pass@localhost:5432/mydb
API_KEY=YOUR_API_KEY
DEBUG=True
Обратите внимание: вокруг знака = пробелов нет, кавычки для простых значений не обязательны. Кавычки нужны, если в значении есть пробелы или спецсимволы.
Сразу закройте файл от Git
Файл .env не должен попасть в репозиторий — иначе смысл теряется. Создайте или откройте файл .gitignore и добавьте строку:
.env
Если проект уже под Git, проверьте командой git status, что .env не значится среди отслеживаемых файлов. Если он туда уже попал раньше — уберите из индекса: git rm --cached .env.
Шаг 4. Ставим python-dotenv и читаем .env
Python сам файл .env не читает — нужна библиотека. Самая распространённая — python-dotenv. Установите:
pip install python-dotenv
Проверьте, что установилось: pip show python-dotenv покажет версию. На момент написания актуальна ветка 1.x.
Теперь подключите её в main.py. Вызов load_dotenv() прочитает файл и подгрузит значения в os.environ:
import os
from dotenv import load_dotenv
# читает .env из текущей директории и кладёт значения в окружение
load_dotenv()
database_url = os.environ.get("DATABASE_URL")
api_key = os.environ.get("API_KEY")
print("DATABASE_URL =", database_url)
print("API_KEY =", api_key)
Запустите python main.py — теперь значения подтянутся из файла, без ручного export. В выводе появятся обе строки из .env.
Шаг 5. Задаём значение по умолчанию и приводим тип
Всё, что приходит из окружения, — строки. Даже DEBUG=True из файла Python видит как строку "True", а не как булево значение. Это ловушка новичков:
import os
from dotenv import load_dotenv
load_dotenv()
# второй аргумент get — значение по умолчанию, если переменной нет
port = int(os.environ.get("PORT", "8000"))
# строку "True" в bool превращаем сравнением, а не bool(...)
debug = os.environ.get("DEBUG", "False") == "True"
print("port =", port, type(port))
print("debug =", debug, type(debug))
Почему не bool(os.environ.get("DEBUG")): любая непустая строка, включая "False", даёт True. То есть bool("False") — это True, и вы получите обратное тому, что ожидали.
Если не получилось
ModuleNotFoundError: No module named 'dotenv'— пакет не установлен или установлен в другое окружение. Проверьте, чтоpipиpythonотносятся к одному интерпретатору. В виртуальном окружении сначала активируйте его, потом ставьте.- Значение равно
None, хотя в .env оно есть — чаще всего файл лежит не в той папке, откуда запускается скрипт.load_dotenv()ищет.envначиная с текущей рабочей директории. Укажите путь явно:load_dotenv("/полный/путь/.env"). - Файл называется
env.txtили.env.txt— Блокнот и проводник Windows любят добавлять расширение. Включите показ расширений и переименуйте ровно в.env. - Переменная из
exportперебивает значение из .env — по умолчаниюload_dotenv()не перезаписывает уже существующие переменные окружения. Если нужно наоборот, передайтеload_dotenv(override=True). - В значении есть
#и оно обрезается — символ решётки начинает комментарий. Оберните значение в кавычки:PASSWORD="a#b#c".
Полезная привычка: файл .env.example
Рядом с .env держите файл .env.example с теми же именами переменных, но без реальных значений. Его, в отличие от .env, коммитят в репозиторий — чтобы коллега или вы сами через полгода видели, какие переменные нужны проекту:
DATABASE_URL=
API_KEY=
DEBUG=False
Что делать дальше
Базовый механизм готов. Следующие шаги, когда упрётесь в них:
- Если переменных стало много и хочется проверять их наличие и типы при старте — посмотрите библиотеки
pydantic-settingsилиenvirons, они читают.envи валидируют значения. - В Django настройки часто читают из окружения через тот же
os.environвsettings.py— принцип тот же, что здесь. - На сервере и в CI переменные обычно задают не файлом, а в панели хостинга или в настройках пайплайна — файл
.envостаётся только для локальной разработки.
Управление секретами в проде (хранилища вроде Vault, шифрование) — отдельная большая тема, здесь мы разобрали только локальный старт.
Разбор научной статьи: Сложный текст — понятным языком, с терминами
Спросить за 12 ₽Источники: python-dotenv — официальная документация (PyPI), os.environ — документация стандартной библиотеки Python
Частые вопросы
А если у меня Python 2 или очень старая версия 3?
Нужен ли python-dotenv в продакшене?
Файл .env всё равно попал в Git, что делать?
Почему DEBUG из .env ведёт себя как True, хотя я написал False?
load_dotenv() не находит файл, хотя он есть. Почему?
Можно ли хранить в .env несколько окружений — dev и prod?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.