如何使用 systemd 在启动时启动 Linux 服务
确保关键服务在服务器重启时自动启动是任何 Linux 系统管理员最基本的职责之一。无论您在 VPS Hosting 环境中运行 Web 应用程序、数据库引擎还是自定义守护进程,意外重启都不应该意味着长时间的停机。使用 systemd — 为当今大多数 Linux 发行版提供支持的现代 init 系统 — 您可以精确定义服务如何、何时以及以谁的身份启动,所有这些都通过一个清晰的声明式单元文件完成。
本综合指南将引导您完成每一步:了解 systemd 是什么、创建生产就绪的服务单元文件、验证权限、测试可执行文件以及管理服务生命周期。最后,您的自定义应用程序将在每次重启时自动存活。
什么是 systemd,为什么它很重要?
systemd 是一个 init 系统和服务管理器,它在大多数主要 Linux 发行版(包括 Ubuntu、Debian、CentOS、Rocky Linux、AlmaLinux 和 Fedora)中替代了 SysVinit 和 Upstart 等较旧的替代品。systemd 不是在启动时执行一系列顺序的 shell 脚本,而是并行化服务启动、解决依赖关系排序,并从单一统一的界面管理系统进程的整个生命周期。
systemd 在服务器环境中的主要优势
| 功能 | 优势 |
|---|---|
| 并行服务启动 | 多服务服务器上的启动时间更快 |
| 依赖关系管理 | 服务仅在其先决条件就绪后启动 |
| 自动重启策略 | 崩溃的服务无需手动干预即可恢复 |
| 通过 journald 的集中式日志记录 | 所有服务输出都被捕获在可查询的日志中 |
| 基于 cgroup 的资源控制 | 每个服务都强制执行 CPU 和内存限制 |
| 套接字和设备激活 | 服务按需启动,而不是无条件启动 |
对于在 Dedicated Servers 或 VPS 实例上管理应用程序的任何人来说,这些功能直接转化为更高的正常运行时间和在负载下更可预测的行为。
理解 systemd 单元文件
systemd 管理的所有内容都由 单元文件 表示 — 一个分为多个部分的纯文本配置文件。一个 service 单元文件告诉 systemd:
- 什么是服务(元数据、描述、依赖关系)
- 如何运行它(可执行文件路径、工作目录、用户上下文)
- 何时启动它(启动目标、相对于其他单元的排序)
- 如果失败该怎么办(重启策略、重启延迟)
系统范围内的服务单元文件位于 /etc/systemd/system/ 中。放置在此处的文件优先于 /usr/lib/systemd/system/ 中的供应商提供的单元,并在软件包升级后保持不变。
分步指南:创建 systemd 服务单元
以下演练使用一个名为 myapp 的虚拟应用程序。将 myapp、myappuser 和 /opt/myapp 的每个实例替换为适合您自己环境的值。
步骤 1:准备工作目录
在编写单元文件之前,决定您的应用程序将从哪里运行。一个专用的工作目录可以保持配置文件、日志和运行时数据的组织,并使权限管理变得简单明了。
如果目录不存在,请创建它:
sudo mkdir -p /opt/myapp> 为什么是 /opt/myapp 而不是 /etc/systemd/system/myapp?
> /etc/systemd/system/ 树是为 systemd 自己的配置保留的。将应用程序数据放在那里是非标准的,可能会造成混淆。根据最适合您工作负载的文件系统层次结构标准类别,使用 /opt、/srv 或 /var/lib。
将所有权分配给将运行该服务的用户:
sudo useradd --system --no-create-home --shell /usr/sbin/nologin myuser
sudo chown -R myuser:myuser /opt/myapp使用 专用系统账户(无登录 shell、无主目录)是安全最佳实践。如果应用程序曾被破坏,它会限制影响范围。
验证结果:
ls -ld /opt/myapp
# Expected output: drwxr-xr-x 2 myuser myuser 4096 Jan 1 00:00 /opt/myapp步骤 2:创建服务单元文件
使用您首选的编辑器打开一个新的单元文件:
sudo nano /etc/systemd/system/myapp.service粘贴以下内容,调整值以匹配您的应用程序:
[Unit]
Description=My Custom Application
Documentation=https://example.com/docs
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
ExecStart=/usr/bin/myapp --config /opt/myapp/myapp.conf
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
RestartSec=5s
User=myuser
Group=myuser
WorkingDirectory=/opt/myapp
StandardOutput=journal
StandardError=journal
SyslogIdentifier=myapp
# Security hardening
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
[Install]
WantedBy=multi-user.target使用 Ctrl+O 保存文件,然后使用 Ctrl+X 退出。
步骤 3:理解每个指令
[Unit] 部分
| 指令 | 目的 |
|---|---|
| Description= | systemctl 输出中显示的人类可读名称 |
| Documentation= | 服务的 URL 或手册页参考 |
| After= | 确保服务在列出的单元处于活动状态*之后*启动 |
| Wants= | 软依赖 — systemd 将*尝试*启动列出的单元,但如果不可用则不会失败 |
> After= 与 Wants=:对于真正需要完全配置的网络接口的服务,使用 After=network-online.target(与 Wants=network-online.target 结合)— 例如,在启动时连接到远程数据库或外部 API 的应用程序。After=network.target 仅保证网络子系统已被*启动*,而不是接口已启动且地址已分配。
[Service] 部分
| 指令 | 目的 |
|---|---|
| Type=simple | 由 ExecStart 启动的进程是主进程(对大多数应用程序的默认设置) |
| ExecStart= | 二进制文件的完整路径和任何参数。始终使用绝对路径。 |
| ExecReload= | 无需完全重启即可重新加载配置的命令(可选但推荐) |
| Restart=on-failure | 仅当服务以非零代码退出或被信号杀死时重启服务。如果您想要重启,即使是干净退出,也可以使用 always。 |
| RestartSec= | 尝试重启前等待的秒数 |
| User= / Group= | 以此用户/组身份运行进程,而不是 root |
| WorkingDirectory= | 在执行 ExecStart 之前设置当前目录 |
| StandardOutput= / StandardError= | 将 stdout/stderr 路由到 systemd 日志 |
| SyslogIdentifier= | 用于过滤此服务的日志条目的标签 |
| NoNewPrivileges=true | 防止进程通过 setuid 二进制文件获得额外权限 |
| ProtectSystem=strict | 将 /usr、/boot 和 /etc 挂载为对服务只读 |
| ProtectHome=yes | 使 /root、/home 和 /run/user 无法访问 |
| PrivateTmp=yes | 为服务提供其自己的私有 /tmp 命名空间 |
[Install] 部分
| 指令 | 目的 |
|---|---|
| WantedBy=multi-user.target | 在系统达到标准多用户(非图形)运行级别时启用该服务。这对于几乎所有服务器守护进程都是正确的目标。 |
步骤 4:验证文件和目录权限
在重新加载 systemd 之前,确认服务用户实际上可以访问它需要的所有内容:
# Check working directory ownership and permissions
ls -ld /opt/myapp
# Confirm the executable exists and is executable
ls -l /usr/bin/myapp
# Verify the config file is readable by myuser
sudo -u myuser cat /opt/myapp/myapp.conf如果任何这些检查失败,在继续之前更正权限:
# Make the binary executable
sudo chmod +x /usr/bin/myapp
# Grant read access to the config file
sudo chmod 640 /opt/myapp/myapp.conf
sudo chown myuser:myuser /opt/myapp/myapp.conf步骤 5:手动测试可执行文件
在将控制权交给 systemd 之前,始终验证您的应用程序正确运行。这将应用程序错误与 systemd 配置问题隔离开来:
sudo -u myuser /usr/bin/myapp --config /opt/myapp/myapp.conf如果应用程序启动没有错误,按 Ctrl+C 停止它并继续。如果失败,对应用程序本身进行故障排除 — 检查所有依赖项是否已安装、环境变量是否已设置以及所需的端口是否可用。
步骤 6:重新加载 systemd 并启用该服务
保存单元文件后,指示 systemd 重新读取其配置:
sudo systemctl daemon-reload启用该服务,使其在每次后续启动时自动启动:
sudo systemctl enable myapp.service此命令在适当的 /etc/systemd/system/multi-user.target.wants/ 目录中创建一个符号链接,将您的单元文件链接到 multi-user.target 启动序列中。您应该看到类似以下的输出:
Created symlink /etc/systemd/system/multi-user.target.wants/myapp.service → /etc/systemd/system/myapp.service.立即启动该服务,无需重启:
sudo systemctl start myapp.service步骤 7:验证服务正在运行
检查服务的当前状态:
sudo systemctl status myapp.service一个健康的服务会产生类似以下的输出:
● myapp.service - My Custom Application
Loaded: loaded (/etc/systemd/system/myapp.service; enabled; vendor preset: disabled)
Active: active (running) since Wed 2025-01-01 12:00:00 UTC; 5s ago
Main PID: 12345 (myapp)
Tasks: 4 (limit: 4915)
Memory: 12.3M
CPU: 45ms
CGroup: /system.slice/myapp.service
└─12345 /usr/bin/myapp --config /opt/myapp/myapp.conf要验证的关键字段:
- Loaded= — 确认单元文件被成功解析,并显示它是否为启动 enabled 或 disabled
- Active= — 该服务当前正在运行
- Main PID= — 您的应用程序的进程 ID
步骤 8:使用 journalctl 监控日志
systemd 将所有服务输出路由到 日志,一个结构化的、可查询的日志存储。使用 journalctl 来检查它:
# View all logs for myapp (most recent last)
journalctl -u myapp.service
# Follow live log output (like tail -f)
journalctl -u myapp.service -f
# Show only logs since the last boot
journalctl -u myapp.service -b
# Show the last 50 lines
journalctl -u myapp.service -n 50
# Show logs since a specific time
journalctl -u myapp.service --since "2025-01-01 12:00:00"如果您的服务无法启动,日志几乎总是包含解释原因的确切错误消息。这是在进行任何更改之前首先要查看的地方。
步骤 9:测试启动行为
要确认服务在重启后存活,而无需手动检查启动序列,您可以模拟它:
# Reboot the server (only if safe to do so)
sudo reboot服务器重新上线后,再次检查服务状态:
sudo systemctl status myapp.service如果它显示 active (running),您的服务已正确配置为自动启动。
管理服务生命周期
一旦您的服务运行,您将定期使用以下命令:
常见 systemctl 命令
# Start the service
sudo systemctl start myapp.service
# Stop the service gracefully
sudo systemctl stop myapp.service
# Restart the service (stop + start)
sudo systemctl restart myapp.service
# Reload configuration without restarting (if ExecReload is defined)
sudo systemctl reload myapp.service
# Enable automatic startup at boot
sudo systemctl enable myapp.service
# Disable automatic startup at boot
sudo systemctl disable myapp.service
# Check whether the service is enabled
sudo systemctl is-enabled myapp.service
# Check whether the service is currently active
sudo systemctl is-active myapp.service
# View the full unit file as systemd interprets it
sudo systemctl cat myapp.service
# Edit the unit file and reload in one step
sudo systemctl edit --full myapp.service故障排除常见问题
服务无法启动:”No such file or directory”
这通常意味着 ExecStart 指向一个不存在的二进制文件或 WorkingDirectory 不存在。验证两个路径:
which myapp
ls -l /opt/myapp服务启动但立即退出
检查日志中应用程序自己的错误输出:
journalctl -u myapp.service -n 100 --no-pager还要验证 Type= 对您的应用程序是否正确。如果您的二进制文件将自己分叉到后台,请改用 Type=forking。
“Permission denied” 错误
服务用户缺少对所需文件或目录的访问权限。使用 ls -la 审计权限,使用 sudo -u 测试交互式访问。
端口已在使用中
另一个进程绑定到您的应用程序需要的端口。使用以下方式识别它:
sudo ss -tlnp | grep :<port>服务在循环中重启
如果 Restart=on-failure 导致快速重启循环,systemd 最终会限制重启。检查 [Service] 部分中的 StartLimitIntervalSec= 和 StartLimitBurst= 来调整此行为,并始终在日志中调查根本原因。
生产服务器的高级 systemd 模式
环境变量和环境文件
永远不要在单元文件中硬编码机密。改用环境文件:
[Service]
EnvironmentFile=/etc/myapp/myapp.env
ExecStart=/usr/bin/myapp使用 touch 和 chmod 600 创建 /etc/myapp.env 来限制访问:
DATABASE_URL=postgresql://user:password@localhost/mydb
API_KEY=supersecretkey服务依赖关系和排序
如果您的应用程序依赖于数据库服务(例如 PostgreSQL 或 MySQL),明确声明该依赖关系:
[Unit]
After=network-online.target postgresql.service
Requires=postgresql.serviceRequires= 是一个硬依赖 — 如果 PostgreSQL 无法启动,systemd 也不会尝试启动您的服务。
看门狗集成
对于任务关键型服务,启用 systemd 的内置看门狗来检测挂起的进程:
[Service]
WatchdogSec=30s
Restart=on-watchdog您的应用程序必须定期调用 sd_notify(0, “WATCHDOG=1”) 来重置看门狗计时器。如果它在 WatchdogSec= 内无法这样做,systemd 会杀死并重启该服务。
选择正确的托管环境
本指南中描述的 systemd 配置模式在所有 Linux 服务器类型中同样适用,但您选择的托管基础设施会影响您对 init 系统和服务管理的控制程度。
- VPS Hosting — 完全的 root 访问权限、完整的 systemd 控制、非常适合自定义应用程序部署。AlexHost VPS 计划在 KVM 虚拟化上运行,为您提供真正的隔离内核环境。
- Dedicated Servers — 为资源密集型服务提供最大性能和隔离。完全的硬件访问权限,没有嘈杂的邻居。
- VPS with cPanel — 将根级别的 systemd 访问权限与图形控制面板相结合,适合从单一界面管理系统服务和 Web 托管的团队。
- Shared Web Hosting — 适合不需要自定义系统服务的标准 Web 应用程序。没有 root 访问权限,但零服务器管理开销。
如果您部署的是自定义守护进程、微服务或任何需要在重启后自动存活的应用程序,具有完全 root 访问权限的 VPS 或专用服务器是适当的选择。
快速参考:完整的 systemd 服务检查清单
在考虑您的服务生产就绪之前,请验证此检查清单上的每一项:
- [ ] 单元文件已保存到 /etc/systemd/system/myapp.service
- [ ] ExecStart= 使用指向有效的、可执行的二进制文件的绝对路径
- [ ] WorkingDirectory= 存在并由服务用户拥有
- [ ] 服务以专用的非 root 系统账户身份运行
- [ ] systemctl daemon-reload 在任何单元文件更改后执行
- [ ] systemctl enable myapp 确认已创建符号链接
- [ ] systemctl status myapp 显示 active (running)
- [ ] journalctl -u myapp 显示无错误
- [ ] 服务器已重启,并在启动后确认服务正在运行
- [ ] 已应用安全加固指令(NoNewPrivileges=true、ProtectSystem=strict、ProtectHome=yes)
结论
systemd 是现代 Linux 服务器的最终服务管理层。通过创建一个结
on All Hosting Services
