正确做法是使用 bind mount 将本地目录挂载进容器,而非 SFTP 同步;因容器文件系统默认只读且不监听宿主机变更,SFTP 无法持久化且多数镜像无 sshd,挂载才能实现改代码即生效。

直接在 Docker 容器里改代码,不是“同步”,而是“挂载”——本地文件系统实时映射进容器,改完即生效,根本不用上传、不用部署、不走 SFTP。
为什么不能用 SFTP 同步到容器目录
很多人试过把代码通过 SFTP 传到容器里(比如 /var/www/html),结果刷新页面 500 或空白。因为:容器内路径是只读的临时文件系统;SFTP 写入的是容器启动时创建的 overlayfs 层,重启就丢;更关键的是,Docker 默认不会监听宿主机文件变化并 reload —— 它压根不知道你改了啥。
- 容器内
/var/www/html实际是镜像自带的空目录,除非你用VOLUME或bind mount显式挂载,否则写进去的内容不会持久,也不同步 - 用 SFTP 往容器 IP 的 22 端口传文件,本质是往容器里跑了个 sshd,但绝大多数 PHP 镜像(如
php:8.2-cli)根本不带 sshd,连都连不上 - 即使强行装了 sshd,PHP-FPM 或 Apache 也不会自动重载,改完
index.php不重启服务,旧字节码还在 opcache 里跑着
正确做法:用 bind mount 把本地目录挂进容器
这是唯一可靠、零延迟、符合 Docker 原意的“改代码即生效”方式。核心就是让容器启动时,把你的项目文件夹直接挂载为容器内的工作目录。
- 确保
docker-compose.yml里有明确的 volume 挂载,例如:services: app: image: php:8.2-apache volumes: - ./:/var/www/html注意:. /是相对路径,必须从项目根目录执行docker-compose up - 如果用单容器模式(没
docker-compose.yml),在 PhpStorm 配解释器时,Project root path和容器内路径必须严格一致,比如填/var/www/html,且 IDE 启动容器时会自动加-v $(pwd):/var/www/html - Apache/Nginx 配置里的
DocumentRoot必须指向这个挂载路径(/var/www/html),不能指向镜像内置路径(如/var/www)
PhpStorm 解释器配置必须复用挂载路径
IDE 要调用容器里的 php 执行 composer install 或单元测试,就必须知道“代码在哪”——这个位置,必须和容器运行时挂载的位置完全一样,否则路径错位,require 找不到文件,debugger 断点打不中。
立即学习“PHP免费学习笔记(深入)”;
- 进
Settings → Languages & Frameworks → PHP → Interpreter,选 Docker 或 Docker Compose 方式添加 - Interpreter path 填
/usr/bin/php(别写/bin/sh -c "php",会破坏参数传递) - 确认
Project root path是你本地项目根目录(如/Users/me/myapp),而容器内对应路径是/var/www/html—— 这俩必须在docker-compose.yml的volumes里对齐 - 如果用了 Xdebug,
xdebug.client_host应设为host.docker.internal(macOS/Windows),Linux 则填宿主机真实 IP;xdebug.client_port要和 PhpStorm 的Listen for Xdebug connections端口一致(默认9003)
改完代码不生效?先查这三件事
挂载没问题,但浏览器还是返回旧内容,大概率不是同步问题,而是缓存或服务未响应新文件。
- 检查容器是否真在用挂载路径:进容器执行
ls -l /var/www/html,输出应显示为-> /path/on/host(符号链接)或直接列出你本地的文件;若显示一堆空文件或旧内容,说明挂载失败 - 确认 Web 服务读的是挂载目录:在容器里
curl -s http://localhost | head -n 5,看返回内容是否随本地修改实时变动;如果不变,可能是 Apache 没 reload,或 PHP OPcache 没关(开发环境建议设opcache.enable=0) - 留意文件权限:Linux/macOS 下,容器内 PHP 进程用户(如
www-data)可能无权读取你本地挂载的文件,加user: "33:33"或chmod -R 755 .可解;Windows WSL2 用户常遇 uid/gid 错配,需在docker-compose.yml里显式指定
真正麻烦的从来不是怎么挂,而是挂完之后,容器里那个 PHP 进程到底有没有权限读、Web 服务有没有用对路径、OPcache 有没有骗你——这些细节漏一个,就卡在“明明改了却没变”。


















