FAQ-15:在 Ubuntu 上安装 Node.js

预计阅读时间:16 分钟

📖 目录

问题速查表

问题解决章节
安装最新 LTS 版本(生产环境推荐)方式一:使用 NodeSource 官方仓库(推荐)
需要在多个版本之间切换方式二:使用 nvm 管理多版本(最灵活)
想一条命令快速安装方式三:使用 Snap(最简但功能受限)
E: Unable to locate package nodejs错误一:E: Unable to locate package nodejs
npm install 报 EACCES 权限错误错误二:npm install 报 EACCES 权限错误
nvm 安装后命令找不到错误三:nvm 安装后命令找不到
node-gyp 编译原生模块失败错误四:node-gyp 编译原生模块失败

Node.js 是构建后端服务、前端工具链和命令行工具的核心运行环境。无论是运行 JavaScript 项目、搭建 API 服务器,还是使用 npm/yarn 管理前端依赖包,都离不开它。Ubuntu 系统仓库自带的 Node.js 版本往往较旧,无法满足现代项目需求。本文提供三种安装方式,按推荐顺序排列,并附带版本管理、常见错误排查和最佳实践。

安装方法

方式一:使用 NodeSource 官方仓库(推荐)

NodeSource 维护了 Node.js 的 APT 仓库,支持安装所有主要版本,是生产环境最可靠的选择。

# 安装 NodeSource 仓库(以 Node.js 20 LTS 为例)
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -

# 安装 Node.js(包含 npm)
sudo apt install -y nodejs

# 验证安装
node --version
npm --version

如果需要安装 Node.js 18 LTS 或 22 LTS,只需将 setup_20.x 替换为 setup_18.xsetup_22.x。NodeSource 会自动添加对应的 APT 源和 GPG 密钥。

方式二:使用 nvm 管理多版本(最灵活)

nvm(Node Version Manager)是一个 bash 脚本,可以安装和管理多个 Node.js 版本,非常适合需要在不同项目间切换版本的开发者。

# 安装 nvm
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash

# 重新加载 shell 配置
source ~/.bashrc

# 安装最新 LTS 版本
nvm install --lts

# 安装指定版本
nvm install 18
nvm install 20
nvm install 22

# 切换版本
nvm use 18
nvm use 20

# 设置默认版本
nvm alias default 20

# 查看已安装版本
nvm list

# 查看可远程安装的版本
nvm ls-remote --lts

# 卸载某个版本
nvm uninstall 18

方式三:使用 Snap(最简但功能受限)

Snap 是 Ubuntu 自带的包管理器,安装一条命令搞定,但存在一些限制:无法全局安装 npm 包到自定义路径,某些原生模块编译可能失败。

# 安装 Node.js 20
sudo snap install node --classic --channel=20

# 验证
node --version
npm --version

注意:Snap 版本的 Node.js 运行在沙箱环境中,对文件系统访问有限制。如果项目需要访问项目目录之外的文件,建议使用前两种方式。

版本管理

在实际开发中,你可能同时维护多个项目,而不同项目要求不同的 Node.js 版本。以下是几种版本管理策略:

# 使用 nvm 在项目目录切换版本
cd ~/project-a
nvm use 18

cd ~/project-b
nvm use 20

# 在项目根目录创建 .nvmrc 文件,指定项目版本
echo "20" > .nvmrc

# 以后在该项目目录下执行以下命令即可自动切换到指定版本
nvm use

使用 NodeSource 时,如果需要升级到新版本,重新运行安装脚本即可覆盖安装,不会产生冲突。而使用 Snap 时,可以通过 --channel 参数指定版本频道。

# nvm 常用命令速查
nvm ls-remote --lts        # 查看远端可用 LTS 版本列表
nvm install 20             # 安装指定版本
nvm use 20                 # 当前 shell 切换版本
nvm alias default 20       # 设置默认版本(新终端生效)
nvm current                # 查看当前版本
nvm ls                     # 列出本机已装版本
nvm uninstall 20           # 卸载指定版本

# 卸载 NodeSource 安装的 Node.js
sudo apt remove -y nodejs
sudo rm -f /etc/apt/sources.list.d/nodesource.list
sudo apt update

nvm 与 NodeSource 混用注意事项:两者会同时修改 PATH 顺序,谁先 export 谁生效。如果安装了 nvm 后 `node` 仍指向 /usr/bin/node,说明 nvm 的初始化脚本未优先执行——把 nvm 的 export 语句放在 ~/.bashrc 靠前的位置,或在当前 shell 执行 nvm use --delete-prefix 20 修复。

真实案例

案例 A:cron 任务报 node: command not found,但手动执行正常

用户通过 NodeSource 安装 Node.js 后,在 crontab 中定时运行一个 Node 脚本,邮件报错 node: command not found,但手动在终端执行同一命令却完全正常。

# cron 邮件中的报错
/bin/sh: 1: node: not found

# 排查:cron 的最小 PATH 不含 /usr/bin 之外的自定义路径
# 手动环境
which node    # /usr/bin/node(NodeSource 安装时会链接到 /usr/bin)

# cron 环境 PATH 只有 /usr/bin:/bin,如果 node 不在其中就会失败
crontab -e
# 修复:在 crontab 顶部显式声明 PATH
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
*/5 * * * * node /home/user/scripts/check.js

根因:cron 运行时的最小环境 PATH 与登录 shell 不同;修复:在 crontab 中显式声明 PATH;验证:手动执行 crontab -l 后等待下一周期,观察任务输出正常。

案例 B:npm install -g 报 EACCES 权限错误

# 报错
npm ERR! code EACCES
npm ERR! syscall mkdir
npm ERR! path /usr/lib/node_modules/cowsay
npm ERR! Error: EACCES: permission denied, mkdir '/usr/lib/node_modules/cowsay'

# 排查:npm 默认全局目录是 /usr/lib/node_modules,需要 root 权限
npm config get prefix    # /usr/local(或 /usr/lib)

# 修复:将全局目录改到用户目录(参考"错误二"方案)
mkdir -p ~/.npm-global
npm config set prefix ~/.npm-global
echo 'export PATH=$PATH:~/.npm-global/bin' >> ~/.bashrc
source ~/.bashrc
npm install -g cowsay    # 成功,无需 sudo

根因:全局 npm 包目录无写权限;修复:改 prefix 到用户目录(比 sudo npm 更安全,避免第三方包以 root 运行);验证cowsay hello 直接可用,npm root -g 指向 ~/.npm-global/lib/node_modules

案例 C:nvm install 卡在 downloading 进度条

用户通过 nvm 安装 Node.js 20 时,进度条停在 50% 不动,最终超时报错 nvm install 20: download timed out

# 报错
$ nvm install 20
Downloading and installing node 20.x.x...
# 进度条卡住,等待超时报错
# nvm install 20: download timed out after 300s

# 排查:nvm 默认从 nodejs.org 下载,国内可能被墙
# 检查网络
curl -I https://nodejs.org/dist/v20.11.0/
# 超时或极慢

# 修复:设置 NVM_NODEJS_ORG_MIRROR 使用国内镜像
echo 'export NVM_NODEJS_ORG_MIRROR=https://npmmirror.com/mirrors/node' >> ~/.bashrc
source ~/.bashrc

# 重试安装
nvm install 20
# 秒级完成

根因:nodejs.org 在国内网络环境下访问不稳定;修复:设置 NVM_NODEJS_ORG_MIRROR 环境变量指向国内镜像;验证nvm install 20 正常完成,node --version 输出 v20.x.x

案例 D:npm ci 报 integrity check failed

CI 环境执行 npm ci 报错 npm ERR! code EINTEGRITY,但本地正常。

# 报错
npm ERR! code EINTEGRITY
npm ERR! sha512-xxxx mismatch for registry.npmjs.org/lodash/-/lodash-4.17.21.tgz

# 排查:package-lock.json 与 npm registry 的 sha512 不一致
# 可能原因:本地 node_modules 缓存污染,或 package-lock 被手动修改

# 修复:删除锁文件和缓存,重新生成
rm -rf node_modules package-lock.json
npm cache clean --force
npm install   # 重新生成 package-lock.json

# 验证
npm ci        # 成功

根因:package-lock.json 中的完整性校验值与 registry 不匹配(常因缓存污染或锁文件损坏);修复:清除缓存重新生成锁文件;预防:CI 中不要手动修改 lock 文件,提交 package-lock.json 到 Git。

案例 E:npm update 报 ERR! code ERESOLVE unable to resolve dependency tree

执行 npm updatenpm install 时遇到依赖冲突无法解决。

# 报错
npm ERR! code ERESOLVE
npm ERR! ERESOLVE could not resolve
npm ERR! While resolving: myapp@1.0.0
npm ERR! Found: react@18.2.0
npm ERR! node_modules/react
npm ERR!   react@"^18.2.0" from the root project
npm ERR! Could not resolve dependency:
npm ERR! peer react@"^17.0.0" from some-legacy-pkg@2.1.0

# 排查:some-legacy-pkg 要求 react 17,但项目用 react 18

# 方法一:强制安装(跳过 peer 依赖检查)
npm install --legacy-peer-deps

# 方法二:查找兼容版本
npm info some-legacy-pkg versions    # 看有没有支持 react 18 的新版本
npm install some-legacy-pkg@latest   # 升级到最新版

根因:npm 7+ 默认严格检查 peer 依赖冲突,旧包的 peer 依赖声明未更新;修复:优先升级依赖到兼容版本,--legacy-peer-deps 作为临时方案;预防:CI 中固定 --legacy-peer-deps 或及时更新上游依赖。

版本管理详解

nvm 完整使用指南

nvm 是 Node.js 版本管理的事实标准,以下是进阶用法:

# 安装特定架构版本(适用于 ARM 服务器)
nvm install 20 --arch=arm64

# 安装并立即使用
nvm install 20 --default   # 安装并设为默认

# 从远程安装指定子版本
nvm install 20.11.0

# 查看本机已安装版本(带当前激活标记)
nvm ls
#    v18.19.0
# -> v20.11.0
#     v22.12.0

# 列出远程可用 LTS 版本(含 codename)
nvm ls-remote --lts | grep "v20\."
#   v20.11.0    2024-01-09
#   v20.11.1    2024-02-14
#   v20.12.0    2024-04-09

# 在当前 shell 切换版本(不新建 shell)
nvm use 20

# 切换版本时自动安装(如果本地没有)
nvm use 22    # 如果 22 未安装,报错
nvm install 22 && nvm use 22   # 先装再切换

# 为某个版本创建别名
nvm alias lts/iron 20.11.0   # 铁 = Node 20 LTS 的 codename
nvm use lts/iron

# 卸载版本前确认没有进程在用
lsof +D $(nvm which 18) 2>/dev/null && echo "有进程在用,先 kill" || nvm uninstall 18

# .nvmrc 支持更多语法
echo "20" > .nvmrc           # 最新 20.x
echo "lts/iron" > .nvmrc     # 指定 LTS codename
echo "20.11.0" > .nvmrc      # 精确版本

# 自动加载 nvm(在 shell 初始化脚本中)
# ~/.bashrc 或 ~/.zshrc 末尾
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"

n vs nvm 对比

另一个流行的版本管理工具是 n,它是一个 Node.js 脚本(不像 nvm 是 bash 脚本),安装更简单:

# 安装 n
npm install -g n

# 使用 n 安装版本
n 20              # 安装最新 20.x
n lts             # 安装最新 LTS
n latest          # 安装最新版
n 18.19.0         # 精确版本

# 查看已安装版本
n ls

# 切换版本(修改 /usr/local/bin/node 符号链接)
n                  # 交互式选择

# 注意:n 安装到系统目录,需要 sudo
sudo n 20

# nvm 与 n 的区别
# nvm: 用户级,不需 sudo,隔离性好,适合多项目多版本
# n:   系统级,需 sudo,更简单,适合单版本快速切换

项目级版本锁定最佳实践

# 方案一:.nvmrc 文件(团队协作标准)
echo "20.11.0" > .nvmrc
# 团队成员进入项目目录后执行 nvm use 自动切换

# 方案二:package.json engines 字段
cat package.json | jq '.engines'
# {
#   "node": ">=20.0.0 <23.0.0",
#   "npm": ">=10.0.0"
# }
# npm install 时会检查 Node 版本,不满足则报错

# 方案三:volta(更现代的方案)
curl https://get.volta.sh | bash
volta pin node@20.11.0   # 写入 package.json
volta pin npm@10.5.0

# 方案四:GitHub Actions 中使用
cat > .github/workflows/ci.yml << 'EOF'
steps:
  - uses: actions/setup-node@v4
    with:
      node-version-file: '.nvmrc'
EOF

故障排查决策树

当 Node.js 环境出现问题时,按以下流程逐步排查:

问题:node 或 npm 命令找不到
├── which node 有输出?
│   ├── 是 → PATH 没问题,检查别名:alias | grep node
│   └── 否 → 查找安装位置:
│       ├── ls /usr/bin/node* /usr/local/bin/node* 2>/dev/null
│       │   ├── 有 → 加入 PATH:export PATH=$PATH:/usr/local/bin
│       │   └── 无 → Node.js 未安装,选择安装方式
│       └── nvm 环境?
│           ├── nvm ls 查看已安装版本
│           ├── nvm use  激活
│           └── source ~/.bashrc 重新加载 nvm 初始化

问题:npm install 报错
├── EACCES 权限错误?
│   ├── 是 → 修改 prefix:mkdir ~/.npm-global && npm config set prefix ~/.npm-global
│   └── 否 → 继续排查
├── ERESOLVE 依赖冲突?
│   ├── 是 → npm install --legacy-peer-deps 或升级冲突依赖
│   └── 否 → 继续排查
├── EINTEGRITY 校验失败?
│   ├── 是 → rm -rf node_modules package-lock.json && npm cache clean --force && npm install
│   └── 否 → 继续排查
├── ENOENT 文件不存在?
│   ├── 是 → 检查 package.json 是否存在,依赖名是否正确
│   └── 否 → 检查网络:curl -I https://registry.npmjs.org/

问题:node-gyp 编译失败
├── 安装 build-essential:sudo apt install -y build-essential python3
├── 检查 Node 版本兼容性:某些原生模块不支持最新 Node
├── 使用 --build-from-source 重试:npm install sharp --build-from-source
└── 考虑使用预编译版本:npm install @img/sharp-linux-x64

问题:Node 版本不兼容
├── 报错:This version of Node.js is not supported
│   ├── 查看项目要求:cat package.json | grep engines
│   ├── 切换版本:nvm install  && nvm use 
│   └── 升级 Node:nvm install --lts && nvm use --lts

问题:npm 全局包路径问题
├── npm root -g 检查全局目录
├── npm config get prefix 检查前缀
├── 确认 PATH 包含 $(npm prefix -g)/bin
└── 修复:echo 'export PATH="$PATH:$(npm prefix -g)/bin"' >> ~/.bashrc

生产环境配置建议

  • 使用 LTS 版本 + NodeSource 仓库:生产环境推荐 Node.js 20 LTS(或 18 LTS),通过 NodeSource 安装,配合 apt-mark hold 锁定版本防止意外升级。
  • 进程管理使用 PM2 或 systemd:不要直接 node app.js 启动服务。PM2 提供集群模式、日志管理、自动重启;systemd 提供原生系统级管理。
  • 设置NODE_ENV=production:在生产环境中显式声明环境变量,许多库会据此优化性能(如 Express 关闭详细错误栈、React 关闭开发检查)。
  • 限制内存使用:Node.js 默认堆内存上限约 1.5GB(64位系统),大数据量场景需要调整:node --max-old-space-size=4096 app.js
  • 监控日志和指标:生产环境应配置日志轮转(logrotate)、应用指标监控(Prometheus + Grafana),及时发现内存泄漏、事件循环延迟等问题。
  • 安全扫描依赖:定期运行 npm audit 检查已知漏洞,配合 npm audit fix 自动修复,CI 中加入 audit 步骤作为安全卡点。
  • Docker 部署建议:多阶段构建减小镜像体积(builder 阶段安装依赖,runtime 阶段只复制 node_modules 和应用代码),使用 alpine 基础镜像。

案例 F:npm run script 报错找不到命令

项目 package.json 中定义了 "dev": "vite",执行 npm run devvite: command not found

# 报错
npm ERR! Missing script: "dev"
# 或
sh: 1: vite: not found

# 排查:vite 是否安装在 node_modules/.bin
ls node_modules/.bin/vite
# 文件不存在

# 原因:node_modules 未安装或不完整
# 解决:重新安装依赖
rm -rf node_modules package-lock.json
npm install

# 验证
ls node_modules/.bin/vite    # 存在
npm run dev                  # 正常启动

# 如果是 monorepo 项目
# 确认在正确的 package 目录下执行
cd packages/web && npm install && npm run dev

根因node_modules 未安装或安装不完整,导致 node_modules/.bin 中缺少可执行文件;修复:清理后重新 npm install

案例 G:Node.js 版本与 npm 版本不兼容

升级 Node.js 后 npm 报错 npm WARN npm does not support Node.js,npm 版本过旧。

# 报错
npm WARN npm does not support Node.js v22.0.0
npm WARN You should probably upgrade to a newer version of node as we
npm WARN can no longer makeguarantees that npm will work with this version

# 排查
node --version    # v22.0.0
npm --version     # 9.6.7(配套 Node 20 的 npm,不兼容 22)

# 原因:Node.js 22 自带的 npm 需要重新安装或更新
# 修复:升级 npm
npm install -g npm@latest

# 或使用 Node.js 自带的 corepack
corepack enable
corepack prepare npm@latest --activate

# 验证
npm --version    # 10.x.x(兼容 Node 22)

根因:Node.js 版本升级后 npm 未同步更新,版本不兼容;修复:执行 npm install -g npm@latest 升级 npm;预防:使用 nvm 管理版本时,每次安装新 Node 版本后验证 npm 版本。

案例 H:nvm 切换版本后全局包丢失

用户切换 Node 版本后,之前全局安装的工具(如 typescript)报 command not found。

# 现象
nvm use 20
tsc --version    # /usr/bin/env: 'node': No such file or directory
# 或
tsc: command not found

# 原因:nvm 每个版本的全局包是隔离的
# Node 18 下全局安装的 tsc 在 Node 20 下不可用

# 修复:在新版本下重新安装
nvm use 20
npm install -g typescript

# 批量迁移全局包
nvm use 18
npm list -g --depth=0    # 查看全局包列表
nvm use 20
npm install -g $(npm list -g --depth=0 --parseable | tail -n +2 | xargs -I{} basename {})

# 或使用 nvm 的 alias 功能
nvm alias default 20
nvm use default

根因:nvm 每个版本的全局包独立存储,切换版本后原全局包不可用;修复:在新版本下重新安装全局包,或使用脚本批量迁移。

卸载与切换

# 卸载 NodeSource 的 Node.js(保留配置文件可用 --purge)
sudo apt purge -y nodejs
rm -f /etc/apt/sources.list.d/nodesource.list

# 卸载 Snap 版本
sudo snap remove node

# nvm 卸载指定版本
nvm uninstall 18

# 完全移除 nvm(删除目录和配置行)
rm -rf ~/.nvm
# 并手动删除 ~/.bashrc 中的 nvm 初始化行

常见错误

错误一:E: Unable to locate package nodejs

这通常是因为 NodeSource 仓库没有正确添加。请确认:

# 检查仓库是否已添加
ls /etc/apt/sources.list.d/nodesource.list

# 如果不存在,重新执行安装脚本
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -

# 更新包索引后重试
sudo apt update
sudo apt install -y nodejs

错误二:npm install 报 EACCES 权限错误

不要用 sudo 运行 npm install。正确做法是修改 npm 的全局安装路径:

# 创建全局目录
mkdir -p ~/.npm-global

# 配置 npm 使用该目录
npm config set prefix ~/.npm-global

# 将路径加入 PATH
echo 'export PATH=$PATH:~/.npm-global/bin' >> ~/.bashrc
source ~/.bashrc

# 之后就可以不带 sudo 安装全局包了
npm install -g typescript

错误三:nvm 安装后命令找不到

nvm 修改的是 shell 的环境变量,安装后需要重新加载配置文件:

source ~/.bashrc
# 或者重新打开终端窗口

# 确认 nvm 已加载
nvm --version

错误四:node-gyp 编译原生模块失败

某些 npm 包(如 sharpbcrypt)需要编译原生 C++ 代码,缺少构建工具会报错:

# 安装编译工具链和 Python
sudo apt install -y build-essential python3

# 之后重新安装有问题的包
npm install sharp

验证安装

# 检查版本
node --version
npm --version

# 运行一个简单的 HTTP 服务器测试
node -e "require('http').createServer((req, res) => { res.end('Hello from Node.js ' + process.version); }).listen(3000)" &

# 等待服务器启动后测试
sleep 1
curl http://localhost:3000

# 停止测试服务器
kill %1

# 测试 npm 全局安装功能
npm install -g cowsay
cowsay "Node.js is working!"

最佳实践

  • 优先选择 LTS 版本:生产环境应使用 LTS(长期支持)版本,如 18.x 或 20.x,它们有更长的安全更新周期。
  • 不要用 sudo 运行 npm:全局安装的包应该放在用户目录下,避免权限污染和安全风险。
  • 使用 .nvmrc 锁定版本:团队协作时在项目根目录放置 .nvmrc 文件,确保所有开发者使用相同版本。
  • 定期更新 npm:npm 本身会频繁发布安全更新,运行 npm install -g npm@latest 保持最新。
  • 考虑使用 corepack:Node.js 16+ 内置 corepack,可以管理 yarn/pnpm 的版本,运行 corepack enable 即可激活。
  • 固定版本并提交锁文件:项目提交 package-lock.json,CI 与生产用 npm ci(而非 npm install)保证依赖一致。
  • CI 中指定 Node 版本:GitHub Actions 用 actions/setup-nodenode-version-file: .nvmrc 自动读取项目版本,避免"本地能跑 CI 挂了"。
  • 非交互环境验证:安装后执行 command -v node 确认 PATH 生效,避免脚本(cron/systemd)中找不到命令。

延伸阅读

案例 I:npm cache 损坏导致安装失败

现象:执行 npm install 时反复报 EINTEGRITYECONNRESET 错误,清除 node_modules 后仍然失败:

# 报错
npm ERR! code EINTEGRITY
npm ERR! sha512-xxxx mismatch for registry.npmjs.org/lodash/-/lodash-4.17.21.tgz

# 清除缓存后重试
rm -rf node_modules package-lock.json
npm cache clean --force
npm install
# 仍然报同样的错误

排查过程

# 检查 npm 缓存目录
$ ls ~/.npm/_cacache/
index-v5/  content-v2/

# 完整清除缓存
$ rm -rf ~/.npm/_cacache
$ npm cache clean --force

# 验证 registry 连接
$ npm config get registry
https://registry.npmjs.org/

$ curl -I https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz
HTTP/2 200

根因:npm 缓存中的完整性校验文件损坏,导致每次安装都验证失败。即使清除 node_modules,缓存中的损坏数据仍然被使用。

修复

# 完整清除 npm 缓存
rm -rf ~/.npm/_cacache
npm cache clean --force

# 重新安装
npm install

# 验证
npm install lodash   # 成功

预防:CI 环境中定期执行 npm cache clean --force。遇到完整性错误时优先清除缓存而非反复重试。

↑ 回到顶部