13 10 月 2026

把游戏服务端或网站跑起来只是第一步。真正麻烦的是:服务器重启后服务没起来、进程半夜崩了没人知道、查日志要翻好几个文件。

systemd 就是解决这些事的。它是现代 Linux 的标准服务管理器,写好一个 .service 文件,就能获得:开机自启、崩溃自动重启、统一日志、标准启停命令。

需要一台常开的机器?财富云计算 的云服务器支持 7×24 运行。NAT 型云服务器无独立公网 IP,价格比带独立 IP 的机型省一半以上,做内网服务、游戏服务端非常划算。

一、为什么要用 systemd

不用 systemd 时的典型做法:

nohup ./start.sh > server.log 2>&1 &

问题一堆:

  • 服务器重启后,没人再把它拉起来
  • 进程崩了就真崩了,不会自动恢复
  • 关掉要自己找 PID kill
  • 日志会无限增长
  • 不知道它是从什么时候开始跑的、跑没跑起来

用了 systemd 之后:

systemctl start myserver
systemctl stop myserver
systemctl restart myserver
systemctl enable myserver      # 开机自启
systemctl status myserver
journalctl -u myserver -f      # 看日志

服务挂了还能自动重启,运维体验完全不同。


二、systemd 基础命令

systemctl start nginx              # 启动
systemctl stop nginx               # 停止
systemctl restart nginx            # 重启
systemctl reload nginx             # 重载配置(不中断服务,nginx 支持)
systemctl status nginx             # 查看状态与最近日志
systemctl enable nginx             # 设置开机自启
systemctl enable --now nginx       # 设置自启并立即启动
systemctl disable nginx            # 取消开机自启
systemctl is-enabled nginx         # 查询是否自启
systemctl is-active nginx          # 查询是否在运行

systemctl list-units --type=service              # 列出已加载的服务
systemctl list-units --type=service --state=failed  # 只看失败的
systemctl list-unit-files --type=service         # 列出所有服务文件及自启状态

systemctl daemon-reload            # ⚠️ 改了 service 文件后必须执行
systemctl reset-failed             # 清除失败计数

开机启动耗时分析(想优化开机速度时):

systemd-analyze
systemd-analyze blame              # 谁拖慢了开机
systemd-analyze critical-chain

三、service 文件写在哪

路径用途
/etc/systemd/system/管理员自建服务的推荐位置,优先级最高
/usr/lib/systemd/system/(有的发行版是 /lib/systemd/system/)软件包安装的服务
~/.config/systemd/user/用户级服务(需 systemctl --user)

自建服务统一放 /etc/systemd/system/,比如 /etc/systemd/system/gameserver.service。


四、service 文件结构详解

一个完整例子(游戏服务端):

[Unit]
Description=CS2 Game Server
Documentation=https://z0w.cn
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=steam
Group=steam
WorkingDirectory=/opt/steam/cs2
ExecStart=/opt/steam/cs2/start.sh
ExecStop=/bin/kill -SIGTERM $MAINPID
ExecReload=/bin/kill -SIGHUP $MAINPID
Restart=on-failure
RestartSec=10
TimeoutStopSec=30
KillSignal=SIGTERM
StandardOutput=journal
StandardError=journal
SyslogIdentifier=cs2
LimitNOFILE=65535
Environment="LANG=en_US.UTF-8"
EnvironmentFile=/opt/steam/cs2/env.conf

[Install]
WantedBy=multi-user.target

[Unit] 段

字段说明
Description服务描述,systemctl status 里能看到
Documentation文档链接
After在哪些单元之后启动(只管顺序,不表示依赖)
Before在哪些单元之前启动
Requires强依赖,依赖挂了自己也停
Wants弱依赖,依赖挂了自己继续跑
Conflicts与哪些服务互斥

游戏服务端最常用的就是 After=network-online.target,确保网络真正就绪(不是 network.target,那个只表示网络服务启动了,不代表 IP 已拿到)。

[Service] 段(核心)

字段说明
Type进程类型,见下文
User / Group以哪个用户身份运行(强烈建议不要 root)
WorkingDirectory工作目录
ExecStart启动命令(必须绝对路径)
ExecStop停止命令
ExecReload重载命令
Restart重启策略
RestartSec重启前等待秒数
TimeoutStopSec停止超时时间
Environment环境变量,可多行
EnvironmentFile从文件读环境变量
LimitNOFILE文件句柄上限(高并发服务要调)
StandardOutput输出去向(journal / file:路径 / null)
SyslogIdentifier日志里的标识名

Type 取值:

类型适用
simple(默认)程序在前台运行不退出,大多数情况
forking程序会 fork 到后台(传统 daemon),需配 PIDFile=
oneshot跑完就退出的脚本(配合 RemainAfterExit=yes)
notify程序启动完会主动通知 systemd(需程序支持 sd_notify)
idle等其他任务都完成了再执行

Restart 策略:

值含义
no不自动重启(默认)
always无论什么原因退出都重启
on-success正常退出(code 0)才重启
on-failure非正常退出、超时、被信号杀死时重启(最常用)
on-abnormal被信号杀死或超时才重启
on-abort异常终止才重启
on-watchdog看门狗超时才重启

[Install] 段

[Install]
WantedBy=multi-user.target      # 多用户命令行模式(服务器标准)

systemctl enable 的本质就是在 multi-user.target.wants/ 目录里建一个软链接。图形界面环境用 graphical.target。


五、实战一:把游戏服务端做成服务

以 SteamCMD 装的通用服务端为例:

1)建专用用户

useradd -r -m -s /sbin/nologin steam
mkdir -p /opt/steam/cs2
chown -R steam:steam /opt/steam

2)写启动脚本

nano /opt/steam/cs2/start.sh
#!/bin/bash
cd /opt/steam/cs2
./cs2 -dedicated -console \
  -port 27015 \
  +map de_dust2 \
  +maxplayers 10 \
  -tickrate 128
chmod +x /opt/steam/cs2/start.sh
chown steam:steam /opt/steam/cs2/start.sh
注意:启动命令必须是前台阻塞运行的。如果你的脚本里用了 nohup ... & 把自己放到后台,systemd 会认为进程已退出,服务立刻失败。

3)写 service 文件

sudo nano /etc/systemd/system/cs2.service
[Unit]
Description=Counter-Strike 2 Dedicated Server
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=steam
Group=steam
WorkingDirectory=/opt/steam/cs2
ExecStart=/opt/steam/cs2/start.sh
Restart=on-failure
RestartSec=15
LimitNOFILE=65535
StandardOutput=journal
StandardError=journal
SyslogIdentifier=cs2

[Install]
WantedBy=multi-user.target

4)启用

sudo systemctl daemon-reload
sudo systemctl enable --now cs2
sudo systemctl status cs2

现在它会开机自动启动,崩了 15 秒后自动重启。


六、实战二:给程序设置资源限制与自动重启策略

限制内存(防止服务端吃光内存把系统拖死):

[Service]
MemoryMax=6G                 # 硬上限,超过会被 OOM kill
MemoryHigh=5G                # 软上限,超过会限流/回收(不杀进程)
CPUQuota=200%                # 最多用 2 个核
TasksMax=500
LimitNOFILE=65535
LimitNPROC=4096

崩溃后逐步退避重启(避免疯狂重启循环):

[Service]
Restart=on-failure
RestartSec=10
StartLimitIntervalSec=300
StartLimitBurst=5            # 5 分钟内最多重启 5 次,超过就放弃

进程守护看门狗(程序需支持 sd_notify):

[Service]
WatchdogSec=30

七、实战三:定时/一次性任务(systemd timer,替代 cron)

cron 的问题是不好调试、没有日志、系统忙时会跳过。systemd timer 更现代:

/etc/systemd/system/backup.service

[Unit]
Description=Daily backup

[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh

/etc/systemd/system/backup.timer

[Unit]
Description=Run backup daily at 03:00

[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true              # 关机错过的时间点,开机后补跑
RandomizedDelaySec=300       # 随机延迟,避免多机同时跑

[Install]
WantedBy=timers.target
systemctl daemon-reload
systemctl enable --now backup.timer
systemctl list-timers        # 查看所有定时器

OnCalendar 常用写法:

OnCalendar=daily                    # 每天 00:00
OnCalendar=*-*-* 03:00:00           # 每天 3 点
OnCalendar=Mon *-*-* 02:00:00       # 每周一 2 点
OnCalendar=*-*-01 04:00:00          # 每月 1 号 4 点
OnCalendar=*:0/15                   # 每 15 分钟

八、日志:journalctl 用法

journalctl -u cs2                        # 看某个服务的全部日志
journalctl -u cs2 -f                     # 实时跟踪(等同 tail -f)
journalctl -u cs2 --since "2026-10-01" --until "2026-10-02"
journalctl -u cs2 -n 100                 # 最近 100 行
journalctl -u cs2 -p err                 # 只看错误级别
journalctl -b                            # 本次开机以来的日志
journalctl -b -1                         # 上一次开机的日志
journalctl --disk-usage                  # 日志占了多少磁盘
journalctl -u cs2 -o json-pretty         # JSON 格式输出(给程序处理)

日志会占磁盘,建议限制:

sudo nano /etc/systemd/journald.conf
[Journal]
SystemMaxUse=500M
MaxRetentionSec=14day
Compress=yes
sudo systemctl restart systemd-journald
journalctl --vacuum-size=500M            # 立即清理到 500M
journalctl --vacuum-time=7d

九、调试 service 的套路

服务起不来,按这个顺序查:

# 1. 看状态和最近的日志
systemctl status myserver -l

# 2. 看完整日志
journalctl -u myserver -n 100 --no-pager

# 3. 手动跑一遍 ExecStart,看是不是命令本身有问题
sudo -u steam /opt/steam/cs2/start.sh

# 4. 检查语法
systemd-analyze verify /etc/systemd/system/myserver.service

# 5. 重置失败状态(超过重启上限后服务会进入 failed)
systemctl reset-failed myserver
systemctl start myserver

常见错误:

报错原因
Failed to start: Exec format error脚本缺 shebang #!/bin/bash,或没 chmod +x
Status=203/EXECExecStart 路径不存在或不可执行
Status=217/USERUser= 指定的用户不存在
Status=200/CHDIRWorkingDirectory 不存在
Start request repeated too quickly一直启动失败,触发了 StartLimit
启动后马上退出(Type=simple)程序自己 fork 到后台了,改 Type=forking + PIDFile=
改了文件没生效忘了 systemctl daemon-reload

十、服务模板:一个实例跑多个

想在一台机器上开 3 个游戏服(27015/27016/27017),用 @ 模板:

/etc/systemd/system/gamesrv@.service

[Unit]
Description=Game Server instance %i
After=network-online.target

[Service]
Type=simple
User=steam
WorkingDirectory=/opt/steam/%i
ExecStart=/opt/steam/%i/start.sh %i
Restart=on-failure
RestartSec=10

[Install]
WantedBy=multi-user.target

%i 会被替换成实例名。启动:

systemctl enable --now gamesrv@cs2
systemctl enable --now gamesrv@valheim
systemctl status gamesrv@cs2

一套配置管多个实例,非常适合开多服的玩家(本站《多游戏服共存与端口规划》有更详细的方案)。


十一、安全加固选项

systemd 提供不少沙箱能力,给对外服务加上能大幅降低被攻破的危害:

[Service]
# 文件系统隔离
ProtectSystem=strict              # /usr /boot /etc 只读
ProtectHome=yes                   # 看不到 /home
PrivateTmp=yes                    # 独立的 /tmp
ReadWritePaths=/opt/steam/cs2     # 只放行需要写的目录

# 权限
NoNewPrivileges=yes               # 禁止提权(SUID 失效)
User=steam
Group=steam

# 系统资源
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectControlGroups=yes
RestrictSUIDSGID=yes
MemoryDenyWriteExecute=yes        # 禁止 W+X 内存(防部分漏洞利用)

# 网络
RestrictAddressFamilies=AF_INET AF_INET6

加完后用 systemd-analyze security 服务名 打分,看看还有哪些可以加强。


十二、完整清单:给任何程序写 service 的步骤

# 1. 确认程序能前台运行
/opt/app/start.sh    # 直接跑,Ctrl+C 能停住

# 2. 建运行用户
useradd -r -s /sbin/nologin apprunner

# 3. 授权目录
chown -R apprunner:apprunner /opt/app

# 4. 写 service 文件
sudo nano /etc/systemd/system/app.service

# 5. 重载并启用
sudo systemctl daemon-reload
sudo systemctl enable --now app

# 6. 验证
systemctl status app
journalctl -u app -f

# 7. 测重启恢复
sudo reboot
# 重启后 systemctl status app 应该 active

常驻服务需要一台稳定的服务器。财富云计算 的云服务器 7×24 运行、支持开机自启与随时重装。若只是内网服务、游戏服务端、学习环境,选 NAT 型云服务器(无独立公网 IP)价格比带独立 IP 的机型省一半以上,性价比很高。

相关阅读:

  • 《screen 与 tmux 会话保持实战》
  • 《服务器资源排查:CPU / 内存 / 磁盘 / 带宽》
  • 《Linux 常用命令速查表》
延伸阅读
  1. 网站环境怎么选:LNMP 手动 / 宝塔面板 / Docker / 云开发
  2. 问卷表单系统搭建:SurveyKing / Formbricks,数据自己收
  3. 图床搭建:Lsky Pro / Lychee,自托管图片托管方案
  4. 邮件列表搭建:listmonk 自托管 Newsletter,把读者握在自己手里
  5. 文档站点搭建:MkDocs / VuePress / Docsify,产品文档与技术手册方案
  6. RSS 阅读器搭建:FreshRSS 自托管,把信息流拿回自己手里
  7. 短链接服务搭建:Shlink 自托管,链接数据自己掌握
  8. 网站状态监控页搭建:Uptime Kuma,服务器挂了第一时间知道
更多阅读
  1. 上一篇:Linux 用户与 sudo 权限管理详解:多人共用服务器怎么管才不乱
  2. 下一篇:screen 与 tmux 会话保持实战:SSH 断线程序也不中断
发表评论 7

取消回复