Токен бота, ключ API, строка подключения к базе — всё это значения, которые не должны лежать в коде и при этом не обязаны лежать в файле .env. Переменные окружения работают и без него: код читает их прямо из окружения процесса, а значения попадают туда оттуда, где запущено приложение. Разбираем, почему .env перестаёт помогать ровно в тот момент, когда проект уезжает с ноутбука на сервер, и как задать переменные окружения вообще без файла.
Почему токен нельзя держать в коде
Токен — это пароль. Вставив его в код строкой TOKEN = "7123456789:***", вы кладёте пароль туда, где живёт исходник: в архив, в репозиторий, в переписку, в скриншот. Код вы кому-то покажете или отдадите — токен уедет вместе с ним.
Утечка токена — это чужой доступ к вашему проекту. С токеном бота можно читать переписку и писать от его имени, с ключом API — тратить ваш лимит в платном сервисе. Замечается такое не сразу: узнаёте вы об этом по счёту или по жалобам пользователей.
Вывод простой: секретные значения должны быть отделены от кода, и переменные окружения — самый простой способ это сделать.
.env — удобство для ноутбука, а не механизм сервера
Файл ничего не изобретает. Библиотеки вроде python-dotenv или dotenv просто открывают .env и раскладывают его содержимое в обычные переменные окружения — те же самые, которые код умеет читать и без них.
Локально это удобно. Пока проект на вашем компьютере, файл рядом с кодом избавляет от ручного export в терминале при каждом запуске.
На сервере файл начинает работать против вас. Он едет внутри архива или папки, которую вы заливаете в GitHub, и забирает с собой все ключи. Его забывают обновлять — и хостинг держит ключ, отозванный месяц назад. А когда файла на сервере нет, приложение падает с невнятной ошибкой про пустое значение.
Читайте значения из окружения, а не из файла
Коду всё равно, откуда пришло значение. Пусть он читает переменные напрямую:
import os
TOKEN = os.environ["BOT_TOKEN"]
DEBUG = os.getenv("DEBUG", "false")
Для Node.js это ровно то же самое:
const token = process.env.BOT_TOKEN;
Тогда один и тот же код работает везде — и на вашей машине, где переменные заполняет .env, и на сервере, где их подставляет платформа. В Go для этого есть os.Getenv, в PHP — getenv(), в Java — System.getenv(): подходящий вызов найдётся в любом языке.
Секреты задаются в панели, а не в архиве
На Dockbox значения вводятся при настройке проекта. Платформа смотрит, какие переменные ждёт код, и просит их в форме: токен бота, ключ API, строку подключения к базе. Они хранятся зашифрованными и подставляются в окружение при старте — файла на сервере нет, а код остаётся тем же.
Изменить значение можно в любой момент в панели проекта: вводите новое, сохраняете — контейнер перезапускается с ним. Пересобирать и заливать архив заново не нужно; заодно так удобно менять тестовый ключ на боевой.
Значения не показываются никому. Имена переменных видны, а сами значения закрыты — в том числе от вас: посмотреть их обратно нельзя, только перезаписать.
.env — в .gitignore, имена — в .env.example
Первое, что надо сделать, — добавить строку .env в .gitignore, чтобы файл не доехал до GitHub, и проверить, что его нет в архиве, который вы загружаете. Это самый частый путь утечки: файл просто лежит в папке с кодом и путешествует вместе с ней.
Второе — держите рядом .env.example со списком имён переменных и без значений. Его коммитить безопасно, и он отвечает на вопрос, что нужно проекту для запуска:
BOT_TOKEN=
DATABASE_URL=
API_KEY=
Через полгода этот список пригодится в первую очередь вам самому, а не только тому, кто получит код после вас.
Ротируйте ключи, которые уже утекли
Если .env или ключ когда-то попадали в репозиторий, считайте его скомпрометированным — даже в приватном репозитории. Удалить файл и закоммитить заново недостаточно: старое значение остаётся в истории и доступно каждому, у кого есть доступ к коду.
Работает только одно: отозвать ключ в сервисе, который его выдал, создать новый и положить его в настройки проекта.
Переменные фронтенда не бывают секретами
Всё, что сборщик подставляет на этапе сборки, — переменные с префиксами VITE_, NEXT_PUBLIC_ или REACT_APP_ — оказывается внутри файлов, которые скачивает браузер. Любой посетитель прочитает их в инструментах разработчика.
Адрес API или публичный идентификатор там уместны, а ключ платного сервиса или пароль от базы — нет. Всё секретное живёт на серверной стороне, а фронтенд обращается к ней.
Сколько это стоит на Dockbox
Starter — 990 ₽/мес, первый месяц бесплатно: 1 vCPU, 2 ГБ RAM, 20 ГБ SSD. В цену входят сборка проекта из вашего кода, деплой из Git, хранение секретов, логи и HTTPS — отдельных счетов за трафик или сборки нет.
Pro — 2 490 ₽/мес (2 vCPU, 4 ГБ RAM, до 5 проектов), Team — 4 990 ₽/мес. Контейнеры работают круглосуточно: ни лимита часов, ни засыпания по неактивности. Цены — в рублях.
Правило простое: .env остаётся локальным удобством, а на сервере значения живут отдельно от кода, в одном месте, где их видно и можно поменять. Если приложение всё равно не видит переменную, хостинг тут обычно ни при чём: либо имя отличается от того, что написано в коде, либо код всё ещё читает файл вместо окружения. И то, и другое чинится в самом проекте — и работает одинаково везде.
Частые вопросы
Почему приложение не видит переменную окружения?
Чаще всего дело в имени: регистр важен, и API_KEY с Api_Key — разные переменные. Вторая причина — код читает файл, а файла на сервере нет. Третья — значение добавили после старта, и нужен перезапуск или новый деплой. Логи в панели показывают, какой из трёх случаев ваш.
Что делать, если .env уже закоммичен в GitHub?
Считать ключи утекшими и выпустить новые в сервисе, который их выдал. Удаление файла не чистит историю: старое значение остаётся в прошлых версиях и доступно всем, у кого есть доступ к коду. Приватный репозиторий тоже не спасает.
Как поменять значение, не загружая код заново?
Откройте проект в панели, введите новое значение в настройках и сохраните — проект перезапустится с ним. Код и архив при этом не трогаются, а ссылка на проект не меняется.
Можно ли хранить в переменной строку подключения к базе?
Да, это обычная практика: приложение берёт хост, пользователя и пароль из окружения и подключается с ними. На Dockbox строка подключения запрашивается при настройке и хранится зашифрованной. Если база поднимается рядом с приложением в вашем docker-compose, адрес сервиса тоже передаётся переменной, а не пишется в код.
Переменные VITE_ и NEXT_PUBLIC_ — это секреты?
Нет. Они попадают в файлы сайта, которые скачивает браузер, поэтому прочитать их может любой посетитель. Для адреса API или публичного идентификатора это нормально, а для ключа платного сервиса или пароля от базы — нет.