npm install timeout 的第一步不是删 node_modules,而是把失败阶段切开。npm registry 先返回 package metadata,metadata 里再给出 dist.tarball 下载地址;前者通了,后者仍可能因为 tarball、TLS、代理或 postinstall 下载失败。

这篇按 2026-05-22 比对过的 npm v11、pnpm 当前文档写。团队里最好用同一张表记录命令输出,不然本地、CI、公司网络三种结果会互相打架。

##确认卡在 metadata 还是 tarball?

npm registry 的安装链路不是一个请求。metadata 请求通常是 GET https://registry.npmjs.org/:package,安装用的精简响应里会包含 dist.tarballdist.shasumintegrity。所以 npm ping 成功,只能说明 registry 入口可达。

用一个公开小包做基准,再用出问题的包复测:

npm config get registry
pnpm config get registry
curl -I -L https://registry.npmjs.org/ --connect-timeout 10 -m 20
npm ping --registry=https://registry.npmjs.org/
npm view left-pad version --registry=https://registry.npmjs.org/

如果 npm view 正常,但 install 卡在某个包,继续查 tarball、postinstall 和私有 scope。遇到 ETIMEDOUTECONNRESETEAI_AGAINERR_SOCKET_TIMEOUT 时,把完整错误码贴进 issue,不要只写“网络失败”。

现象更可能的位置下一条命令
curl registry 超时DNS、出口网络、registry 可用性status.npmjs.org 和 DNS
npm view 正常,install 慢tarball、依赖体积、postinstallnpm install --verbose
只私有包失败scoped registry、token、权限npm config list -l
只 pnpm 失败store、lockfile、workspacepnpm install --reporter=append-only
只 CI 失败cache、secret、runner 网络打印 Node/pnpm/registry/lockfile

.npmrc 应该查哪几层?

npm 官方把配置来源拆成命令行 flag、环境变量和四类 npmrc:项目 .npmrc、用户 ~/.npmrc、全局 $PREFIX/etc/npmrc、内置 npmrc。更具体的配置会覆盖更宽的配置;官方示例也明确用户配置优先于全局配置。

排查时先打印真实生效值:

npm config list -l
npm config get registry
npm config get userconfig
npm config get globalconfig
pnpm config list

项目 .npmrc 只写团队一致的 registry、scope、重试上限,不写个人出口、个人 token、临时证书路径。私有包要显式 scoped,认证字段要按 registry host 限定:

registry=https://registry.npmjs.org/
@your-scope:registry=https://npm.pkg.github.com/
//npm.pkg.github.com/:_authToken=${NPM_TOKEN}
fetch-retries=2
fetch-timeout=300000

npm 文档要求 _auth_authTokenusername_passwordemailcafilecertfilekeyfile 这类字段按 URI fragment 绑定 registry,例如 //registry.npmjs.org/:_authToken=。没绑定 host 的 token 是团队安全事故,不是便利配置。

npm 和 pnpm 的重试参数怎么对齐?

npm 与 pnpm 的配置名相似,但默认 timeout 不一样。npm v11 文档里 fetch-timeout 默认是 300000ms;pnpm settings 里 fetchTimeout 默认是 60000ms。拿 pnpm 的 60 秒失败去判断 npm 的 5 分钟阈值,会误判。

维度npm v11pnpm settings排查意义
默认 registryhttps://registry.npmjs.org/可用 registries.default确认默认入口一致
重试次数fetch-retries=2fetchRetries=2不要无限重试
最小重试间隔fetch-retry-mintimeout=10000fetchRetryMintimeout=1000010 秒级抖动可被吸收
最大重试间隔fetch-retry-maxtimeout=60000fetchRetryMaxtimeout=60000超过 1 分钟仍失败要查链路
单次超时fetch-timeout=300000fetchTimeout=60000pnpm 默认更快失败
并发依赖 npm 实现networkConcurrency 默认 auto,16-64 上限区间并发过高会放大弱网络问题

临时拉长 timeout 可以帮助确认是不是慢链路,但别把它当长期方案。CI 里长期写 10 分钟 timeout,会让 token 过期、scope 写错、证书错误这类硬失败晚 10 分钟才暴露。

TLS、证书和公司代理怎么分开查?

TLS 证书问题常被写成“网络超时”,但错误码不同。看到 UNABLE_TO_VERIFY_LEAF_SIGNATURESELF_SIGNED_CERT_IN_CHAINCERT_HAS_EXPIRED,优先看证书链,不要先换 registry。

Node.js 官方 CLI 文档列出 NODE_EXTRA_CA_CERTS=file,可把额外 CA 证书加入 Node 信任链。npm 侧也有 strict-sslcafileca;pnpm settings 里 strictSsl 默认是 true,也支持 registry 级别证书字段。

# 临时验证证书文件是否解决问题
NODE_EXTRA_CA_CERTS=/path/to/corp-ca.pem npm install

# npm 侧查看 TLS 相关配置
npm config get strict-ssl
npm config get cafile
npm config get ca

strict-ssl=false 只能作为一次性定位手段,不能提交到项目 .npmrc。真正可维护的写法是把公司 CA 文件放到受控路径,在 CI secret 或 runner 镜像中注入,再记录证书更新人和到期日。

proxy 环境变量和 npm 配置谁优先?

npm 文档里的 proxyhttps-proxy 默认是 null,noproxy 默认读取 NO_PROXY 环境变量。pnpm settings 也有 httpsProxyhttpProxynoProxy。问题在于:项目 .npmrc、用户 .npmrc、shell 环境变量和 CI secret 可能同时存在。

把当前 shell 的代理变量打出来:

env | grep -i '_proxy'
npm config get proxy
npm config get https-proxy
npm config get noproxy
pnpm config get httpsProxy
pnpm config get noProxy

临时排查用命令前缀,别污染仓库:

HTTPS_PROXY=http://127.0.0.1:7890 pnpm install --reporter=append-only
NO_PROXY=localhost,127.0.0.1,.corp.example.com npm install

如果团队同时依赖 GitHub Packages、npm registry、Docker Registry、Cloudflare 和 GitHub Actions,网络路径抖动会在 install、deploy、Dashboard 三个环节轮流出现,把 proxy、token、证书、缓存写成团队规范,再判断是否是网络路径问题。

pnpm store、lockfile 和 workspace 怎么排?

pnpm 的失败不一定发生在网络层。store 损坏、workspace 递归构建、pnpm-lock.yaml 漂移,都能表现成“install 卡住”,看版本、store、锁文件,再决定要不要清缓存。

pnpm -v
pnpm store path
pnpm store status
pnpm install --frozen-lockfile --reporter=append-only

pnpm store status 用来检查 store 中包内容是否被改过,退出码 0 表示内容仍匹配原始解包状态。pnpm store prune 会删除当前机器上没有项目引用的包;如果后面又需要这些包,pnpm 会重新下载。

动作适合什么时候用不适合什么时候用
pnpm store path确认当前 store 位置不能证明缓存健康
pnpm store status怀疑 store 被改或损坏不能修网络超时
pnpm store prune磁盘太大、旧项目很多CI 每次都跑会放大下载量
删除 node_modules链接结构混乱、包升级后异常不能替代 lockfile 排查
删除 pnpm-lock.yaml只有确认要重新解依赖排障第一步绝对不该做

monorepo 还要看 workspace 的 postinstall。某个包下载二进制、编译原生模块、访问 GitHub Release,都会绕开 npm registry 这一层。

CI 和本地结果不一致时查什么?

pnpm install 文档明确:CI 中有 lockfile 时默认更严格,--frozen-lockfile 会阻止更新 pnpm-lock.yaml,manifest 和 lockfile 不匹配就失败。本地跑通,只能说明你电脑上的配置和缓存可用。

CI 里建议固定版本并打印证据:

node -v
corepack enable
corepack prepare [email protected] --activate
pnpm -v
npm config get registry
pnpm config get registry
pnpm store path
pnpm install --frozen-lockfile --prefer-offline --reporter=append-only

--prefer-offline 会跳过缓存数据的新鲜度检查,但缺包时仍会访问服务端;--offline 只从 store 安装,缺包就失败。两者不要混用成“网络修复方案”。

CI runbook 至少记录 6 个字段:Node 版本、包管理器版本、registry、lockfile hash、cache key、secret 名称。失败日志保留第一次 timeout 前后的 50 行,比连续重跑 5 次更有价值。

如果还没恢复,怎么交接给团队?

交接时不要写“npm 网络不行”。把失败缩到一个最小复现,包含包名、版本、registry、错误码、地区/runner、是否私有包、是否 postinstall。这样后端、运维和前端能对同一条证据说话。

如果 install、deploy 和 Dashboard 访问都在同一批团队成员身上反复失败,可以把核心后台和 CI 访问固定到同一套工作网络,用海外服务跑 GitHub Actions / Cloudflare 的稳定线路承载这些开发任务;它不替代 token、证书、缓存和 lockfile 规范,只减少排查变量。

要记录的字段示例谁来判断
包管理器与版本npm 11 / pnpm 10.12.1前端负责人
失败阶段metadata / tarball / postinstall前端 + DevOps
错误码ETIMEDOUT / EAI_AGAIN / CERT_HAS_EXPIREDDevOps
registry 与 scopenpmjs / GitHub Packages / 私有 Verdaccio包维护人
TLS / proxycafile、NODE_EXTRA_CA_CERTS、HTTPS_PROXY运维
CI 证据runner、cache key、secret 名称CI 维护人

如果 npm status 页显示 registry installation 服务异常,暂停大规模 CI 重跑。状态页正常时,就按这张表继续缩小范围:默认 registry、scoped registry、TLS、代理、pnpm store、CI lockfile。

相关阅读

容易被忽略的坑

strict-ssl=false 能不能长期用?

不能。它会降低 TLS 校验,最多用于一次性确认是不是公司证书链问题。长期方案是配置受控 CA:npm 用 cafile,Node 运行环境用 NODE_EXTRA_CA_CERTS,CI 镜像同步证书文件。

monorepo 的 postinstall 暗坑

某个包下载二进制、编译原生模块、访问 GitHub Release,都会绕开 npm registry 这一层。卡在 postinstall 时用 verbose 日志确认最后一个执行脚本,再决定是查代理、加证书还是锁版本。

scoped registry 和 token 的常见错配

@scope:registry=、token host、包名 scope 三者必须同时匹配。一个项目同时用 npmjs、GitHub Packages 和私有 registry 时,未限定 token 或写错 scope 会表现成 401、404 或超时。