线上发布包体积大源于 vendor/ 混入开发依赖、测试文件、.git、未优化 autoloader 等冗余内容;必须采用多阶段构建,禁止在 final 阶段执行 composer install,否则会引入 composer 二进制、临时解压目录、全局缓存等运行时无用文件;copy 必须分拆(如 vendor 单独复制),避免误带 tests/.git 等;需主动清理 vendor/bin/docs/tests,并正确配置 buildkit 缓存与权限。

线上发布包体积大,根本不是 Composer 本身的问题,而是 vendor/ 里混进了不该存在的东西:开发依赖、测试文件、.git 目录、未优化的 autoloader、甚至 /root/.composer 缓存残留。多阶段构建不是“可选优化”,是必须项;否则你打包出来的镜像,50% 以上体积都属于运行时完全不需要的垃圾。
为什么 composer install 在 final 阶段执行一定会胖?
final 阶段如果还跑 composer install,就等于把 Composer 二进制、下载器、临时解压目录(/tmp/composer_*)、全局缓存路径(/root/.composer)全塞进生产镜像。这些路径不会被自动清理,且无法通过 COPY --from=builder 规避——因为它们根本没被显式复制,而是 RUN 过程中写入的。
- 不要在 final 阶段
RUN composer install,哪怕只加--no-dev也无效——它仍会下载、解压、生成未清理的 autoload 文件 - final 阶段的基础镜像(如
php:8.3-cli-alpine)通常不带unzip、git,composer install可能直接失败或 fallback 到慢速源码安装 - 一旦用了
--prefer-source或私有包没配好 auth,final 阶段还会暴露凭证风险
COPY --from=builder 路径写错,多阶段白搭
最常见错误是 COPY --from=builder /app . —— 看似简洁,实则把整个构建目录拖进 final 镜像:/app/tests、/app/.git、/app/docker-compose.yml 全部照单全收。体积膨胀 100MB 起步。
在 Linux 上通过 Docker 运行 OpenClaw,并使用 Tailscale 实现远程访问。⚠️ 涉及 sudo、Docker、Tailscale和凭证挂载——请先查阅安全章节...
- 必须分拆复制:
COPY --from=builder /app/vendor /var/www/html/vendor+COPY . /var/www/html - 确保 builder 阶段
WORKDIR和 final 阶段路径对齐,否则autoload.php路径失效 - 如果项目用 PSR-4 自动加载,还要确认
vendor/autoload.php被正确引用,别因路径错位导致Class not found
BuildKit --mount=type=cache 不生效的三个原因
启用 BuildKit 后加了 RUN --mount=type=cache,id=composer-cache,dest=/root/.composer/cache composer install,但缓存还是不复用?大概率卡在这三处:
- 没在 Dockerfile 开头声明语法:
# syntax=docker/dockerfile:1—— 缺这行,--mount直接被忽略 -
id=composer-cache在不同项目间复用时冲突:CI 环境建议用项目名哈希做 ID,比如id=project-x-composer-cache - builder 阶段用了
FROM composer:2这类专用镜像,但它的/root/.composer/cache默认权限是 root:root,而某些 CI runner 以非 root 用户运行,挂载失败静默跳过
最终镜像里还有 vendor/bin/ 和 docs/?清理要主动做
--no-dev 只过滤掉 require-dev 声明的包,但很多包(如 monolog/monolog、guzzlehttp/guzzle)自带 vendor/{pkg}/bin/、vendor/{pkg}/docs/、vendor/{pkg}/tests/,这些不会被自动剔除。
- builder 阶段安装后加清理指令:
RUN find vendor/ -name 'bin' -o -name 'docs' -o -name 'tests' -prune -exec rm -rf {} + 2>/dev/null || true - 避免用
composer dump-autoload --classmap-authoritative替代--optimize-autoloader:前者不删文件,后者才真正生成扁平 classmap 并跳过文件 stat - 检查 final 镜像是否含
vendor/bin/phpunit或vendor/symfony/console/Resources/—— 这些都是体积隐形杀手
真正的瘦身不在压缩技巧,而在每一层 COPY 和 RUN 的意图是否清晰:builder 阶段只负责产出干净的 vendor/,final 阶段只负责运行。任何模糊地带(比如“顺手把整个目录复制过来”“反正后面再删”)都会让体积失控。镜像大小不是 build 完才看的指标,而是每一步 COPY 和 RUN 都该心里有数的约束。










