1. Jetson Orin NX 16G 开机自启三个 AI 服务到底难在哪
Jetson Orin NX 16G 这块板子做边缘推理很合适,16G 统一内存既能跑 Ollama 拉起来的量化模型,也能同时挂一个 llama.cpp 的 llama-server,再叠一层 OpenClaw Gateway 做统一入口。但真正让人头疼的不是模型跑不跑得动,而是断电重启之后,这三个服务谁先谁后、端口有没有就绪、Gateway 会不会因为 Ollama 还没起来就直接退出。我试过手动nohup挂后台,结果一次意外断电后全没了,第二天到现场发现设备是「开机了但服务没起」的状态。
这篇就聚焦一件事:用 systemd 把 Ollama、llama-server、OpenClaw Gateway 串成一条有依赖顺序的启动链,做到无人值守开机自启。核心检索词就是 Jetson Orin NX 上 Ollama、llama-server、OpenClaw Gateway 的 systemd 开机自启编排。适合谁看?手上有一台 Orin NX 16G、已经能手动跑起这三个组件、现在想把它们变成「插电即用」的边缘 AI 盒子的人。如果你还在纠结模型怎么下载、CUDA 怎么装,那得先把那步走完,这篇假设三个二进制都已经能手动执行。
难点其实有三个。第一是依赖顺序:OpenClaw Gateway 启动时会去连 Ollama 和 llama-server 的本地端口,如果这俩还没监听,Gateway 要么报错退出,要么进入一个半死不活的状态。第二是端口就绪:systemd 的After=只保证启动顺序,不保证「对方真的在监听端口」,所以需要配合重启策略或者健康探测。第三是权限与路径:Ollama 官方安装脚本会建一个ollama用户,模型目录如果换到/srv下,得确保这个用户有读写权限,否则服务起来但加载模型失败。
我踩过的坑是:一开始只写了After=ollama.service,结果 Ollama 进程起来了但模型还没加载完,Gateway 去请求就超时。后来改成Restart=always加RestartSec=3,让 Gateway 自己重试,反而比死等更稳。下面按「先配 Ollama override → 再配 llama-server → 再配 Gateway → 最后绑 target」的顺序来,每一步都给可直接复制的 unit 文件。
2. TaoToken 前置:把 API Key 和接入文档先备好
在动手写 systemd 之前,有个前置动作建议先做掉:把 TaoToken 的 API Key 和接入文档准备好。原因很实际——OpenClaw Gateway 这类网关组件,很多时候不只是转发本地 llama-server,还会挂一个云端模型作为兜底或者做路由。你本地 3B 模型答不了的复杂问题,可以让 Gateway 转发到云端大模型,这样边缘设备的能力上限就不被板子算力锁死了。
TaoToken 在这里的角色是提供 OpenAI 兼容的 API 入口,你拿到 Key 之后,在 OpenClaw 的配置里填 Base URL、Key、Model ID 三件套就能接上。具体操作:打开 API Keys 页面生成一个 Key,然后对照接入文档确认 Base URL 和模型名。Base URL 用https://taotoken.net/api,不要带任何多余路径。模型 ID 按文档里列出的写,别自己猜。
这里要提醒一句:Key 不要写死在openclaw.json里然后提交到 git,也不要用弱口令。建议放到/etc/default/openclaw-gateway这种权限 600 的环境文件里,systemd 通过EnvironmentFile=读进去。这样即使配置文件被看到,Key 也不会泄露。生成 Key 的时候选一个足够长的随机串,别用123456这种。
如果你只是想让本地三个服务跑起来、暂时不接云端,这一步可以跳过,但建议还是先把 Key 备着,后面想加路由时不用再回头折腾。接入文档里通常会写清楚请求格式、鉴权头怎么写、有哪些模型可选,照着填就行。把 Key 和文档链接存到一个安全的地方,下一步配 Gateway 的时候会用到。
3. 可复制配置:三个 unit 文件加一个 target
这一节是全文的核心,所有文件都可以直接复制,只需要替换三个占位符:<JETSON_USER>(登录用户名,比如 ubuntu)、<JETSON_HOME>(比如 /home/ubuntu)、<LLAMA_MODEL>(gguf 模型绝对路径)。先替换再保存,别原样贴进去。
3.1 Ollama:用 override.conf 而不是改官方 unit
Ollama 官方安装脚本已经建好了ollama.service,不要直接改它,用 override 的方式叠加配置,这样升级 Ollama 时你的定制不会被覆盖。新建/etc/systemd/system/ollama.service.d/override.conf:
[Service] Environment="OLLAMA_HOST=127.0.0.1:11434" Environment="OLLAMA_CONTEXT_LENGTH=4096" Environment="OLLAMA_KEEP_ALIVE=10m" Environment="OLLAMA_MODELS=/srv/ollama/models"OLLAMA_HOST绑到 loopback,避免暴露到局域网;OLLAMA_CONTEXT_LENGTH固定上下文长度,防止不同请求把显存吃爆;OLLAMA_KEEP_ALIVE=10m让模型在空闲 10 分钟内常驻,减少反复加载。模型目录换到/srv/ollama/models后,必须把属主给ollama用户:
sudo mkdir -p /srv/ollama/models sudo chown -R ollama:ollama /srv/ollama/models这一步漏了的话,服务能起来但ollama pull或加载模型会报权限错误。
3.2 llama-server:系统级服务加 Jetson 保守参数
llama.cpp 的 llama-server 是个轻量 HTTP 服务,支持 OpenAI 兼容 API。新建/etc/systemd/system/llama-server.service:
[Unit] Description=llama.cpp server After=network-online.target Wants=network-online.target [Service] Type=simple User=<JETSON_USER> Group=<JETSON_USER> WorkingDirectory=<JETSON_HOME> Environment="PATH=/usr/local/bin:/usr/bin:/bin" Environment="GGML_CUDA_ENABLE_UNIFIED_MEMORY=1" ExecStart=<JETSON_HOME>/src/llama.cpp/build/bin/llama-server \ -m <LLAMA_MODEL> \ --alias qwen2.5-3b-instruct-gguf \ --host 127.0.0.1 \ --port 8080 \ -c 4096 \ -np 1 \ -ctk q8_0 \ -ctv q8_0 Restart=always RestartSec=3 LimitNOFILE=65535 [Install] WantedBy=multi-user.target几个参数说明:--alias给模型一个固定 ID,OpenClaw 对接时用这个 ID 而不是路径;-c 4096上下文长度和 Ollama 保持一致;-np 1单并发,Jetson 上别贪多;-ctk q8_0 -ctv q8_0把 KV cache 量化到 8 位,省显存;GGML_CUDA_ENABLE_UNIFIED_MEMORY=1是 llama.cpp 在 Linux 上的统一内存开关,显存不够时回退到系统内存而不是直接崩。Restart=always保证进程挂了自动拉起。
3.3 OpenClaw Gateway:系统级服务加依赖声明
OpenClaw 官方默认给的是 user service,但在 always-on 设备上改成 system service 更合适。先确认~/.openclaw/openclaw.json里 gateway 段有mode: "local",否则 Gateway 会拒绝启动:
{ "gateway": { "mode": "local", "bind": "loopback", "port": 18789, "auth": { "token": "REPLACE_WITH_A_LONG_RANDOM_TOKEN" } } }然后新建/etc/systemd/system/openclaw-gateway.service:
[Unit] Description=OpenClaw Gateway After=network-online.target ollama.service llama-server.service Wants=network-online.target ollama.service llama-server.service [Service] Type=simple User=<JETSON_USER> Group=<JETSON_USER> WorkingDirectory=<JETSON_HOME> Environment="HOME=<JETSON_HOME>" Environment="PATH=/usr/local/bin:/usr/bin:/bin" EnvironmentFile=-/etc/default/openclaw-gateway ExecStart=/usr/bin/env openclaw gateway --bind loopback --port 18789 Restart=always RestartSec=3 LimitNOFILE=65535 [Install] WantedBy=multi-user.targetAfter=和Wants=同时声明 Ollama 和 llama-server,保证启动顺序。EnvironmentFile=-前面的减号表示文件不存在也不报错。token 放环境文件:
sudo tee /etc/default/openclaw-gateway >/dev/null <<'EOF' OPENCLAW_GATEWAY_TOKEN=REPLACE_WITH_A_LONG_RANDOM_TOKEN EOF sudo chmod 600 /etc/default/openclaw-gateway3.4 可选:绑成一个 target 统一管理
新建/etc/systemd/system/jetson-ai-stack.target:
[Unit] Description=Jetson Local AI Stack Wants=ollama.service llama-server.service openclaw-gateway.service After=ollama.service llama-server.service openclaw-gateway.service [Install] WantedBy=multi-user.target以后systemctl start jetson-ai-stack.target就能一起拉起三个服务。
3.5 一次性落盘并启用
把占位符替换后,执行:
sudo systemctl daemon-reload sudo systemctl enable --now ollama sudo systemctl enable --now llama-server sudo systemctl enable --now openclaw-gateway sudo systemctl enable jetson-ai-stack.targetenable --now同时做两件事:设置开机自启 + 立即启动。到这里配置就落盘了。
4. 验证请求:systemctl 状态与端口探测
配完不算完,得验证启动链真的通了。先看三个服务的状态:
sudo systemctl status ollama --no-pager sudo systemctl status llama-server --no-pager sudo systemctl status openclaw-gateway --no-pager正常应该是active (running)。如果哪个是failed,先看日志:
journalctl -u ollama -e --no-pager journalctl -u llama-server -e --no-pager journalctl -u openclaw-gateway -e --no-pager然后做端口探测,确认服务真的在监听:
curl http://127.0.0.1:11434/api/tags curl http://127.0.0.1:8080/v1/models第一个返回 Ollama 已加载的模型列表,第二个返回 llama-server 的模型信息。如果 curl 报Connection refused,说明服务没起来或者端口不对。OpenClaw 这边用官方 CLI 检查:
openclaw status openclaw gateway status --require-rpc openclaw doctor openclaw channels status --probe--require-rpc会强制要求 RPC 真正可用,比单纯看进程状态更严格。openclaw doctor会做一轮自检,把配置问题列出来。
最后做一次真实的重启验证,这是最关键的一步:
sudo reboot等设备起来后,重新 SSH 上去,直接跑:
systemctl is-active ollama llama-server openclaw-gateway三个都返回active才算开机自启成功。再 curl 一次两个本地端口,确认模型服务也恢复了。如果 Gateway 是active但 RPC 不通,多半是它启动时 Ollama 还没加载完模型,Restart=always会在几秒后重试,等 10 秒再查一次通常就好了。
远程访问 Dashboard 的话,在笔记本上开 SSH 隧道:
ssh -N -L 18789:127.0.0.1:18789 <JETSON_USER>@<JETSON_IP>然后浏览器打开http://127.0.0.1:18789/。因为 Gateway 绑的是 loopback,只能通过隧道访问,这样比直接暴露端口安全。
5. 本篇常见错排查:401、local proxy failed、reading choices、OAuth
这一节把几个高频报错对照着讲,都是实际会撞上的。
401 Unauthorized:出现在 OpenClaw 请求云端模型时。原因通常是 token 没读到或者写错了。检查/etc/default/openclaw-gateway里的OPENCLAW_GATEWAY_TOKEN和openclaw.json里的auth.token是否一致。注意 systemd 读环境文件是在服务启动时,改完要sudo systemctl restart openclaw-gateway。如果你接的是 TaoToken 的云端 API,401 还可能是 Key 失效或 Base URL 写错,确认 Base URL 是https://taotoken.net/api,Key 从 API Keys 页面重新生成一个。
local proxy failed:Gateway 转发到本地 llama-server 或 Ollama 时连不上。先curl http://127.0.0.1:8080/v1/models确认 llama-server 活着,再确认 Gateway 配置里指向的端口和--port 8080一致。如果 llama-server 是active但 curl 不通,看日志里有没有failed to bind或者模型加载失败。模型路径写错、gguf 文件损坏都会导致进程起来但端口不监听。
reading choices 相关报错:一般是请求返回体不是预期的 OpenAI 格式,Gateway 解析失败。常见于 llama-server 的--alias没设,或者请求里 model 字段填的 ID 和 alias 对不上。确认--alias qwen2.5-3b-instruct-gguf和 Gateway 配置里的 model ID 完全一致,大小写敏感。
OAuth 报错:OpenClaw 某些渠道需要 OAuth 授权,如果 token 过期或者授权文件路径不对会报这个。检查HOME环境变量是否指向了正确的家目录,OAuth 凭证通常存在~/.openclaw/下。systemd 服务里Environment="HOME=<JETSON_HOME>"这行不能少,否则服务以 root 或错误的家目录去找凭证,必然失败。
还有一个隐蔽的坑:After=ollama.service只保证顺序,不保证 Ollama 模型加载完成。如果 Gateway 启动太快,第一次请求会失败,靠Restart=always重试。想更稳可以给 Gateway 加ExecStartPre=/bin/sleep 5,但这是权宜之计,不如让重试机制兜底。
6. 长期跑边缘 AI 服务,把 Key 和接入方式固定下来
设备配好只是开始,长期无人值守跑下去,真正省心的是把接入方式固定成一套标准。本地三个服务负责低延迟推理,云端 API 负责兜底和复杂任务,OpenClaw Gateway 做统一路由,这套结构在 Orin NX 16G 上跑得挺稳。TaoToken 的 API Key 建议单独存一份,别和本地配置混在一起,换设备或者扩容时直接复用。
如果你后面要接更多模型或者做多设备管理,可以走 Coding Plan 那条线,把编码和 Agent 类的长期任务也纳进来。验证模型连通性的时候,模型对话页面能快速确认 Key 和 Base URL 是否配对成功,比在设备上反复 curl 省事。接入文档里对请求格式和鉴权头写得比较细,遇到 401 或者格式问题时对着查一遍基本能定位。
最后留一个实用习惯:每次改完 unit 文件,先systemctl daemon-reload再 restart,别直接 restart,否则改动不生效。重启验证至少做一次真实reboot,别只用systemctl restart糊弄自己——开机自启这件事,只有真重启过才算数。