permission denied (publickey) 表明 composer 通过 git 使用 ssh 拉取私有仓库时密钥认证失败,需检查密钥存在性、ssh-agent 加载、~/.ssh/config 配置、known_hosts 及 gitlab url 格式是否正确。

composer install 时提示 Permission denied (publickey)
这是最典型的信号:Composer 正在用 SSH 尝试拉取私有 Git 仓库(比如 GitHub/GitLab 私有库),但系统没找到可用的 SSH 密钥,或密钥没加到 ssh-agent。
根本原因不是 Composer 本身不支持 SSH,而是它依赖 Git 命令行行为 —— 只要 git clone git@github.com:org/repo.git 在终端能成功,Composer 就能成功。
- 检查当前用户下是否已有 SSH 密钥:
ls -al ~/.ssh/id_rsa*,没有就先用ssh-keygen -t ed25519 -C "your@email"生成 - 确保
~/.ssh/config里为对应域名配置了正确的 Host 别名和 IdentityFile(尤其当用多个密钥时) - 运行
ssh-add -l看密钥是否已加载;没看到就执行ssh-add ~/.ssh/id_rsa(路径按实际改) - 测试连通性:
ssh -T git@github.com或ssh -T git@gitlab.com,看到 “You’ve successfully authenticated” 才算过关
composer.json 中写 git@ 地址却 fallback 到 HTTPS
Composer 默认会把 git@host:path/repo.git 解析成 SSH 协议,但如果你发现它悄悄换成了 HTTPS(比如日志里出现 https://github.com/org/repo.git),大概率是包的 dist 信息被缓存或镜像源劫持了。
- 删掉
vendor/和composer.lock,再跑composer install --no-cache - 确认
composer.json的repositories没配错:私有包必须用vcs类型,且url字段明确写git@开头(如"url": "git@gitlab.example.com:group/project.git") - 避免全局配置干扰:检查
composer config -g repo.packagist是否被设为国内镜像(有些镜像会强制转 HTTPS),临时禁用用composer config -g repo.packagist false
GitLab 私有组 + 子组嵌套导致 clone 失败
GitLab 支持多层子组(如 topgroup/subgroup/project),但它的 SSH URL 格式容易出错 —— 不是所有客户端都正确解析斜杠。Composer 调用 Git 时若遇到路径解析失败,会直接报 fatal: repository '...' not found。
- 务必使用 GitLab 页面上显示的「Clone with SSH」按钮里的完整 URL,不要手动拼接;常见正确格式是
git@gitlab.example.com:topgroup/subgroup/project.git(注意是冒号后跟全路径,不是斜杠) - 如果用的是自建 GitLab,确认
gitlab.yml中gitlab_shell_ssh_port和external_url配置一致,否则 SSH 连接可能被路由到错误端口 - 在
~/.ssh/config中为该域名显式指定IdentitiesOnly yes,防止多密钥环境下选错 key
CI 环境(GitHub Actions / GitLab CI)中 SSH 权限失效
本地好使,CI 上失败,基本是因为 SSH 密钥没注入、权限不对,或 known_hosts 缺失导致连接被拒绝。
- CI 中不要用
ssh-add加载密钥文件路径(如/home/runner/.ssh/id_rsa),而应把密钥内容作为 secret 注入,再用echo "${{ secrets.SSH_KEY }}" | ssh-add -(注意换行符保留) - 必须预填充
known_hosts:用ssh-keyscan -t rsa gitlab.example.com >> ~/.ssh/known_hosts(域名按实际替换),否则 SSH 会卡在交互式确认 - CI 容器默认 umask 较松,生成的
~/.ssh目录权限可能是 755,但 OpenSSH 要求是 700;加一步chmod 700 ~/.ssh
git clone 就失败,Composer 只是忠实转发这个失败。很多人反复改 composer.json 却不查 ssh -T 输出,结果绕半天。










