Ubuntu开机自启服务systemd.service配置教程(Ubuntu服务)(Linux服务)upstart(systemd教程)
1. Ubuntu 开机自启服务踩坑记从 upstart 到 systemd.service 的完整迁移如果你在 Ubuntu 上写了个脚本或者编译了个二进制程序想让它开机就跑起来、挂了还能自己拉起来那 systemd.service 就是绕不开的一环。我见过太多人第一次配服务时踩的坑把 .service 文件丢进/etc/systemd/system/的子目录里systemctl enable直接报Unit file xxx.service does not exist或者用软链接方式配好了disable的时候发现源文件被删了再或者服务明明active了但程序就是没跑起来日志里全是status203/EXEC。这篇就聚焦 Ubuntu 下 systemd.service 开机自启的完整配置流程顺带把 upstart 旧方式和 rc.local 的兼容问题讲清楚。适合谁看手里有 Python 脚本、Go 二进制、Java jar 包或者 shell 脚本想让它在 Ubuntu 16.10 及以上版本开机自启、后台常驻、异常自动重启的开发者。读完你能拿到一份可直接复制的 .service 模板知道enable/start/status每一步在干什么也能自己排查启动依赖和日志问题。先说版本分界线这个决定了你该用哪套方案。Ubuntu 14.04 及更早版本用的是 upstart 作为默认 init 系统配置文件放/etc/init/目录下.conf后缀同时还有/etc/rc.local这种老式开机脚本。Ubuntu 15.04 到 16.04 是过渡期默认切到了 systemd但仍然兼容 upstart。Ubuntu 16.10 及更高版本包括现在的 20.04、22.04、24.04默认且唯一推荐的就是 systemd。所以如果你在 16.10 之后的系统上还去折腾 upstart 的.conf文件基本是白费功夫systemd 根本不读那个目录。为什么非要把程序配成服务而不是直接nohup ./app 完事六个实际好处开机自动启动系统重启后不用手动登录去拉后台运行不占终端你 SSH 断开也不影响异常退出能自动重启配合Restartalways和RestartSec就是简易守护进程用systemctl统一管理启停查状态一条命令能声明依赖关系比如等网络就绪后再启动日志自动进 journaldjournalctl -u 服务名直接看不用自己重定向到文件再tail -f。我试过在一个边缘设备上把视频转码程序配成服务程序本身是 C 编译的二进制放在/ky/tml/ky_ai_convert_video/下。最开始图省事直接./kyai_video_converter 结果设备断电重启后程序没了得重新 SSH 上去手动拉。后来配成 systemd 服务Restartalways加上RestartSec3手动kill掉进程三秒后自己就起来了重启设备也自动跑省心很多。2. TaoToken 前置准备API Key 与接入信息获取在正式写 .service 文件之前如果你的服务本身需要调用大模型 API比如做视频转码后要调模型生成摘要、或者跑一个常驻的 Agent 服务那得先把接入信息准备好。TaoToken 的 API 地址是https://taotoken.net/api这个地址不加任何 UTM 参数直接用于代码里的 Base URL。获取 API Key 的路径打开https://taotoken.net/api-keys登录后在控制台里创建 Key。这个 Key 就是你在服务里配置的鉴权凭证通常以sk-开头。拿到之后不要硬编码在 .service 文件里明文暴露推荐用EnvironmentFile引入一个单独的 env 文件权限设成 600只有 root 能读。如果你要验证模型是否可用可以先去模型对话页面https://taotoken.net/model-chat手动发一条消息测试确认 Key 有效、模型能正常返回。这一步能帮你排除掉「服务配好了但 API 调不通」的混淆情况——到底是 systemd 的问题还是 Key 的问题先分开验证。对于长期跑编码任务或者 Agent 的服务比如一个常驻的代码补全后端可以考虑 Coding Plan地址是https://taotoken.net/coding-plan。控制台入口在https://taotoken.net/console接入文档在https://taotoken.net/doc。如果你用的是 Claude Code 这类工具做服务化封装Anthropic 兼容接入的说明在https://taotoken.net/claudecode-anthropic。这里要强调一个原则systemd 负责的是「进程怎么起、什么时候起、挂了怎么办」它不负责你的程序内部逻辑。所以 API Key、Base URL、Model ID 这些是程序配置层面的事.service 文件只负责把环境变量传进去、把工作目录设对、把启动命令写对。两者别混在一起排查。3. 可复制配置systemd.service 模板与 upstart 对比先给一份可以直接改的 .service 模板以视频转码服务为例文件名ky_ai_convert_video.service[Unit] Descriptionky_ai_convert_video service Afternetwork.target Wantsnetwork-online.target [Service] Typesimple Userroot Grouproot WorkingDirectory/ky/tml/ky_ai_convert_video EnvironmentFile-/etc/ky_ai_convert_video.env ExecStart/ky/tml/ky_ai_convert_video/kyai_video_converter Restartalways RestartSec3 StandardOutputjournal StandardErrorjournal [Install] WantedBymulti-user.target配套的 env 文件/etc/ky_ai_convert_video.env权限 600TAOTOKEN_API_KEYsk-你的实际Key TAOTOKEN_BASE_URLhttps://taotoken.net/api TAOTOKEN_MODEL_ID你的模型ID如果你用的是 JSON 配置的客户端比如某些 Node 服务读settings.json可以这样写{ baseUrl: https://taotoken.net/api, apiKey: sk-你的实际Key, model: 你的模型ID }TOML 格式的客户端配置[llm] base_url https://taotoken.net/api api_key sk-你的实际Key model 你的模型ID三件套记牢Base URL 填https://taotoken.net/apiKey 填你创建的sk-开头凭证Model ID 填你实际要调的模型标识。这三个在 Cline MCP、CC Switch、Codex 的auth.json里都是同样的填法只是字段名不同。现在对比 upstart 旧方式。upstart 的配置文件放/etc/init/my_service.conf内容长这样start on runlevel [2345] stop on runlevel [!2345] respawn exec /path/to/your/script.shstart on runlevel [2345]表示进入 2、3、4、5 运行级别时启动respawn表示进程挂了自动重启。这套在 Ubuntu 14.04 上能用但 16.10 之后 systemd 不读/etc/init/目录你写了也不生效。所以新系统一律用 systemd。rc.local 方式也提一下。老版本直接在/etc/rc.local里写命令加可执行权限就行。新版本 rc.local 默认被禁用需要创建/etc/systemd/system/rc-local.service[Unit] Description/etc/rc.local Compatibility ConditionPathExists/etc/rc.local [Service] ExecStart/etc/rc.local start Typeforking TimeoutSec0 RemainAfterExityes [Install] WantedBymulti-user.target然后chmod x /etc/rc.localsystemctl enable rc-local.service。但这是兼容旧脚本的过渡方案新服务不建议这么写因为 rc.local 里的命令没有独立的日志单元、没有依赖声明、没有自动重启策略排查起来很痛苦。关于文件放置位置这里有个大坑。systemd 官方说会递归遍历/etc/systemd/system/下的子目录但实测下来在子目录里放 .service 文件或者软链接systemctl enable都会报Failed to enable unit: Unit file xxx.service does not exist。测试了三种情况子目录里建软链接失败子目录里直接拷贝 .service 文件失败不建子目录直接在/etc/systemd/system/下建软链接成功。所以结论很明确.service 文件或者它的软链接必须直接放在/etc/systemd/system/根目录下别放子目录。软链接方式还有个注意点systemctl disable时如果/etc/systemd/system/下是软链接disable 会把软链接删掉源文件还在如果是普通文件拷贝disable 只删除default.target.wants/下的启用链接/etc/systemd/system/下的 .service 文件本身不会被删。另外软链接名必须和目标文件名一致否则 enable 时报Link has been severed。4. 验证请求enable/start/status 与日志排查配置写好后按顺序执行。第一步把 .service 文件放到/etc/systemd/system/下设置权限sudo chown root:root /etc/systemd/system/ky_ai_convert_video.service sudo chmod 644 /etc/systemd/system/ky_ai_convert_video.service第二步重载 systemd 配置让它识别新文件sudo systemctl daemon-reload第三步启用开机自启sudo systemctl enable ky_ai_convert_video.service成功时输出类似Created symlink /etc/systemd/system/multi-user.target.wants/ky_ai_convert_video.service → /etc/systemd/system/ky_ai_convert_video.service.说明启用链接建好了。第四步立即启动服务sudo systemctl start ky_ai_convert_video.service第五步查看状态sudo systemctl status ky_ai_convert_video.service --no-pager--no-pager很重要不加的话日志太长会进分页模式卡住你的脚本执行。正常输出里Active: active (running)表示在跑下面会显示主进程 PID 和最近几行日志。第六步确认开机自启已启用sudo systemctl is-enabled ky_ai_convert_video.service返回enabled就对了。再确认运行状态sudo systemctl is-active ky_ai_convert_video.service返回active。看日志用 journalctlsudo journalctl -u ky_ai_convert_video.service --no-pager -n 50-n 50显示最近 50 条-f实时跟踪--since 2024-01-01 --until 2024-01-02按时间范围筛。如果程序自己还写了日志文件比如ky_ai_api.log可以tail -f ky_ai_api.log配合看。验证自动重启先ps -ef | grep convert找到进程 PIDkill pid杀掉等三秒再ps -ef | grep convert发现进程又起来了说明Restartalways和RestartSec3生效了。重启设备测试sudo reboot起来后直接systemctl status看是否自动运行。如果服务需要调 TaoToken API验证请求是否通在服务日志里找 API 调用记录或者手动在服务的工作目录下用同样的环境变量跑一次 curlcurl -X POST https://taotoken.net/api/v1/chat/completions \ -H Authorization: Bearer $TAOTOKEN_API_KEY \ -H Content-Type: application/json \ -d {model:你的模型ID,messages:[{role:user,content:ping}]}返回正常 JSON 说明 Key 和网络都没问题那 systemd 服务里调不通就是环境变量没传进去或者工作目录不对。5. 本篇常见错排查401、203/EXEC、Unit file does not exist报错一Failed to enable unit: Unit file xxx.service does not exist.这个前面说过九成是把 .service 文件放进了/etc/systemd/system/的子目录。systemd 虽然文档说递归遍历但 enable 操作实际只在根目录找。解决把文件或软链接直接放到/etc/systemd/system/下然后daemon-reload再 enable。软链接的话检查链接名和目标名是否一致不一致会报Link has been severed。报错二status203/EXEC服务启动失败。这是 ExecStart 指定的可执行文件找不到或者没有执行权限。检查三点路径是不是绝对路径systemd 不认相对路径文件有没有x权限chmod x加上如果是脚本shebang 行#!/bin/bash写对没有。另外 WorkingDirectory 设了但目录不存在也会出问题。报错三服务active (running)但程序实际没干活。常见原因是 Type 设错了。比如你的程序是 fork 到后台的守护进程父进程启动后立即退出这时候 Type 应该用forking用simple的话 systemd 会认为主进程退出了服务就结束了。反过来前台常驻的程序用forking也会出问题。判断方法看你的程序启动后是不是占着终端不返回是的话用simple启动后立刻返回、后台有个子进程在跑用forking。报错四API 返回 401。这个跟 systemd 无关是 Key 的问题。检查 EnvironmentFile 里的TAOTOKEN_API_KEY是不是sk-开头、有没有多余空格或换行、env 文件权限是不是 600 且 root 可读。如果服务用User指定了非 root 用户env 文件该用户读不到也会导致变量为空进而 401。解决要么 env 文件权限放开到该用户可读要么把变量直接写在[Service]段的Environment里不推荐明文暴露。报错五local proxy failed或连接超时。检查 Base URL 是不是写成了https://taotoken.net/api别多加斜杠或者写成别的路径。网络依赖方面Afternetwork.target只保证网络栈初始化了不保证真的能出网。需要出网的服务建议加Wantsnetwork-online.target和Afternetwork-online.target确保网络真正就绪后再启动。报错六reading choices之类的 JSON 解析错误。这通常是 API 返回了非预期格式比如返回了错误页 HTML 而不是 JSON。先手动 curl 确认接口返回正常再检查程序里解析逻辑。如果服务日志里能看到完整响应体直接看响应内容最快。排查通用思路先systemctl status看服务状态和最近日志再journalctl -u 服务名 -n 100看详细日志然后手动用同样的命令和环境变量在 shell 里跑一遍对比差异。shell 里能跑通、服务里跑不通基本就是环境变量、工作目录、用户权限这三个差异点。6. 语义一致 CTA接入文档与模型验证入口服务配好之后如果你的程序需要调模型能力接入文档在https://taotoken.net/doc里面有各语言的调用示例和参数说明。API Key 管理在https://taotoken.net/api-keys可以创建多个 Key 给不同服务用方便轮换和隔离。想先手动验证模型通不通去https://taotoken.net/model-chat发条消息试试。长期跑编码类或 Agent 类常驻服务Coding Plan 在https://taotoken.net/coding-plan控制台总入口https://taotoken.net/console。Claude Code 相关的 Anthropic 兼容接入看https://taotoken.net/claudecode-anthropic。最后补一个实用技巧改完 .service 文件后一定要systemctl daemon-reload再restart不然 systemd 用的还是旧配置。改 env 文件的话restart服务即可env 是启动时读取的。想看服务到底加载了哪些环境变量systemctl show ky_ai_convert_video.service -p Environment可以查。日志占空间的话journalctl --vacuum-size100M清理到只剩 100M。这些命令配服务的时候顺手记一下排障能省不少时间。