Docker分离部署NGINX与PHP-FPM共享卷权限报错求解
Docker本地命名卷首次挂载时,会将先启动的容器内挂载目标路径的文件复制到卷中,但卷内文件的所有权默认继承容器内原路径的权限配置。
你的私有PHP-FPM镜像构建时如果用root用户拷贝应用代码,没有将cache这类需要写入的目录权限分配给PHP-FPM的运行用户,挂载后PHP-FPM进程以非root用户运行,就会因无写入权限抛出Permission denied错误。
另外跨容器挂载卷时Linux仅通过UID/GID校验权限,和容器内的用户名无关,只要两个容器内访问卷的进程UID/GID匹配对应权限即可正常访问。nginx仅需静态文件读权限,不需要写入权限,不要为了省事给目录设置777权限,违反最小安全原则。
第一步:在构建私有PHP-FPM镜像阶段固定权限(推荐方案,性能最优、最安全)
调整你的PHP镜像Dockerfile,在拷贝代码后直接设置正确的所有权和权限,示例配置如下(以Alpine版PHP-FPM基础镜像为例,Debian系逻辑一致):FROM php:8.2-fpm-alpine # (可选)固定PHP-FPM运行用户www-data的UID/GID为82,和nginx:alpine镜像内运行用户的GID兼容,方便权限配置 RUN apk add --no-cache shadow && \ usermod -u 82 www-data && \ groupmod -g 82 www-data && \ apk del shadow # 拷贝项目代码到容器内工作路径 COPY . /var/www/html # 统一设置代码目录权限:所有文件默认755权限,所有者为www-data,仅给需要写入的目录分配组写权限 RUN chown -R www-data:www-data /var/www/html && \ chmod -R 755 /var/www/html && \ chmod -R 775 /var/www/html/cache /var/www/html/logs # 把所有需要PHP写入的目录列在这里构建新的私有镜像替换原有镜像即可。
因为你的compose配置中app服务在server服务之前启动,命名卷首次初始化时会直接从app容器拷贝已经设置好正确权限的代码,nginx进程默认对755权限的文件有读权限,完全满足静态文件分发需求,不需要额外调整nginx容器的用户配置。第二步:清理旧的无效命名卷
如果你之前已经启动过服务,本地已经存在初始化过的旧code卷,Docker不会重复覆盖卷内已有文件和权限,需要先删除旧卷再重新启动:docker compose down -v注意:该操作会删除卷内所有存储数据,生产环境执行前请提前备份卷内内容。
可选临时方案(不推荐生产长期使用,适合快速验证)
如果暂时不方便重新构建镜像,可以在app服务的启动命令中增加临时权限修正逻辑,启动时先给需要写入的目录授权再启动PHP-FPM,配置示例:app: image: PRIVATE_IMAGE volumes: - code:/var/www/html networks: - inside restart: always # 替换www-data为你镜像内实际运行PHP-FPM的用户 command: > sh -c "chown -R www-data:www-data /var/www/html/cache && chmod -R 775 /var/www/html/cache && docker-php-entrypoint php-fpm"该方案的缺点是应用体量较大时,每次启动递归修改权限会拉长启动时间。
启动服务后执行以下操作验证权限正常:
- 进入app容器,执行
su -s /bin/sh www-data -c "touch /var/www/html/cache/test_write.txt",无报错即说明PHP-FPM写入权限正常 - 访问应用页面,确认不再抛出权限错误,静态资源可正常加载
内容的提问来源于stack exchange,提问作者Francesco Posa

