gitlab ci中composer依赖加速的核心是复用~/.composer/cache而非vendor,因后者含平台相关符号链接、编译产物和autoload逻辑,易致跨runner环境不一致;必须显式设composer_cache_dir、缓存~/.composer/cache、用composer.lock哈希作key,并加--no-interaction --prefer-dist --optimize-autoloader --no-dev参数。

直接缓存 vendor/ 目录是错的,GitLab CI 中 Composer 依赖加速的核心是复用 ~/.composer/cache,而不是 vendor —— 后者含平台相关符号链接、扩展编译产物和 autoload 生成逻辑,跨 runner 极易出错。
为什么 composer install 在 CI 里总慢?
不是网络或机器问题,而是每次构建都重新下载 zip 包、解压、写入 vendor、生成 autoload —— 这些步骤在无缓存时全都要重来。Composer 自带缓存机制,但 GitLab CI 默认不挂载、不指定路径,等于没启用。
-
~/.composer/cache/files存的是已下载的 zip 归档和元数据,与 PHP 版本、OS、runner 环境解耦,适合跨 job 复用 -
vendor/是安装结果,含vendor/bin/phpunit这类符号链接(指向 runner 本地路径)、扩展 .so 文件、autoload_classmap.php 等,直接缓存会引发路径错误或 autoload 冲突 - 只缓存
vendor/或只缓存~/.composer/cache都不完整;但优先级必须是后者,前者可选且需额外约束
必须显式设置 COMPOSER_CACHE_DIR 环境变量
GitLab CI runner 不预设该变量,Composer 仍会往默认 ~/.composer/cache 写 —— 但这个路径可能被 runner 清理,或因权限问题写入失败。不设就等于没配缓存。
- 在
before_script中加:export COMPOSER_CACHE_DIR="$HOME/.composer/cache" - 紧接着 mkdir:
mkdir -p "$COMPOSER_CACHE_DIR",避免因目录不存在导致后续写入静默失败 - 不要用
$CI_PROJECT_DIR/.composer-cache替代:它随 job 清理,且无法跨 job 共享 - 别用
composer config --global cache-dir:CI 容器生命周期短,配置不持久,无效
cache: 配置必须匹配实际路径和 key 策略
GitLab 的 cache: 块只负责“保存和恢复文件”,是否命中取决于路径是否一致、key 是否随依赖变化而更新。
-
paths:必须写成- ~/.composer/cache,不能漏掉末尾斜杠,也不能写成~/.composer/cache/files(files是子目录,但 GitLab 缓存整个目录树,写父目录更稳妥) -
key:必须基于composer.lock内容哈希,不能只用$CI_COMMIT_REF_SLUG或$CI_JOB_NAME—— 否则 lock 文件变更后仍用旧缓存,安装结果与 lock 不符 - 推荐写法:
key: files: [composer.lock],GitLab 自动计算文件内容哈希,最可靠 - 若需区分 PHP 版本(如并行跑 8.2 / 8.5),加
prefix: "php-${PHP_VERSION}",避免版本混用
composer install 必须带的四个参数
缺一不可,否则缓存可能白配、命令卡住、或 autoload 性能差。
-
--no-interaction:跳过 auth token、yes/no 提示,CI 没终端,不加会 hang 住 -
--prefer-dist:强制走 zip 包而非 git clone,速度快、体积小、缓存友好;某包没 dist 时 Composer 自动 fallback,无需担心 -
--optimize-autoloader(或-o):生成 classmap,减少文件系统查找,生产环境必备 -
--no-dev:CI 构建通常不需要 PHPUnit、PHPStan 等 dev 依赖,减小 vendor 体积、缩短安装时间
最终命令就是:composer install --no-interaction --prefer-dist --optimize-autoloader --no-dev。注意顺序无关,但少一个都可能让缓存失效或构建失败。
最容易被忽略的是 COMPOSER_CACHE_DIR 没设、composer.lock 被 .gitignore 排除(导致 GitLab 计算哈希时跳过)、以及 cache: paths 写错成 vendor/ —— 这三处出错,缓存就形同虚设。











