开机自动获取 IP 并通过 Bark 推送到手机
有些设备没有固定显示器,或者经常断电重启。机器重新启动之后,第一件事通常不是看日志,而是先确认它当前拿到了什么 IP,能不能连上。
这篇文章记录一套比较实用的方案:设备开机后自动等待网络初始化,获取 IPv4 地址,然后通过 Bark 推送到手机。这样机器一启动,手机上就能收到类似下面这样的通知:
主机 nvidia 已开机,IP: 192.168.2.89
本文方案基于 systemd + NetworkManager + Bark,适合 Ubuntu / Debian 一类使用 systemd 的 Linux 机器。
方案目标
目标很明确:
- 开机后自动执行,不需要手工登录
- 等网卡拿到 IP 之后再发送,不抢跑
- 推送失败时自动重试
- 尽量减少对开机流程的干扰
- 日志可追踪,方便排障
Bark 简介
Bark 是一个给 iPhone 推送通知的服务。只要拿到自己的 Bark 推送地址,就可以通过 HTTP 请求直接发消息。
例如:
https://bark.example.com/xxxxxxxxxxxxx/推送标题/推送内容
其中:
xxxxxxxxxxxxx是设备 key- 后面的标题和内容需要做 URL 编码
脚本思路
脚本的逻辑分成三步:
- 先等网络在线
- 再轮询指定网卡的 IPv4 地址
- 拿到 IP 后调用 Bark 接口推送
为了减少依赖,发送部分不依赖 curl,而是直接使用系统自带的 python3 标准库完成 HTTP 请求。
开机推送脚本
脚本路径:
/home/nvidia/.local/bin/boot-ip-bark.sh
脚本内容如下:
#!/usr/bin/env bash
set -euo pipefail
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
BARK_BASE="https://bark.example.com/xxxxxxxxxxxxxxxxxxxxxx"
TITLE="开机IP通知"
INTERFACE="${INTERFACE:-enP2p1s0}"
NM_WAIT_TIMEOUT="${NM_WAIT_TIMEOUT:-180}"
MAX_WAIT="${MAX_WAIT:-300}"
SLEEP_SEC="${SLEEP_SEC:-3}"
BARK_RETRIES="${BARK_RETRIES:-3}"
BARK_RETRY_SLEEP="${BARK_RETRY_SLEEP:-5}"
wait_for_network() {
if command -v nm-online >/dev/null 2>&1; then
nm-online -q -t "$NM_WAIT_TIMEOUT" || true
fi
}
get_ip() {
/usr/sbin/ip -4 -o addr show dev "$INTERFACE" scope global 2>/dev/null | awk '{split($4,a,"/"); print a[1]; exit}'
}
wait_for_network
ip_addr=""
for _ in $(seq 1 "$MAX_WAIT"); do
ip_addr="$(get_ip || true)"
if [ -n "$ip_addr" ]; then
break
fi
sleep "$SLEEP_SEC"
done
if [ -z "$ip_addr" ]; then
ip_addr="$(hostname -I 2>/dev/null | awk '{print $1}')"
fi
if [ -z "$ip_addr" ]; then
logger -t boot-ip-bark "no IPv4 address found"
exit 1
fi
host="$(hostname 2>/dev/null || echo unknown)"
content="主机 ${host} 已开机,IP: ${ip_addr}"
for attempt in $(seq 1 "$BARK_RETRIES"); do
if /usr/bin/python3 - "$BARK_BASE" "$TITLE" "$content" <<'PY'
import sys
from urllib.parse import quote
from urllib.request import Request, urlopen
base, title, content = sys.argv[1:4]
url = f"{base}/{quote(title, safe='')}/{quote(content, safe='')}"
req = Request(url, headers={"User-Agent": "boot-ip-bark/1.0"})
with urlopen(req, timeout=10) as resp:
resp.read()
PY
then
logger -t boot-ip-bark "sent bark notification for ${host} ${ip_addr}"
exit 0
fi
sleep "$BARK_RETRY_SLEEP"
done
logger -t boot-ip-bark "failed to send bark notification for ${host} ${ip_addr}"
exit 1
systemd 配置
相比 crontab @reboot,systemd 更适合做这种开机任务:
- 启动顺序可控
- 日志统一进
journalctl - 失败后可自动重试
- 方便查看状态
服务文件路径:
/etc/systemd/system/boot-ip-bark.service
内容如下:
[Unit]
Description=Send boot IP notification to Bark
Wants=NetworkManager-wait-online.service
After=NetworkManager-wait-online.service NetworkManager.service network-online.target
[Service]
Type=oneshot
User=nvidia
Group=nvidia
Environment=INTERFACE=enP2p1s0
Environment=NM_WAIT_TIMEOUT=180
Environment=MAX_WAIT=300
Environment=SLEEP_SEC=3
Environment=BARK_RETRIES=3
Environment=BARK_RETRY_SLEEP=5
ExecStart=/home/nvidia/.local/bin/boot-ip-bark.sh
Restart=on-failure
RestartSec=20s
TimeoutStartSec=25min
Nice=10
IOSchedulingClass=idle
[Install]
WantedBy=multi-user.target
启用命令:
sudo systemctl daemon-reload
sudo systemctl enable NetworkManager-wait-online.service
sudo systemctl enable boot-ip-bark.service
sudo systemctl start boot-ip-bark.service
验证方式
可以用下面几个命令检查是否正常:
查看服务状态:
systemctl status boot-ip-bark.service
查看本次开机日志:
journalctl -b -u boot-ip-bark.service
查看脚本自己的日志:
journalctl -b -t boot-ip-bark
如果推送成功,通常能看到类似日志:
sent bark notification for agile-nvidia 10.8.26.89
关键细节和踩坑
这套配置里,最容易踩的坑主要有几个。
1. 不要只靠 @reboot
cron 的 @reboot 触发时机通常太早,而且环境变量很精简。脚本里经常会出现命令找不到、网络还没起来、IP 为空等问题。
2. curl 不一定存在
很多最小化系统没有安装 curl。如果脚本依赖 curl,开机时就可能直接失败。用 python3 标准库可以减少一个不必要依赖。
3. network-online.target 不等于一定拿到业务 IP
很多人会误以为只要依赖了 network-online.target 就可以直接推送。实际上不一定。最稳妥的做法仍然是脚本里自己再确认一遍指定网卡的 IPv4。
4. 这类服务可能拉长启动时间
如果机器没插网线、DHCP 很慢,等待网络的服务可能会拖长 boot 时间。它通常不会让系统完全无法进入,但会延长启动链路。
如果你的目标是“任何情况下都不能影响开机”,更适合改成:
systemd timer延迟执行- 或者启动后异步执行,而不是挂在关键启动链路上
适合什么场景
这套方案特别适合下面这些场景:
- 小主机、工控机、NAS、开发板
- 没有显示器常驻连接的设备
- 经常断电重启的实验机
- 远程维护的 Linux 设备
尤其是 DHCP 环境下,设备每次启动 IP 都可能变化,手机能第一时间收到新地址,维护效率会高很多。
总结
如果你只是想实现“开机后把当前 IP 推送到手机”,核心并不复杂,真正难的是把它做得稳定。
一套可用的实现至少要满足这几点:
- 用
systemd管理,而不是单纯依赖cron - 明确等待网络和等待 IP 的逻辑
- 减少外部依赖
- 保留日志,方便排错
这样机器重启后,你不用先猜它拿到了哪个地址,手机上直接就能收到通知。
- 感谢你赐予我前进的力量

