根本原因是gitlab runner以无家目录、非交互方式运行,导致composer config -g配置丢失或不生效;应显式设置composer_home指向固定路径(如/cache/composer),配合cache声明该路径并初始化镜像源,才能持久复用配置与缓存。

GitLab Runner 执行 composer install 慢,根本原因往往不是网络本身,而是默认没启用国内镜像源,且每次构建都重新下载依赖、解压、安装——尤其在无缓存的 shared runner 上更明显。直接改 composer.json 不现实(污染项目),临时加 --repository 又难维护。最稳妥的做法,是在 Runner 环境里预设全局镜像代理。
为什么不能只靠 composer config -g repo.packagist?
GitLab Runner 默认以非交互式、无家目录($HOME 为空或指向临时路径)方式运行,composer config -g 写入的配置会丢失或不生效。另外,某些 Runner executor(如 docker 或 kubernetes)每次启动都是干净容器,-g 配置无法持久化。
- 检查当前生效配置:
composer config -g repo.packagist,很可能返回空或报错 - 即使写入成功,也仅对当前 shell 会话有效,CI job 启动的新进程看不到
-
COMPOSER_HOME环境变量未显式设置时,Composer 默认用/root/.composer或$HOME/.composer,而 Runner 容器里$HOME往往未定义
推荐方案:在 .gitlab-ci.yml 中显式设置 COMPOSER_HOME + 镜像源
不依赖全局配置,也不修改项目文件,而是让每个 job 显式控制 Composer 行为。关键点是把镜像源和缓存路径绑定到一个可复用、可挂载的路径上。
将GitHub webhook事件桥接为Pilot Protocol事件。使用此技能的时机:1. 需要在Pilot代理中接收GitHub事件 2. 想要触发代理...
- 始终设置
COMPOSER_HOME到固定路径,例如/cache/composer - 用
composer config --no-plugins --global repo.packagist composer https://mirrors.aliyun.com/composer/初始化该路径下的配置 - 配合
cache:声明缓存/cache/composer目录(注意:不是vendor/) - 务必加
--no-plugins,避免插件加载失败导致命令卡住
before_script: - export COMPOSER_HOME="/cache/composer" - mkdir -p "$COMPOSER_HOME" - composer config --no-plugins --global repo.packagist composer https://mirrors.aliyun.com/composer/ <p>cache: key: "$CI_JOB_NAME" paths:</p>
- /cache/composer
- vendor/
如果用 Docker executor,还要注意 PHP 镜像的 Composer 版本
很多官方 PHP 镜像(如 php:8.2-cli)自带 Composer 2.x,但部分旧版(如 php:7.4-cli)可能带的是 1.x —— 而阿里云镜像对 Composer 2 支持更稳定,1.x 遇到重定向或证书问题概率更高。
- 优先选用带 Composer 2 的基础镜像,比如
composer:2或php:8.2-cli - 若必须用旧镜像,建议显式升级:
curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer - 不要用
apt-get install composer,Debian/Ubuntu 官方源的 Composer 版本太老,且不支持--no-plugins - 镜像地址别用
https://packagist.phpcomposer.com(已停用),用https://mirrors.aliyun.com/composer/或https://packagist.org(仅作 fallback)
真正起效的关键不在“换镜像”,而在让 Composer 每次都读同一份配置、复用同一份缓存。一旦 COMPOSER_HOME 和 cache 路径对齐,首次安装后后续 job 的 composer install 通常能压到 10 秒内——前提是项目依赖没变。如果依赖频繁变动,那就得看 vendor/ 缓存是否被正确命中,而不是只盯着镜像速度。










