
我是如何将 Node.js 应用部署到生产环境的(2026)
我把自己从代码到生产环境的完整部署流程整理了一下,几分钟就能搞定。
应用层: Node.js + Express
数据库: SQLite(小型项目)或 PostgreSQL(大型项目)
进程管理: systemd
反向代理: Nginx
SSL证书: Let's Encrypt(免费、自动续期)
服务器: 5-10元/月的 VPS(阿里云、腾讯云等)
持续集成/部署: GitHub Actions(公开仓库免费) # 确保你的应用包含:
# - package.json 中有 start 脚本
# - .env.example(模板文件,不包含敏感信息!)
# - .gitignore(排除 node_modules、.env、dist)
# - 构建脚本(如果是 TypeScript) # package.json
{ "scripts": { "start": "node dist/server.js", "build": "tsc", "dev": "tsx watch server.ts" }
} # SSH 登录服务器
ssh root@your-server-ip # 更新系统
apt update && apt upgrade -y # 安装 Node.js(通过 nvm)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.0/install.sh | bash
source ~/.bashrc
nvm install 22
nvm use 22
nvm alias default 22 # 全局安装 PM2(进程管理器)
npm install -g pm2 # 安装 Nginx
apt install nginx -y # 安装 Certbot(用于 SSL)
apt install certbot python3-certbot-nginx -y # 创建应用目录
mkdir -p /var/www/myapp
chown -R $USER:$USER /var/www/myapp # 方式 A:Git pull(最简单)
cd /var/www/myapp
git clone https://github.com/yourname/myapp.git .
npm ci --production
npm run build # 如果是 TypeScript # 方式 B:GitHub Actions(自动化)
# .github/workflows/deploy.yml
name: Deploy on: push: branches: [main] jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Deploy to server uses: appleboy/ssh-action@master with: host: ${ secrets.HOST } username: ${ secrets.USERNAME } key: ${ secrets.SSH_KEY } script: | cd /var/www/myapp git pull origin main npm ci --production npm run build pm2 restart myapp # 使用 PM2 启动
cd /var/www/myapp
pm2 start npm --name "myapp" -- start # 常用 PM2 命令
pm2 list # 列出所有进程
pm2 logs myapp # 查看日志
pm2 logs myapp --lines 100 # 最近 100 行
pm2 restart myapp # 重启
pm2 stop myapp # 停止
pm2 delete myapp # 删除
pm2 monit # 实时监控 # 保存 PM2 配置(重启后生效)
pm2 startup # 生成启动脚本
pm2 save # 保存当前进程列表 # 或者使用 ecosystem.config.js 进行配置
module.exports = { apps: [{ name: 'myapp', script: 'npm', args: 'start', cwd: '/var/www/myapp', instances: 1, autorestart: true, watch: false, max_memory_restart: '500M', env: { NODE_ENV: 'production', PORT: 3000, }, }],
}; # /etc/nginx/sites-available/myapp
server { listen 80; server_name myapp.com www.myapp.com; location / { proxy_pass http://127.0.0.1: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; proxy_set_header X-Forwarded-Proto $scheme; proxy_cache_bypass $http_upgrade; }
} # 启用站点
ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/
nginx -t # 测试配置
systemctl reload nginx # 获取免费 SSL 证书(自动续期!)
certbot --nginx -d myapp.com -d www.myapp.com # Certbot 会自动修改 Nginx 配置:
# - 将 HTTP 重定向到 HTTPS
# - 添加 SSL 证书路径
# - 添加安全响应头 # 测试自动续期
certbot renew --dry-run # 自动续期由 Certbot 自动设置
# 检查命令:systemctl status certbot.timer # 添加到 Nginx server 块: # 安全响应头
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Content-Security-Policy "default-src 'self'" always; # 限流
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s; location /api/ { limit_req zone=api burst=20 nodelay; proxy_pass http://127.0.0.1:3000;
} # 阻止常见攻击路径
location ~ /\. { deny all;
} # 防火墙(UFW)
ufw allow 22/tcp # SSH
ufw allow 80/tcp # HTTP
ufw allow 443/tcp # HTTPS
ufw enable # Fail2Ban(自动封禁可疑 IP)
apt install fail2ban -y
systemctl enable fail2ban # 禁用 root 登录
# 编辑 /etc/ssh/sshd_config:
# PermitRootLogin no
# PasswordAuthentication no(仅使用 SSH 密钥!)
systemctl restart sshd # PM2 监控
pm2 monit # 服务器资源
htop
df -h
free -h # 应用日志
pm2 logs myapp # Nginx 访问日志
tail -f /var/log/nginx/access.log # Nginx 错误日志
tail -f /var/log/nginx/error.log # 设置日志轮转(防止磁盘被占满!)
# /etc/logrotate.d/myapp
/var/log/myapp/*.log { daily rotate 14 compress delaycompress missingok notifempty create 0640 www-data adm
} # 在服务器上创建 .env 文件(不要提交到 git!)
cat > /var/www/myapp/.env << EOF
NODE_ENV=production
PORT=3000
DATABASE_URL=/var/www/myapp/data.db
JWT_SECRET=your-random-secret-here
EOF # 设置权限
chmod 600 .env
chown $USER:$USER .env # 在应用中,使用 dotenv:
# npm install dotenv
// 生产环境会自动加载 .env □ 应用构建无错误
□ .env.example 已提交(不包含敏感信息!)
□ 服务器上已创建 .env,包含生产环境配置
□ 进程管理器已配置(PM2/systemd)
□ Nginx 反向代理正常工作
□ SSL 证书已安装
□ HTTP 已重定向到 HTTPS
□ 安全响应头已配置
□ 限流已启用
□ 防火墙已配置(UFW)
□ SSH 仅使用密钥认证
□ 日志轮转已设置
□ 监控已部署
□ 数据库已备份
□ 崩溃自动重启(PM2)
□ 服务器重启自动启动(PM2 startup)
□ GitHub Actions CI/CD 已配置(可选但推荐) 你的部署方案是什么?有什么心得?