单服务器生产部署:从架构到故障恢复 · 2/10

用 systemd 托管网站应用:用户、环境文件、重启与日志

把手动运行的网站进程整理为 systemd 服务,明确专用用户、工作目录、环境文件、启动命令、自动重启和日志查看,并保留安全验证顺序。

关闭 SSH 后网站也应该继续运行

在终端执行 npm start 能证明应用可运行,不能证明它适合生产。会话断开、服务器重启或进程崩溃后,网站可能直接消失。systemd 的价值是把运行方式写成系统可理解的声明:以谁运行、从哪里启动、失败如何处理、日志去哪里。 开始前先完成 新服务器基础配置SSH、防火墙安全设置。下面使用 myapp 和 127.0.0.1:3000 作为示例,目录与命令必须替换成项目真实值。

先用专用用户跑通同一条启动命令

创建不能交互登录的系统用户,并让它只拥有需要的目录。不要直接写 unit 后反复猜权限;先切换到该用户,用生产环境变量执行准确启动命令。

bash
sudo useradd --system --home /opt/myapp --shell /usr/sbin/nologin myapp
sudo chown -R myapp:myapp /opt/myapp
sudo -u myapp -H /usr/bin/node /opt/myapp/server.js

确认应用只监听 127.0.0.1:3000,并用 curl http://127.0.0.1:3000/health 验证。Node、pnpm 或其他运行时路径要使用 command -v 找到的绝对路径;systemd 不会自动加载你的交互式 shell 配置。

环境文件与 unit 分开保存

把非公开配置放到 /etc/myapp/myapp.env,所有者 root,组可以是应用专用组,权限限制为 640 或更严格。环境文件不是保险箱,拥有读取权限的进程仍能看到它;真正高敏感值还要结合专用 secrets 机制和轮换。

ini
# /etc/systemd/system/myapp.service
[Unit]
Description=My website application
After=network.target

[Service]
Type=simple
User=myapp
Group=myapp
WorkingDirectory=/opt/myapp
EnvironmentFile=/etc/myapp/myapp.env
ExecStart=/usr/bin/node /opt/myapp/server.js
Restart=on-failure
RestartSec=5s

[Install]
WantedBy=multi-user.target

不要在 ExecStart 里写依赖 shell 展开的复杂串联命令。需要构建、迁移和启动三个阶段时,把构建放在发布流程,数据库迁移单独控制,服务只负责启动已经准备好的版本。

加载、启动与验证要分三步

bash
sudo systemd-analyze verify /etc/systemd/system/myapp.service
sudo systemctl daemon-reload
sudo systemctl start myapp
sudo systemctl status myapp --no-pager
curl -fsS http://127.0.0.1:3000/health
sudo systemctl enable myapp

先 start 并验证健康,再 enable 开机启动。status 显示 active 只说明进程存在,不代表页面可用,所以必须请求健康端点。随后重启服务器做一次真实验收,确认服务和反向代理都能自动回来。

Restart 不是越积极越好

Restart=on-failure 适合意外退出,RestartSec 给依赖恢复和日志留时间。不要用极短间隔无限重启一个配置错误的程序,它会刷满日志并掩盖首个错误。systemd 有启动频率限制,触发后应查看根因,修复后再 reset-failed。 应用主动正常退出是否需要重启,取决于进程语义。定时任务不应伪装成长驻服务;一次性迁移也不应配置永久重启。

用 journalctl 看当前这一轮,而不是淹没在历史里

bash
journalctl -u myapp -n 100 --no-pager
journalctl -u myapp -f
journalctl -u myapp --since '20 minutes ago'

日志里应有时间、级别和请求关联信息,但不能打印密码和完整令牌。应用自行写文件时还要配置轮转;若统一进入 journal,也要确认系统日志的磁盘上限和保留周期。

完成标准

关闭 SSH 后应用仍在,服务器重启后自动回来,进程不是 root,环境文件权限可解释,健康端点成功,journalctl 能找到一次启动和一次失败原因。下一篇把公网请求安全地交给它:Caddy 多站点反向代理与配置验证

参考来源

  1. systemd.servicesystemd
  2. systemd.execsystemd
我正在使用 · 推广推荐

需要时再考虑的两项实用服务

这不是自动排名,也不是每个人都需要购买。我只列出自己正在使用的入口,并把适用场景和限制说清楚。

推广说明:链接包含我的推荐信息。你通过链接注册或开通后,我可能获得平台奖励;不会因此向你额外收费。价格、活动、地区与服务条款请以下单页为准。 查看完整商业披露