
本文详解 laravel 应用通过 docker compose 连接 mysql 失败的常见原因及完整解决方案,涵盖环境变量配置、网络通信、权限设置与端口暴露等关键环节。
本文详解 laravel 应用通过 docker compose 连接 mysql 失败的常见原因及完整解决方案,涵盖环境变量配置、网络通信、权限设置与端口暴露等关键环节。
在使用 Docker 部署 Laravel 项目时,「Laravel 无法连接 MySQL」是最常见的集成问题之一,典型表现为 SQLSTATE[HY000] [1045] Access denied 或 Lost connection to MySQL server at 'waiting for initial communication packet'。这些问题并非代码缺陷,而是容器间网络、认证配置与服务初始化顺序不匹配所致。以下为系统性解决方案:
✅ 正确配置 MySQL 容器(关键修正)
原 docker-compose.yml 中存在多个隐患:
- ❌ MYSQL_ROOT_PASSWORD 与 MYSQL_PASSWORD 同值 + MYSQL_ALLOW_EMPTY_PASSWORD: 1 冲突,导致认证逻辑混乱;
- ❌ 未声明 MYSQL_USER,Laravel 默认尝试以 root 用户连接,而 MySQL 8+ 默认禁止 root 从远程主机登录;
- ❌ 使用 ./docker-compose/mysql:/docker-entrypoint-initdb.d 挂载初始化脚本,但未确保数据库已就绪即启动 Laravel,易引发连接超时。
推荐修正后的 db 服务配置:
db:
image: mysql:8.0
container_name: travellist-db
restart: unless-stopped
expose:
- "3306" # 仅对同一网络内容器开放(安全且必要)
environment:
MYSQL_DATABASE: ${DB_DATABASE:-laravel}
MYSQL_USER: ${DB_USER:-laravel}
MYSQL_PASSWORD: ${DB_PASSWORD:-secret}
MYSQL_RANDOM_ROOT_PASSWORD: "1" # 禁用 root 密码明文暴露
volumes:
- laravel-database:/var/lib/mysql # 使用命名卷持久化数据(比 bind mount 更可靠)
networks:
- travellist
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "healthcheck", "--password=healthpass"]
timeout: 20s
retries: 10
start_period: 40s
volumes:
laravel-database:
? 说明:expose 保证 app 和 nginx 容器可通过服务名 db 访问 MySQL(Docker 内置 DNS),无需 ports 暴露到宿主机——除非需外部工具(如 TablePlus)直连。若需外部访问,将 expose 替换为 ports: ["3306:3306"],并确保 MYSQL_USER 具备远程访问权限(见下文)。
✅ Laravel 环境配置必须匹配容器网络
在 Laravel 的 .env 文件中,DB_HOST 必须设为 MySQL 服务名 db(Docker 自动解析为对应容器 IP):
DB_CONNECTION=mysql
DB_HOST=db # ✅ 关键!不是 localhost 或 127.0.0.1
DB_PORT=3306
DB_DATABASE=${DB_DATABASE:-laravel}
DB_USERNAME=${DB_USER:-laravel}
DB_PASSWORD=${DB_PASSWORD:-secret}
⚠️ 注意:.env 中的 ${DB_*} 变量需与 docker-compose.yml 的 environment 字段一致,且应在运行 docker-compose up 前通过 export 或 .env 文件注入宿主机环境变量。
✅ 验证连接与排错步骤
-
确认容器网络互通:
docker exec -it travellist-app ping -c 3 db # 应成功收到响应
-
进入 App 容器测试 MySQL 连接:
docker exec -it travellist-app bash # 在容器内执行: php artisan tinker >>> DB::connection()->getPdo(); // 若报错,说明配置或 MySQL 未就绪
-
检查 MySQL 用户权限(MySQL 8+ 特别注意):
docker exec -it travellist-db mysql -u root -p${DB_PASSWORD} -e "SELECT host, user FROM mysql.user;" # 确保输出包含:'%' 'laravel'(允许任意主机连接)若缺失,手动授权(首次启动后执行一次):
docker exec -it travellist-db mysql -u root -p${DB_PASSWORD} -e \ "CREATE USER '${DB_USER:-laravel}'@'%' IDENTIFIED BY '${DB_PASSWORD:-secret}'; \ GRANT ALL PRIVILEGES ON \`${DB_DATABASE:-laravel}\`.* TO '${DB_USER:-laravel}'@'%'; \ FLUSH PRIVILEGES;"
✅ 补充建议:提升健壮性
- 添加依赖等待逻辑:在 app 服务启动前等待 MySQL 就绪,避免 Laravel 启动时 MySQL 尚未初始化完成。可在 Dockerfile 中使用 wait-for-it.sh 或在 docker-compose.yml 中配置 depends_on + healthcheck(如上所示)。
-
禁用 MySQL 8 默认密码策略(可选):若需兼容旧版客户端,在 db 的 environment 中添加:
MYSQL_INIT_COMMAND: "SET GLOBAL validate_password.policy=LOW;"
-
PHP 配置优化:确保 app 容器中 php.ini 启用 pdo_mysql 扩展(你的 Dockerfile 已正确安装,但建议验证):
RUN docker-php-ext-install pdo_mysql && docker-php-ext-enable pdo_mysql
通过以上配置,99% 的 Laravel-Docker-MySQL 连接问题将被根除。核心原则始终是:容器间通信靠服务名,权限控制靠独立用户,数据持久化靠命名卷,调试起点是 docker exec 直连验证。











