最稳离线方案是artifact类型:仅读本地.tar.gz包,不联网、不fallback、不校验远程元数据;必须提前在联网机生成规范命名包(如monolog/monolog-2.13.0.tar.gz),统一放平级目录,再配置repositories.offline指向该路径,并全局禁用packagist.org。

用 artifact 类型最稳,别碰 packagist 替换
离线私有源的核心目标是让 composer install 一启动就不发任何 HTTP 请求——这只有 artifact 或 path 类型能做到,改 repo.packagist 地址纯属无效操作。artifact 是唯一推荐用于部署的离线类型:它只读本地 .tar.gz 文件,不探测、不 fallback、不校验远程元数据。
关键不在“怎么配”,而在“包怎么来”:
- 所有压缩包必须提前在联网机生成,命名严格为
vendor-name/package-name-version.tar.gz(例如monolog/monolog-2.13.0.tar.gz) - 每个包内必须含有效
composer.json,且name和version字段要与文件名完全一致 - 把这些包统一放在一个平级目录下(如
/opt/artifacts/),禁止嵌套子目录 - 离线机执行:
composer config repositories.offline '{"type":"artifact","url":"/opt/artifacts/"}'
path 类型只适合本地开发联调
path 类型跳过元数据请求,直接读取本地目录里的 composer.json,但它不是为离线部署设计的,只适用于你手头已有完整源码、且需要频繁改包的场景。
常见踩坑点:
- 路径必须是相对路径(如
./packages/my-utils),不能写绝对路径(/home/user/my-utils) -
composer.json中必须显式配置"options": {"symlink": false},否则离线机上软链失效 - 包的
name字段必须和require时写的名称完全一致(包括大小写) - 安装命令必须带
@dev:例如composer require my-utils/my-utils:@dev,漏掉就找不到
必须全局禁用 packagist,否则还是会连网
即使你已配置了本地仓库,composer install 仍可能触发 DNS 查询或报 Could not fetch https://repo.packagist.org/packages.json,根本原因是默认源的 fallback 行为没被真正切断。
三步清空 fallback 链路:
- 运行
composer config --global repo.packagist false(全局关掉) - 检查项目级配置:
composer config repo.packagist输出应为空,不能是composer https://packagist.org - 打开项目
composer.json,删掉repositories数组里任何指向https://packagist.org的条目 - 加
-v参数验证:composer install -v如果还出现Downloading https://repo.packagist.org/packages.json,说明某处没生效
别用 Satis 做纯离线源,它不适合无网络环境
Satis 是个构建工具,不是运行时服务。它需要联网抓包、生成静态文件,最终输出的是一个带 packages.json 和 dist/ 的 Web 可访问目录。如果你的离线机连 HTTP 服务都起不了(比如没装 Nginx/Apache),Satis 生成的东西根本没法用。
更现实的问题:
- Satis 构建过程本身依赖网络(拉 GitHub、Packagist),无法在目标离线机上运行
- 它生成的
packages.json里仍含dist.url字段,值是 HTTP 地址;离线机若没架 Web 服务,这个 URL 就是死链 - 除非你额外把 Satis 输出目录挂载为本地 Web 服务(如
php -S localhost:8000),否则和 artifact 比毫无优势
真正离线,就两条路:artifact(部署用)、path(开发用)。其他方案要么绕远,要么留后门。最容易被忽略的是 COMPOSER_DISABLE_NETWORK=1 这个环境变量——它不是可选开关,而是硬性前提;不设它,所有配置都可能被绕过。











