【Linux】Linux 中 .service 文件核心介绍
- 一、基本概念
-
- [1.1 存放路径](#1.1 存放路径)
- [1.2 核心结构](#1.2 核心结构)
- [1.3 关键补充说明](#1.3 关键补充说明)
- 二、详细介绍
-
- [2.1 [Unit] 节(通用配置)](#2.1 [Unit] 节(通用配置))
- [2.2 [Service] 节(服务运行核心)](#2.2 [Service] 节(服务运行核心))
- [2.3 [Install] 节(开机自启配置)](#2.3 [Install] 节(开机自启配置))
- [三、如何编写一个 .service 文件?](#三、如何编写一个 .service 文件?)
- [四、如何使用 .service 文件?](#四、如何使用 .service 文件?)
一、基本概念
systemd 是 Linux 操作系统的系统与服务管理器,是现代 Linux 发行版(CentOS 7+、Ubuntu 16.04+、Debian 9+ 等)的默认初始化系统(init system),替代了传统的 SysVinit。
它的核心目标是并行启动系统服务、统一管理系统进程、简化服务配置与运维,提供了进程管理、日志收集(journald)、设备挂载、定时任务、套接字激活等全套系统能力。
1.1 存放路径
-
系统级服务:
/usr/lib/systemd/system/(系统安装的软件自带服务,如 nginx、mysql。 禁止手动修改 ) -
用户自定义服务:
/etc/systemd/system/(优先级更高,推荐 存放自定义.service文件) -
运行时服务:
/run/systemd/system/(仅由 systemd 自动生成, 禁止手动修改 ) -
核心作用:告诉 systemd 如何启动、停止、重启服务,以及服务的依赖关系、运行环境、日志配置等。
-
文件格式 :纯文本配置文件,采用
INI风格,由多个节(Section) 组成,每个节包含键值对配置。
1.2 核心结构
.service 文件分为三个核心节,部分可选节:
[Unit] :描述服务的元数据、依赖关系、启动顺序、文档说明等全局配置,是服务与系统服务管理器交互的基础配置。
- 绝大多数场景下建议保留,即使不写任何配置项,systemd 也会使用默认值;
- 理论上可以完全省略(空段或不写),但会缺失依赖、描述等信息,不推荐。
bash
# /etc/systemd/system/my-service.service
# 注释推荐独占一行
[Unit]
Description=我的自定义服务 # 服务描述(必写,便于管理)
After=network.target # 依赖:网络启动后再启动本服务
Wants=network-online.target # 弱依赖:有网络更好,没有也没关系,但是我更希望你存在。
Requires=network.target # 强依赖:网络服务失败则本服务也失败(一般不使用)
[Service] :定义服务的 核心运行逻辑 ,包括启动/停止/重启命令、执行用户、工作目录、重启策略、标准输出 / 错误日志等,是服务能正常运行的核心。
-
必须存在!!!
-
如果没有这个段,systemd 无法知道如何启动服务,会直接启动失败。
bash
[Service]
Type=simple # 服务类型(simple 为默认,前台进程)
# 启动命令(唯一必填配置, 并且可执行程序必须指定绝对路径,不能使用相对路径,且 Exec* 后面不能跟注释)
ExecStart=/usr/bin/myapp
User=ubuntu # 运行用户
Restart=on-failure # 重启策略:进程异常退出时重启
WorkingDirectory=/opt/myapp # 工作目录
[Install] :定义服务的安装配置 ,核心用于配置 开机自启 、服务所属的 target(运行级别),是 systemctl enable/disable 命令的核心依赖。
- 可选,取决于你的需求。
- 如果你不需要开机自启 :可以完全省略
[Install]段; - 如果你需要开机自启 :必须添加该段,且至少包含
WantedBy或RequiredBy配置。
bash
[Install]
WantedBy=multi-user.target # 多用户图形/命令行环境下开机自启
可选节 :如 [Install] 之外,还有 [Timer](配合定时器使用)、[Socket](套接字激活服务)等。
如何使用?
bash
sudo systemctl daemon-reload # 重载配置
sudo systemctl start my-service.service # 手动启动
sudo systemctl stop my-service.service # 手动停止
# 设置开机启用并立即启动(如果没有 [Install] 段,则无法使用 enable/disable)
sudo systemctl enable --now my-service.service
# 禁止开机自启
sudo systemctl disable my-service.service
1.3 关键补充说明
-
默认值机制
systemd 对未配置的段 / 配置项会使用内置默认值,例如:
- 省略
[Unit]段:服务描述为(unknown),依赖为默认值; [Service]段省略Type:默认值为simple;- 省略
User:默认以root用户运行。
- 省略
-
[Install]段的核心配置只有
WantedBy=和RequiredBy=是核心配置,用于指定服务所属的 target,是开机自启的关键。 -
systemd 服务文件的语法规则
- 配置项为
键=值格式,等号两侧 不能有空格(老版系统) ; - 注释以
#开头,建议注释独占一行 。Exec 开头的命令行,行尾绝对不能加注释,比如:不可以写在 ExecStart 等执行的指令后面,他会把ExecStart后面的所有值作为参数传递给服务,如果有注释就会引发报错; - 配置文件路径:系统服务存
/etc/systemd/system/,第三方服务建议存此目录,而非/usr/lib/systemd/system/。
- 配置项为
-
注意点
推荐 等号(=) 前后不能有空格。
如果有注释,注释单独占一行。不要写在以 Exec 开头的指令的后面,否则会报错。
自定义的 .service 文件放在
/etc/systemd/system目录下。Exec 指令的后面必须写指令的绝对路径,不能写相对路径,否则会报错。
| 段名 | 强制要求 | 核心作用 | 适用场景 |
|---|---|---|---|
[Unit] |
推荐保留 | 元数据、依赖、启动顺序 | 所有生产环境服务 |
[Service] |
必须 | 启动命令、运行配置、重启策略 | 所有 systemd 服务 |
[Install] |
可选 | 开机自启、target 关联 | 是否需要开机自启 systemctl enable |
二、详细介绍
2.1 [Unit] 节(通用配置)
| 配置项 | 作用 | 示例 |
|---|---|---|
Description |
服务的简短描述 | Description=My Custom Service |
Documentation |
服务文档地址 | Documentation=https://example.com/docs |
After |
控制我在谁之后启动(仅控制启动顺序,不强制服务启动成功) | After=network.target(网络启动后再启动本服务) |
Before |
控制我在谁之前启动(仅控制启动顺序,不强制服务启动成功) | Before=nginx.service |
Requires |
强依赖(依赖的服务启动失败,本服务也会启动失败) | Requires=network.target |
Wants |
弱依赖(依赖的服务启动失败,不影响本服务启动) | Wants=network-online.target |
-
Description: 对服务进行简洁描述,可以使用中文,可以有空格。 推荐写上。
-
Documentation: 服务的文档地址,类似于使用说明。可以是man手册(适用于系统自带的服务), 可以是网页URL(适用于自己写的服务),也可以是本地文件路径。文档地址可以写多个,使用空格分开。 自定义的服务一般不用写这个参数。
-
After/Before: 仅控制启动顺序,不控制依赖关系。我必须要在谁之前/之后启动,但是你是否成功启动与我无关, 我只管启动的先后顺序。
-
Requires=/Wants: 仅控制 依赖关系 ,不控制启动顺序。我的启动需要有你这个服务作为依赖,分为强依赖和弱依赖。systemd 默认并行启动服务,除非你使用( Before/After )显示指定启动顺序。两者可以组合使用,形成 "先启动谁、再启动谁、谁失败会影响谁"。
-
Requires 强依赖: 依赖的服务必须存在且成功启动,否则本服务会被停止。如果你添加了强依赖,那么强依赖 必须 成功运行。这个参数一般不使用。
-
Wants 弱依赖: 依赖的服务(是否存在/是否启动成功)不影响本服务,弱依赖只是作为一个辅助参数, 锦上添花 ,有更好,没有也无妨,一点不影响本服务的启动。存在的意义:表达弱依赖关系,让 systemd 知道这个服务对我很重要,但是你启动失败了也没关系,不影响我的启动。
以 Ubuntu 的防火墙 ufw 为例:
bash
# /usr/lib/systemd/system/ufw.service
[Unit]
# 对 ufw 服务的简洁描述
Description=Uncomplicated firewall
# 文档信息
Documentation=man:ufw(8)
# 我的服务(ufw)在该服务(local-fs)启动之后启动(仅控制启动顺序,不强制服务启动成功)
After=local-fs.target
# 我的服务(ufw)在该服务(network-pre)启动之前启动(仅控制启动顺序,不强制服务启动成功)
Before=network-pre.target
# 这个Requires是我自己加的默认没有,这里做演示
# 表示:"ufw" 服务需要 "local-fs" 服务成功启动,本服务才能启动,是强依赖,本服务的启动强依赖于requires后面的服务。
# 如果 "local-fs" 服务启动失败,那么"ufw" 服务也不会启动成功。(启动时无先后顺序,本服务和Requires后面跟的服务,并行启动。强调requires后跟的服务是否成功。如果成功,则本服务可能成功,也可能失败,如果失败,则本服务,也启动失败)
# Requires=local-fs.target
# 与requires 差不多,这是弱依赖。我需要这两个目标(多个目标用空格分开)达成,但不强求必须成功。即使这两个都是失败了,也不影响我启动。
# 你有更好,没有也无所谓,也不影响我启动。
Wants=network-pre.target local-fs.target
# 是否关掉 systemd 默认给你加的一堆依赖?
# 这个是系统更底层的基础服务。有的系统服务需要添加这个
# 个人自己写的服务一定不要加这个,否则可能会启动失败
DefaultDependencies=no
2.2 [Service] 节(服务运行核心)
定义服务的执行方式,包括启动/停止/重启命令、执行用户、工作目录、重启策略、标准输出 / 错误日志等,是服务能正常运行的核心。必须一个服务要想启动必须要有 [Service] 。
| 配置项 | 作用 | 示例 |
|---|---|---|
Type |
服务类型,决定 systemd 如何判断服务启动成功 | - simple(默认):主进程是前台进程,启动后不退出 - forking:进程会 fork 子进程,父进程退出,适合守护进程 - oneshot:一次性执行(执行完就退出) |
User / Group |
服务运行的用户 / 组 | User=www-data、Group=www-data |
WorkingDirectory |
服务的工作目录 | WorkingDirectory=/opt/myapp |
Environment |
环境变量 | Environment=PATH=/usr/local/bin:/usr/bin |
ExecStart |
必须配置:服务启动的命令(绝对路径) | ExecStart=/usr/bin/python3 /opt/myapp/app.py |
ExecStop |
服务停止时执行的命令 | ExecStop=/usr/bin/kill -9 $MAINPID |
ExecReload |
服务重启 / 重载时执行的命令 | ExecReload=/bin/kill -HUP $MAINPID |
Restart |
重启策略(推荐生产环境配置) | - no(默认,不重启) - on-failure(异常退出时重启) - always(任何退出都重启) |
RestartSec |
重启间隔(秒) | RestartSec=5 |
StandardOutput/StandardError |
日志输出配置 | StandardOutput=journal+console(输出到系统日志 + 控制台) |
bash
# /usr/lib/systemd/system/ufw.service
[Service]
# 服务只需要执行一次,执行完 → 进程退出 → 服务状态变成 inactive (dead)
Type=oneshot
# 与Type=oneshot 组合使用
RemainAfterExit=yes
# 服务启动时,执行的指令
ExecStart=/usr/lib/ufw/ufw-init start quiet
# 服务关闭时执行的指令(一般不用写)
ExecStop=/usr/lib/ufw/ufw-init stop
Type 参数介绍:
| Type类型 | 特点 | 进程状态 | 适用于 |
|---|---|---|---|
| Type=simple | 启动后一直运行(如果不写,默认就是simple) | 常驻内存 | 自定义服务一般都是这个。 或者你不写,使用默认值。 |
| Type=oneshot | 跑一次就退出。执行完 → 进程退出 → 服务状态变成 inactive (dead) | 执行完就死 | 初始化脚本、或者一次性脚本/任务 |
| Type=forking | 程序启动后会自己创建子进程,然后父进程会自己退出,子进程再后台运行。 | 常驻内存 | Nginx、MySQL、Apache、 传统老服务 |
RemainAfterExit 参数介绍:
RemainAfterExit=yes 表示:就算进程退出了,systemd 也认为服务是成功的,不会把它标记为失败。一般是执行一次性任务,与 Type=oneshot 组合使用,只要你的服务是一次性任务,执行完就退出,就用它。
User / Group 参数介绍:
用来给服务设置权限,保证安全!就是当服务启动时,以哪个用户的身份运行这个服务,启动后该服务是谁(User)在运行?所属哪个组(Group)?如果不写,默认都是 root 用户,权限过大,不安全,生产环境中禁止!!一般自己写的服务就写当前使用的用户名(普通用户)就行,不推荐使用 root 。比如:当前用户是:zzq,以用户 zzq 的身份运行这个服务,你可以写:
bash
User=zzq
Group=zzq
另外,由于每个用户都默认属于一个同名的主组,所以你可以只写一个 User ,systemd 会自动把 Group 设成和 User 一样。
WorkingDirectory 参数介绍:
服务的当前工作目录。服务启动前先 cd 进去的目录,所有相对路径,都是相对于它!如果不写,默认是根目录 / 。
比如:使用 Java 运行 myapp.jar 这个 jar 包,这个jar 包会产生日志文件,日志文件存放的根路径就是你定义的 WorkingDirectory 路径。所有文件都是相对于这个路径存放,如果没设置,那么日志文件就会由 root 用户在根路径创建。
通常 myapp.jar 需要文件的绝对路径 /home/ubuntu/myapp.jar :
bash
ExecStart=/usr/bin/java -jar /home/ubuntu/myapp.jar
配置了 WorkingDirectory 可以将 /home/ubuntu 目录省略,直接使用相对路径,更简洁明了:
bash
WorkingDirectory=/home/ubuntu
ExecStart=/usr/bin/java -jar myapp.jar
Environment 参数介绍:
就是给程序设置环境变量。
比如,在终端执行:
bash
java -jar myapp.jar --server.port=8888
写入服务,设置环境变量:
bash
Environment="SERVER_PORT=8888"
ExecStart=/usr/bin/java -jar myapp.jar --server.port=${SERVER_PORT}
ExecStart 参数介绍:
服务启动的指令, 必须配置 ,当你执行 sudo systemctl start ufw 时,systemd 就会跑这一行 ExecStart=/usr/lib/ufw/ufw-init start quiet 。表示的是:启动 ufw 防火墙的脚本,并且安静启动(不输出多余日志),这个更偏向底层,比较复杂,我们换一个简单点的指令进行介绍:
bash
ExecStart=/usr/bin/java -jar /home/ubuntu/myapp.jar
这个指令表示:在服务启动时,使用 java 运行一个叫 myapp.jar 的 jar 包。
注意:
ExecStart后面跟的程序的指令(java)必须是绝对路径,不能是相对路径。 文件的指令可以是相对路径(前提是必须存在WorkingDirectory)。- java 程序的路径:
/usr/bin/java(绝对路径), 参数:-jar,启动的 jar 包:/home/ubuntu/myapp.jar(绝对路径)。 Exec*后面不能跟注释,否则会报错。在运行指令时,systemd 会把ExecStart后面的所有内容当作指令传给可执行程序,注释可能会被当成参数传递,可执行程序检测到不是正确的参数就会报错。
ExecStop 参数介绍:
服务关闭时,要执行的命令。与 ExecStart 类似。ExecStop 通常是给 Type=oneshot 脚本服务使用,常驻服务 Type=simple 不需要写。
ExecReload 参数介绍:
服务重载配置时执行的命令。适用于支持热重载配置的服务(指:不重启、只重新加载配置的服务),不支持就不写 ExecReload 参数,自定义的服务一般不写。比如:nginx 支持热重载配置(nginx -s reload)在 /usr/lib/systemd/system/nginx.service 中就有 ExecReload , 我们平时写的 Java 程序不支持也不需要写 ExecReload。
Restart 参数介绍:
配置重启策略(推荐生产环境配置)。systemd 会根据 Restart= 的值,决定要不要重启。
| 参数值 | 作用说明 | 适用场景 |
|---|---|---|
Restart=no |
默认值 ,服务退出 / 崩溃后,不自动重启 | 一次性脚本、临时任务 |
Restart=on-failure |
服务非正常退出(崩溃、报错、被强制终止、非 0 退出码)时重启 | Java 应用、FRP、Nginx、数据库服务 |
Restart=always |
无论服务是正常退出、崩溃还是被终止,永远自动重启 | 核心业务服务、必须常驻的进程 |
| 以下两个不常用: | ||
Restart=on-success |
仅服务正常退出(退出码 0)时重启,失败则不重启 | 极少使用,适配特殊成功退出场景 |
Restart=on-abnormal |
仅非正常退出 时重启,与on-failure功能基本一致(部分系统下无区别) |
通用服务,替代on-failure |
RestartSec 参数介绍:
与 Restart 搭配使用,服务异常退出后,延迟重启的时间(单位:秒),避免疯狂重启占用资源。RestartSec=3 表示:服务退出后,3秒后重启。
StandardOutput/StandardError 参数介绍:
这两个参数是 systemd 服务配置 中,用于控制服务 标准输出(stdout) 和 标准错误输出(stderr) 重定向方式的核心配置,决定了服务的日志如何存储、如何查看。
| 参数 | 作用 | 取值 | 说明 |
|---|---|---|---|
StandardOutput |
控制标准输出(程序正常打印的日志)的输出方式 | journal(默认) |
日志写入 systemd 日志,可通过 journalctl 查看 |
journal+console |
同时写入日志 + 输出到控制台(仅调试场景) | ||
null |
丢弃所有标准输出,不记录日志 | ||
绝对路径(如 /var/log/app.log) |
输出日志到指定文件 | ||
StandardError |
控制标准错误输出(程序报错、异常信息)的输出方式 | 取值与 StandardOutput 完全一致 |
单独配置错误日志的输出路径 / 方式 |
如果不写,默认是 journal ,使用以下指令查看日志:
bash
journalctl -u myapp.service -f
-u myapp.service:unit,筛选服务,只看这个服务。-f: follow 实时追踪,实时刷新(跟着输出)。
写绝对路径,输出 / 错误分别写入不同文件:
bash
[Service]
ExecStart=/home/ubuntu/myapp.jar
StandardOutput=/var/log/myapp/output.log
StandardError=/var/log/myapp/error.log
2.3 [Install] 节(开机自启配置)
用于配置服务的安装和启用规则,执行 systemctl enable 时生效。如果你想配置开机自启,就加这个 [Install] 。
| 配置项 | 作用 | 示例 |
|---|---|---|
WantedBy |
定义服务在哪个目标(target)下启用,最常用 | WantedBy=multi-user.target(多用户命令行环境,服务器通用) |
RequiredBy |
强依赖的目标 | RequiredBy=graphical.target |
bash
# /usr/lib/systemd/system/ufw.service
[Install]
# 多用户命令行环境,大部分情况下都选这个
WantedBy=multi-user.target
-
WantedBy = 弱依赖安装(推荐 99% 场景)。当系统进入多用户模式(Linux 默认就是多用户模式)时,顺便启动我的服务,我挂了不影响系统。
-
RequiredBy = 强依赖安装(几乎不用)。系统必须等我服务启动成功,才能进入多用户模式,我挂了系统起不来!!
-
它们只在你执行
systemctl enable时生效。
三、如何编写一个 .service 文件?
以 java 程序为例,如何以服务的方式运行 myapp.jar 程序?
创建一个 service 文件 myapp.service:
bash
sudo vim /etc/systemd/system/myapp.service
写入配置文件:
bash
[Unit]
# 服务描述
Description=我的 java 程序服务 myapp.service
# 依赖网络启动后再启动本服务(本地测试,无需连接外网)
After=network.target
# 弱依赖网络就绪
Wants=network-online.target
[Service]
# 服务类型(有默认值,可以不写)
Type=simple
# 运行该服务的用户(修改为你自己的用户名)
User=ubuntu
# 工作目录
WorkingDirectory=/home/ubuntu
# 设置环境变量(例如:JVM参数、应用程序端口(8081端口)等)
Environment="JAVA_OPTS=-Xms512m -Xmx1024m"
Environment="SERVER_PORT=8081"
# 启动命令------绝对路径(修改为你自己的 jar 包名字)
ExecStart=/usr/bin/java ${JAVA_OPTS} -jar myapp.jar --server.port=${SERVER_PORT}
# 停止命令:优雅地终止进程
ExecStop=/usr/bin/kill -TERM $MAINPID
# 异常退出时自动重启
Restart=on-failure
# 重启间隔3秒
RestartSec=3
# 日志输出到系统日志(有默认值,可不写)
StandardOutput=journal
StandardError=journal
[Install]
# 多用户环境下启用(不需要开机自启,则移除此项)
WantedBy=multi-user.target
网络补充:
- network.target :网络初始化完成,网卡亮了,但 IP 还没拿到,不能上网!相当于自己windows 电脑没有网络,但是可以使用mysql, http:localhost:8080 等。
- network-online.target :网络真正准备好了(IP 拿到、DNS 正常、能上网)。相当于自己windows电脑连接了网络,可以使用远程服务器上的 OSS 对象存储,MySQL 数控库等。 适用于生产环境 。
- 使用那一个? 如果仅仅使用本地的网络服务,就使用 network.target , 如果需要连外部服务(OSS, Redis, MQ, MySQL等)使用 network-online.target 。
省略非必要参数,最基础的启动 java 程序服务:
bash
[Service]
ExecStart=/usr/bin/java -jar /home/ubuntu/myapp.jar
四、如何使用 .service 文件?
修改 / 新增 .service 文件后,必须重载 systemd 配置,让系统识别新服务:
bash
sudo systemctl daemon-reload
启动服务:
bash
# sudo systemctl start myapp.service
# 可以简写为myapp(省略 .service)
sudo systemctl start myapp
查看服务状态:
bash
sudo systemctl status myapp
设置开机自启(service 文件中必须有[Install]):
bash
sudo systemctl enable myapp
禁止开机自启:
bash
sudo systemctl disable myapp
停止服务:
bash
sudo systemctl stop myapp
重启服务:
bash
sudo systemctl restart myapp
查看服务日志:
bash
# 查看当前服务日志
sudo journalctl -u myapp
# 实时跟踪日志(类似 tail -f)
sudo journalctl -u myapp -f
# 查看最近10分钟的日志
sudo journalctl -u myapp --since "10 minutes ago"
End
你好,少年,未来可期~