在 DigitalOcean Droplet 上部署 Node.js 指南 2026
将 Node.js 应用部署到生产环境不仅仅是运行 npm start — 你需要一个系统在服务器重启时也能持续运行你的应用,需要反向代理来管理流量,需要 HTTPS 来保护传输中的数据。本指南将逐步引导你在 DigitalOcean Droplet 上完成每一步,从准备实例、通过 nvm 安装 Node.js、使用 PM2 运行应用、配置 Nginx 作为反向代理,到使用 Let's Encrypt 设置免费 SSL — 包括每条真实的命令。
目录
准备 Droplet 并安装 Node.js (nvm)
在安装任何东西之前,你需要选择与工作负载匹配的 Droplet 大小。对于小到中型 Node.js API 或网络应用,DigitalOcean 的基础 Droplet(1 GiB 内存 / 1 vCPU / 25 GB SSD,每月 $6)足以用于测试和低流量应用。对于需要构建过程或处理许多并发连接的生产工作负载,应升级到 2 GiB 内存 / 2 vCPU / 60 GB SSD(每月 $18),这样 PM2 就可以在两个核心上以集群模式运行。如果你的主要用户在泰国,选择 sgp1(新加坡)区域以获得最低延迟,blr1(班加罗尔)是下一个最好的选择。创建 Droplet 后(选择 Ubuntu LTS 映像),SSH 连接并首先更新系统,使用 apt update && apt upgrade -y。使用 adduser deploy && usermod -aG sudo deploy 创建非 root 用户而不是以 root 运行所有内容,然后启用 DigitalOcean 的云防火墙(完全免费),它让你限制访问仅限 SSH (22)、HTTP (80) 和 HTTPS (443)。接下来,通过 nvm (Node 版本管理器) 而不是直接使用 apt 安装 Node.js,因为 nvm 让你可以轻松切换版本并始终拥有最新版本。运行 curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash,然后使用 source ~/.bashrc 加载更新的 shell 配置文件。使用 nvm install --lts 安装最新 LTS 版本,使用 nvm use --lts 将其设置为默认值。使用 node -v 和 npm -v 验证安装。如果你的机器只有 512 MiB 或 1 GiB 内存,在大型依赖集上运行 npm install 可能会因内存耗尽而杀死进程。为了安全起见,请预先添加 1-2 GB 的交换文件。
- 基础 Droplet 1 GiB 内存(每月 $6)足以用于测试;建议生产环境使用 2 GiB/2 vCPU(每月 $18)以启用集群模式
- 为泰国用户选择 sgp1(新加坡)区域获得最低延迟,或选择 blr1(班加罗尔)作为备选
- 使用 adduser + usermod -aG sudo 创建非 root 用户,然后启用免费云防火墙限制端口为 22/80/443
使用 PM2 运行 Node.js 应用
根据我们的实测——PM2 是一个 Node.js 进程管理器,可以让你的应用在后台持续运行,如果崩溃会自动重启,并支持在单台机器上并行运行多个实例。使用 npm install -g pm2 全局安装它。使用 git clone https://github.com/user/myapp.git && cd myapp 克隆你的项目仓库到机器上,然后使用 npm install --production 安装依赖。如果你的项目需要构建(如 TypeScript 或前端打包),先运行 npm run build。准备好后,使用 pm2 start app.js --name myapp 启动你的应用 — PM2 将立即在后台运行它并为其命名以便后续管理。使用 pm2 list 检查所有进程的状态,使用 pm2 logs myapp 查看实时日志,或使用 pm2 monit 打开显示 CPU 和内存的仪表板。对于有 2 个或更多 vCPU 的 Droplet,你可以使用集群模式来充分利用每个核心,使用 pm2 start app.js -i max --name myapp,它会自动为每个 CPU 核心创建一个 worker,并以轮询方式跨它们进行负载均衡。对于需要环境变量或特殊参数的更复杂项目,使用生态系统文件而不是每次都输入长命令。创建 ecosystem.config.js 文件,其中包含名称、脚本、实例、环境和其他设置,然后使用 pm2 start ecosystem.config.js 运行。这种方法使重新部署和迁移到新 Droplet 更加一致,因为配置存在于可以提交到你的仓库的单个文件中。
- 使用 npm install -g pm2 安装 PM2,然后使用 pm2 start app.js --name myapp 启动应用
- 使用 pm2 list 检查状态,使用 pm2 logs 查看实时日志,使用 pm2 monit 打开仪表板
- 拥有 2+ vCPU 核心的 Droplet 可以使用 pm2 start app.js -i max 的集群模式来利用每个核心
- 对于具有环境变量或特殊参数的复杂项目,使用 ecosystem.config.js 而不是键入命令
- 需要 TypeScript 或前端构建的项目必须在 pm2 start 之前运行 npm run build
将 Nginx 配置为反向代理
你的 Node.js 应用运行在 PM2 下通常绑定到内部端口如 3000,这不应该直接暴露给用户。将 Nginx 设置为反向代理让你可以在标准端口 80/443 上提供网站,支持在同一 Droplet 上使用不同域名的多个应用,并简化下一步的 SSL 设置。使用 apt install nginx -y 安装 Nginx。在 /etc/nginx/sites-available/myapp 创建一个新的配置文件,其中包含如下的 server 块:server { listen 80; server_name yourdomain.com; location / { proxy_pass http://localhost:3000; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } } 如果你的应用使用 WebSocket(如 Socket.io),Upgrade 和 Connection 头部是至关重要的,因为没有它们实时连接会断开或回退到轮询。通过使用 ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/ 创建符号链接来启用配置。在重新加载前务必使用 nginx -t 测试语法 — 如果通过且无错误,使用 systemctl reload nginx 重新加载服务。别忘了使用 ufw allow 'Nginx Full' 打开防火墙让 HTTP/HTTPS 通过。要在同一 Droplet 上托管多个应用,通过为每个域创建单独的配置文件并将每个 proxy_pass 指向你的 PM2 实例使用的不同端口来重复此过程。
- 使用 apt install nginx 安装 Nginx,然后在 /etc/nginx/sites-available/ 中创建 server 块
- 将 proxy_pass 设置为你的 Node.js 应用监听的内部端口,例如 http://localhost:3000
- 如果你的应用使用 WebSocket,包括 Upgrade 和 Connection 头部,否则实时连接会断开
- 在每次重新加载前使用 nginx -t 测试语法,以在配置错误导致网站崩溃前捕获它们
- 使用 ufw allow 'Nginx Full' 打开防火墙以允许端口 80 和 443 上的流量
使用 Let's Encrypt 设置 SSL
在 Nginx 通过 HTTP 正确代理后,下一步是使用 Let's Encrypt 启用 HTTPS,它颁发免费证书并自动续期。该工具是 Certbot,一个 Nginx 特定的插件。使用 apt install certbot python3-certbot-nginx -y 安装它。在运行任何东西之前,确保你的域名的 DNS A 记录指向你的 Droplet IP — 如果没有,Certbot 将无法验证它。一旦 DNS 就绪,运行 certbot --nginx -d yourdomain.com -d www.yourdomain.com。Certbot 会要求提供电子邮件用于过期警告,然后自动修改你的 Nginx 配置,添加一个带有证书路径的 listen 443 块和从 HTTP 到 HTTPS 的重定向。Let's Encrypt 证书有效期为 90 天,但 Certbot 安装了一个 systemd 计时器或 cron 作业,每天检查两次,并在过期接近时自动续期(通常剩余不足 30 天)。通过运行 certbot renew --dry-run 测试自动续期是否有效,无需等待 — 如果无错误完成,自动续期已准备好。一个关键细节:即使启用了 HTTPS 后,端口 80 也必须保持打开状态,因为 Let's Encrypt 在续期时的 HTTP-01 质询验证需要它。如果你的防火墙在设置 HTTPS 后阻止了端口 80,自动续期将静默失败,你直到证书真正过期时才会意识到这一点。
- 在请求证书前使用 apt install certbot python3-certbot-nginx 安装 Certbot
- 在运行 certbot --nginx 前确保你的域名 DNS A 记录指向 Droplet IP
- certbot --nginx -d yourdomain.com -d www.yourdomain.com 发行证书并自动更新 Nginx 配置
- 证书有效期 90 天并通过 systemd 计时器自动续期 — 使用 certbot renew --dry-run 测试
自动重启和日志管理
用户经常问到的一点是:许多人在部署后忘记的一个步骤是确保 PM2 在 Droplet 重启时重启你的应用,这可能因维护或电源问题随时发生。在你第一次 pm2 start 后,始终运行两个配套命令:pm2 startup 生成一个 systemd 服务指令(复制并使用 sudo 运行输出),然后是 pm2 save 记录当前进程列表。当 Droplet 重启时,systemd 会调用 PM2,它加载保存的进程列表并自动重启所有内容而无需手动干预。PM2 默认将应用 stdout/stderr 存储在 ~/.pm2/logs/ 中,但如果不管理,日志文件会增长并消耗你的 SSD,尤其是在较小的 Droplet 上。使用 pm2 install pm2-logrotate 安装 pm2-logrotate 模块以按大小或年龄自动轮换日志,将旧文件压缩为 .gz。你可以配置大小限制和保留深度。PM2 默认自动重启崩溃的进程,但如果一个进程重启得太频繁(1 分钟内超过 15 次),PM2 会进入"errored"状态以防止无限重启循环 — 这表示你需要调查日志中的实际错误,而不是让它继续失败。
- 在每次部署后运行 pm2 startup 然后 pm2 save 以在 Droplet 重启时自动启动你的应用
- PM2 默认将日志存储在 ~/.pm2/logs/;如果不管理,它们会填满小 Droplet 上的 SSD
- 使用 pm2 install pm2-logrotate 自动轮换和压缩日志
- PM2 自动重启崩溃的进程,但如果重启次数超过每分钟 15 次,它会进入'errored'状态以防止循环
何时使用这种方法(用例)
如本所述,在你自己的 Droplet 上部署 Node.js 在以下情况下是理想的:你需要对基础设施的完全控制,如详细定制 Nginx 配置、在一台机器上运行多个应用或服务、管理后台工作者或 cron 作业及网络服务器、或处理大量需要持久连接的 WebSocket 连接。有时自托管提供比托管平台更灵活。另一个原因是成本:基础 Droplet 从每月 $6 开始,如果你明智地分配资源,可以同时运行许多应用,不像 App Platform 按容器实例计费(例如,共享 1vCPU/1GiB 容器每个应用每月 $10)。反之,如果你的团队缺乏 DevOps 专业知识,不想自己管理 OS 更新、安全补丁、SSL 续期或扩展,DigitalOcean App Platform(一个托管 PaaS)更好。你直接从 Git 部署,其他一切 — SSL、扩展、零停机更新 — 都是自动的,虽然你以灵活性换取了简单性并失去了 OS 级别的控制。简而言之,Droplet + PM2 + Nginx 适合拥有现有 DevOps 知识并需要自定义基础设施或多服务部署以节省每个应用成本的团队,例如中型 SaaS 后端、移动应用 API、Webhook 接收器或实时服务如聊天或通知。优先考虑市场速度而不是基础设施控制的团队应该考虑 App Platform 代替。
- 适合需要自定义 Nginx 配置、在一个 Droplet 上运行多个应用/服务或 cron/后台工作者的团队
- 完美适合需要重型 WebSocket 持久连接(如聊天或通知服务)的应用
- 长期成本通常在单个 Droplet 上运行多个应用时更低,而不是按容器付费
- 如果你没有时间自己管理服务器或不想处理补丁和扩展,选择 App Platform 代替
常见错误及其修复方法
最常见的错误是在部署后忘记 pm2 startup && pm2 save,所以你的应用在 Droplet 重启时会消失而没有任何警告。用户通常只在几小时后网站宕机时才注意到。通过在每次 Droplet 维护或重启后检查 pm2 list 来保护自己,确认进程仍在运行。第二个常见问题是 Nginx 不转发 Upgrade 和 Connection 头部,导致 Socket.io 等 WebSocket 应用无法连接或频繁断开,尽管本地测试工作正常。检查浏览器控制台是否有 WebSocket 连接错误。通过验证你的 Nginx server 块包括 proxy_set_header Upgrade $http_upgrade; 和 proxy_set_header Connection 'upgrade'; 来修复。第三个问题是在试图在已被遗留进程使用的端口上运行新应用时出现 EADDRINUSE 错误。使用 lsof -i :3000 找到什么在占用端口,然后杀死它或在重新启动前使用 pm2 delete myapp。第四个问题是在新安装后忘记为 Nginx 打开防火墙,导致你的网站虽然服务在运行但从外部无法访问。使用 ufw status 检查是否有允许 Nginx Full 或端口 80/443 的规则。最后,当你的防火墙或 Nginx 配置在启用 HTTPS 后阻止端口 80 时,证书自动续期会静默失败,破坏 Let's Encrypt HTTP-01 质询。即使 HTTPS 已启用,也要始终保持端口 80 打开,并定期检查 certbot certificates 以提前查看过期日期。
- 忘记 pm2 startup && pm2 save 会导致 Droplet 重启后应用死亡 — 定期检查 pm2 list
- Nginx 缺少 Upgrade/Connection 头部会破坏 WebSocket — 将它们添加到代理配置
- EADDRINUSE 错误表示端口冲突 — 使用 lsof -i :PORT 检查并在重启前运行 pm2 delete
最佳实践
对于安全,禁用 SSH 密码登录并仅使用 SSH 密钥,加上创建非 root 用户用于日常工作并仅在必要时使用 sudo。启用 DigitalOcean 的免费云防火墙以及机器上的 ufw 来限制打开的端口仅限所需的。对于配置,永远不要在代码中硬编码敏感值如数据库密码或 API 密钥 — 通过带有 dotenv 之类库的 .env 文件使用环境变量,并始终将 .env 添加到 .gitignore 以防止意外泄露到你的仓库。对于监控,DigitalOcean 为每个账户提供免费监控和 1 个正常运行时间检查 — 启用它们以实时监控 CPU、内存和磁盘使用情况,并在网站宕机时在用户发现前获得警报。对于备份,在进行重大更改(如升级主要 Node.js 版本或更改数据库架构)前创建 Droplet 快照 — 快照成本仅为每月每 GiB $0.06,比从失败的更改中恢复便宜得多。对于性能,在任何拥有 2+ vCPU 核心的 Droplet 上使用 PM2 集群模式来利用所有核心,而不是让 Node.js 单线程。对于网络,如果你需要一个用于与外部系统白名单的静态 IP,附加到活跃 Droplet 的保留 IP 是免费的,但如果不附加,它成本为每月 $5 — 始终将其绑定到你的生产 Droplet 以避免浪费。
- 禁用 SSH 密码登录并仅使用 SSH 密钥 + 创建非 root 用户用于日常任务
- 通过 dotenv 在 .env 中存储敏感值并将 .env 添加到 .gitignore — 永远不要在代码中硬编码
- 启用免费云防火墙和 ufw 一起,限制端口仅限真正必要的
- 在进行重大更改前创建 Droplet 快照($0.06/GiB/月),以防需要回滚
- 在 2+ vCPU Droplet 上使用 PM2 集群模式并将保留 IP 绑定到活跃 Droplet 以避免每月 $5 闲置费