Подключение базы и .env в Laravel: разбор на живом примере
Приложение отдаёт SQLSTATE[HY000] [2002] Connection refused, а на другой машине всё работает. Разбираем, как Laravel читает .env, где кешируется конфиг и почему миграции падают молча.

Классический сценарий: клонируете проект, запускаете php artisan migrate и получаете SQLSTATE[HY000] [2002] Connection refused. Или хуже — на проде после деплоя приложение молча продолжает ходить в старую базу, хотя в .env уже прописаны новые креды. Оба случая упираются в одно: как именно Laravel читает .env, когда он кеширует конфиг и в каком порядке значения перекрывают друг друга.
Разбираем на Laravel 11.x с PostgreSQL 16 и MySQL 8. Речь не про то, как «настроить базу с нуля» — про то, где эта настройка ломается на живом проекте и как понять, какое значение реально попало в приложение.
Как значение доходит от .env до подключения
Заблуждение, из-за которого теряют часы: думать, что приложение читает .env при каждом запросе. Это не так. Файл .env — источник для переменных окружения, а конфигурация подключения живёт в config/database.php. Между ними — функция env(), а поверх всего — кеш конфига.
Цепочка выглядит так:
- Пакет
vlucas/phpdotenvпри старте читает.envи кладёт значения в переменные окружения процесса. - Файлы в
config/вызываютenv('DB_HOST', '127.0.0.1')— берут значение окружения или дефолт. - Соединение с базой строится по массиву из
config('database.connections.<...>'), а не из.envнапрямую.
Ключевое правило Laravel:
env()надёжно работает только внутри файловconfig/. Вызовenv()в контроллере или сервисе после кеширования конфига вернётnull— переменной в окружении уже нет.
Минимальный блок .env для базы
Для стандартного подключения нужны пять переменных плюс выбор драйвера. Ниже — рабочий пример для PostgreSQL:
# .env, драйвер pgsql (Laravel 11.x)
DB_CONNECTION=pgsql
DB_HOST=127.0.0.1
DB_PORT=5432
DB_DATABASE=shop
DB_USERNAME=shop_user
DB_PASSWORD=YOUR_DB_PASSWORDДля MySQL меняется драйвер и порт по умолчанию:
# .env, драйвер mysql (Laravel 11.x)
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=shop
DB_USERNAME=shop_user
DB_PASSWORD=YOUR_DB_PASSWORDИмена ключей (DB_CONNECTION, DB_HOST и остальные) — те, что читает стандартный config/database.php в свежем скелете. Если вы переименовали ключ в конфиге, он должен совпадать с именем в .env, иначе значение просто не подхватится.
Где это ломается на живом проекте
1. Кеш конфига держит старые значения
Самая частая причина «поменял .env, ничего не изменилось». Если на сервере выполнялась команда php artisan config:cache, Laravel собрал все config/*.php в один файл bootstrap/cache/config.php и больше не читает .env вообще. Меняете пароль в .env — приложение продолжает подключаться со старым.
Проверить, закеширован ли конфиг:
# покажет текущее значение так, как его видит приложение
php artisan tinker
>>> config('database.connections.pgsql.host')Если значение не совпадает с .env — сбросьте кеш:
# сбросить кеш конфига
php artisan config:clear
# на проде — пересобрать заново после правки .env
php artisan config:cacheПравило для прода: любое изменение .env без последующего config:cache (или хотя бы config:clear) не вступит в силу, если кеш был собран. Внесите это в скрипт деплоя.
2. Значение с пробелом или спецсимволом в пароле
Пароль вида p@ss word#1 без кавычек обрежется на пробеле или на # (символ комментария). phpdotenv требует брать такие значения в двойные кавычки:
DB_PASSWORD="p@ss word#1"Симптом — SQLSTATE[HY000] [1045] Access denied при том, что руками тем же паролем в консоли базы вы заходите.
3. 127.0.0.1 против localhost
Не одно и то же. localhost у MySQL часто означает подключение через Unix-сокет, а 127.0.0.1 — через TCP на порт. Если база слушает только TCP, а вы указали localhost, получите Connection refused. В Docker 127.0.0.1 вообще указывает внутрь самого контейнера приложения — базу надо искать по имени сервиса из docker-compose.yml, например DB_HOST=db.
Несколько подключений в одном проекте
Реальная задача: основная база на PostgreSQL, а логи или аналитика — в отдельной MySQL. В config/database.php в массиве connections объявляются оба, а в .env для второго заводятся отдельные переменные:
# второе подключение
DB_LOG_HOST=127.0.0.1
DB_LOG_PORT=3306
DB_LOG_DATABASE=analytics
DB_LOG_USERNAME=analytics_user
DB_LOG_PASSWORD=YOUR_SECOND_DB_PASSWORDВ коде запроса подключение выбирается явно:
// выбираем именованное подключение, а не дефолтное
$rows = DB::connection('logs')
->table('events')
->where('user_id', $userId)
->get();Имя logs должно совпадать с ключом массива в config/database.php. Дефолтное подключение задаётся значением DB_CONNECTION.
Сравнение способов задать конфигурацию
| Способ | Когда срабатывает | Риск |
|---|---|---|
Прямо в .env | Всегда, если конфиг не закеширован | После config:cache изменения игнорируются |
Захардкожено в config/database.php | Всегда, даже с кешем | Секреты попадают в git, разные окружения ломаются |
| Переменные окружения системы (не файл) | Читаются, если .env их не перекрывает | Труднее отследить, откуда взялось значение |
Для секретов на проде многие уходят от файла .env к системным переменным окружения или менеджеру секретов, но принцип чтения через env() в config/ остаётся тем же.
Проверка перед первым запросом
Прежде чем гонять миграции, убедитесь, что соединение живое:
php artisan tinker, затемDB::connection()->getPdo();— если вернулся объект PDO без исключения, коннект есть.php artisan migrate:status— покажет список миграций и, если база недоступна, честно упадёт с текстом ошибки драйвера.- Проверьте, что PHP-расширение драйвера установлено:
php -m | grep pdo. Для PostgreSQL нуженpdo_pgsql, для MySQL —pdo_mysql. Без расширения будетcould not find driver.
Что делать дальше
После того как соединение поднялось: пропишите config:cache и config:clear в правильном порядке в скрипт деплоя, чтобы прод не жил на старом кеше. Отдельно решите вопрос секретов — держать пароль базы в .env на сервере под доступом только у процесса приложения или вынести в менеджер секретов. Пул соединений и таймауты драйвера — отдельная тема, здесь мы разобрали только доставку конфигурации до подключения.
Разбор научной статьи: Сложный текст — понятным языком, с терминами
Спросить за 12 ₽Источники: Laravel 11.x — Database: Getting Started, Laravel 11.x — Configuration (env и кеш конфига)
Частые вопросы
Поменял пароль в .env, а приложение подключается со старым. Почему?
У меня другая версия Laravel, например 10.x. Инструкция подойдёт?
Что делать, если вижу ошибку could not find driver?
Почему localhost не работает, а 127.0.0.1 работает?
Можно ли вызывать env() в контроллере или сервисе?
Как безопасно хранить пароль базы на проде?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.