教程 / 发布与基础设施 / 重新部署自托管 VPS
📝 文字 ● 中级 更新于 2026-05-13

重新部署自托管 VPS

向你自己的服务器推送代码更新。SSH 连接,拉取代码,重启服务。或者封装成一行部署脚本。典型应用的部署时间:10–30 秒。

手动流程(先理解原理)

1

SSH 连接到服务器:

ssh deploy@your-server-ip
2

进入项目目录并拉取代码:

cd /var/www/myapp
git pull origin main
3

安装新依赖(仅当 package.json / requirements.txt 有变动时才需要):

npm install --production
# or: pip install -r requirements.txt
# or: bundle install --deployment
4

构建项目(如有需要):

npm run build
5

重启服务:

# If using systemd:
sudo systemctl restart myapp

# If using pm2:
pm2 reload myapp

# If using Docker:
docker compose pull && docker compose up -d

就这些——新代码已经上线了。

封装成部署脚本

6

你会频繁执行这些操作,用脚本能省下不少时间。在服务器上创建 deploy.sh

#!/usr/bin/env bash
set -euo pipefail

cd /var/www/myapp
git pull origin main
npm install --production
npm run build
sudo systemctl restart myapp
echo "✓ Deployed at $(date)"

赋予执行权限:chmod +x deploy.sh。之后 SSH 进来,一条命令搞定重新部署:

./deploy.sh

从本地一条命令完成部署

7

甚至不需要手动 SSH——在你的 Mac 上直接用脚本搞定:

# deploy.sh on your laptop
#!/usr/bin/env bash
ssh deploy@your-server-ip '/var/www/myapp/deploy.sh'

或者直接内联执行:

ssh deploy@your-server-ip 'cd /var/www/myapp && git pull && npm install --production && npm run build && sudo systemctl restart myapp'
为重启操作单独配置免密 sudo。如果 sudo systemctl restart myapp 要求输入密码,脚本就会卡住。在 /etc/sudoers.d/myapp-restart 中添加一条规则,允许部署用户无需密码执行这一条命令。

零停机部署方案

8

简单粗暴地重启会中断正在处理的请求。真正的生产环境需要更稳健的方案:

  • pm2 reload — 逐个重启 Node.js 工作进程,新进程启动后再关闭旧进程。pm2 ↗ 内置支持。
  • systemd Type=notify — 服务就绪后主动通知 systemd,再由 systemd 关闭旧实例,停机窗口比默认重启更小。
  • Nginx 后面跑两个实例 — 在不同端口运行两份副本,切换 Nginx upstream,再重启另一个。实现简单,无需额外工具。
  • Docker Compose up -d — 对于 Compose 管理的服务,设置 deploy.update_config 后 Docker 会自动滚动替换容器。

git push 自动触发部署

9

想在每次推送到 main 时自动部署,最简单的几种方案:

  • GitHub Action SSH 远程执行。workflow_dispatchon: push: main 触发,Action 执行 ssh ... 'deploy.sh'。需要将 SSH 部署密钥存储为 GitHub Secret。
  • Webhook + 轻量监听服务。GitHub 在 push 时触发 Webhook;服务器上一个小型 Node/Python 服务接收后运行 deploy.sh。注意:公网 Webhook 端点需要验证签名(原理与Stripe Webhook 类似)。
  • 拉取模式(cron)。服务器每 60 秒轮询一次 git,有新提交就执行部署。最简单,但有轻微延迟。

回滚

10

如果新部署出了问题:

cd /var/www/myapp
git reset --hard HEAD~1   # back to the previous commit
npm install --production
npm run build
sudo systemctl restart myapp

这会回退到上一个提交。长期来看更好的做法是:将最近 N 个版本保存在带编号的目录中,用符号链接 current 指向当前版本——这样无需重新构建即可立即回滚。

数据库迁移需要格外小心。如果部署包含 schema 变更,请在重启服务之前执行迁移(确保新代码与新 schema 匹配),同时将迁移设计为向后兼容(新代码能读取旧 schema,旧代码也能读取新 schema),这样紧急回滚时就不会出错。

参考资料

下一步