自托管位置跟踪应用怎么选:Traccar、OwnTracks、Dawarich 对比
对比 Traccar、OwnTracks、Dawarich 和 Home Assistant:分别适合实时共享、手机上报、个人位置历史与自动化,并说明为何不能把数据接收端点直接暴露在公网。
哪款自托管位置跟踪应用适合您的情况
自托管位置跟踪取决于一个问题:您是要为自己保留位置历史,还是要与其他人一起查看实时位置?Dawarich 适合前者。它是 Google Maps Timeline 的自托管替代方案,并且可以导入您已有的位置历史。Traccar 适合后者。它最初为 GPS 硬件和车队设计,也将手机视为一种跟踪设备。OwnTracks 则作为手机应用和消息格式,为这两种方案提供底层支持。如果您运行 Home Assistant,它已经知道手机的位置,但默认只保留 10 天的位置轨迹。
请根据您下个月想查看的内容进行选择:
- 去年 3 月去过的所有地点和行程地图:Dawarich。
- 显示多个设备、地理围栏和事件规则的实时地图:Traccar。
- 让一部手机将位置发送到您已经运行的系统:使用 HTTP 模式的 OwnTracks。
- 仅用于在家和外出自动化,不关心历史记录:单独使用 Home Assistant。
在手机能够访问您控制的 HTTPS 端点之前,这些方案都无法运行,因此下面将单独介绍服务器端。如果您还在确定服务器上还应运行哪些服务,今年值得自托管的更完整清单可以说明位置跟踪应用在整体服务中的位置。
Traccar:GPS 硬件、车队和实时位置
Traccar 是一款跟踪服务器。它支持专用 GPS 跟踪器使用的协议,每个跟踪器分别监听 5000 到 5150 范围内的 TCP 和 UDP 端口,并在此基础上提供实时地图、地理围栏、事件规则和报表。Web 界面默认监听 8082 端口。
手机端可使用 Android 和 iOS 版 Traccar Client 应用。该应用通过 OsmAnd 协议上报位置,默认监听 5055 端口。应用设置决定电池续航和记录条数。Distance 表示设备移动时每行驶 N 米请求一次更新。Interval 在距离为 0 时按时间间隔上报。Angle 在航向变化达到指定角度时触发更新,单位为度。Stationary Heartbeat 控制设备停止移动时的行为。Traccar 官方文档说明,这些设置的实际效果都无法保证,因为最终由手机决定。
Traccar 附带嵌入式 H2 数据库,因此安装后无需额外配置即可运行;项目不建议在生产环境中使用 H2。对于较小的服务器,官方建议使用 MySQL 或 MariaDB;对于大型服务器,则建议使用 PostgreSQL,也可以搭配 TimescaleDB。应在积累历史数据前迁移数据库,因为之后转换 H2 文件需要手动操作,而且没有受支持的工具。
Traccar 不适合的场景:它是用于实时查看设备的控制台。其报表主要提供车队报表、行程、停留和汇总数据,因此,记录一年个人周末活动时,得到的更像查询结果,而不是时间线。
OwnTracks:一个手机应用和一个端点,仅此而已
OwnTracks 是最精简的经典配置。应用每次判断设备发生移动时,都会发布一小段 JSON 负载;它可以通过 MQTT(消息队列遥测传输)或普通 HTTP 发布。在 HTTP 模式下,端点是形如 http[s]://[user[:password]@]host[:port]/path 的 URL,并使用 HTTP Basic 进行身份验证。该项目强烈建议使用 https:// 方案。连接 OwnTracks Recorder 时,路径为 /pub。
监控模式比任何服务器设置都更重要。Quiet 仅在您手动请求时发布。Manual 会增加区域监控和低功耗位置请求。Significant 是常规的自动模式。Move 会在设备移动 locatorDisplacement 米或经过 locatorInterval 秒时立即发布,以先发生者为准;默认值分别为 100 米和 300 秒。OwnTracks 文档说明,Move 模式的耗电量相当于导航应用,并建议在出行或充电期间使用,而不是作为日常设置。
OwnTracks Recorder 会存储收到的数据并提供一个小型地图。许多人从不安装它,因为无论应用指向 Recorder、Dawarich 还是 Home Assistant,负载都完全相同。这正是从这里开始的原因:应用是稳定、简单的生产端,之后可以再决定使用哪个消费端。
Dawarich:自托管的 Google Maps Timeline
Dawarich 采用 AGPL-3.0 许可证,并通过 Docker Compose 文件运行。一个可用的安装包含 4 个容器:Rails 应用、用于后台任务的 Sidekiq worker、PostgreSQL 和 Redis。应用提供 3000 端口。可导入的数据源包括 Google Maps Timeline、OwnTracks、Strava、Immich、GPX 和 GeoJSON 文件,以及照片中的 EXIF 数据。正因为可以导入早于服务器部署时间的历史数据,这些导入功能使它成为时间线,而不只是实时地图。
数据接收使用携带账户页面中 API key 的 HTTP 端点。OwnTracks 向 /api/v1/owntracks/points?api_key=... 发送数据,Overland 向 /api/v1/overland/batches?api_key=... 发送数据。GPSLogger 复用 OwnTracks 端点。
注意该 key 的传输位置。它位于查询字符串中,而 nginx 的默认日志格式会记录完整的请求行。因此,每个位置点都会使该 key 以明文写入 /var/log/nginx/access.log。将此日志视为机密。如果将日志发送到任何集中式系统,请轮换该 key。不要将可用的 URL 粘贴到支持帖中。
对于 compose 文件本身、卷和 restart policy,请参阅在 VPS 上运行 compose 的容器机制。本页只介绍决策。
Home Assistant:擅长判断是否在家,不适合保存历史轨迹
如果您已经运行 Home Assistant,就已经具备设备追踪功能。配套应用会报告位置,OwnTracks 集成会提供 webhook URL 和加密密钥,您需要将它们粘贴到应用中。配置 MQTT 后,该集成会改为监听 MQTT 消息,而不是 HTTP。
限制在于数据保留时间。Home Assistant 的 recorder 默认保留 purge_keep_days: 10,并在本地时间每天 04:12 自动清理数据,以防数据库无限增长。这个默认值适合家庭自动化数据库,但不适合位置存档。Home Assistant 可以回答“现在有人在家吗”。如果不将同一数据流发送到能够长期保存数据的其他系统,它无法回答“我在 3 月 14 日去过哪里”。
持续跟踪还是偶尔共享?
持续跟踪表示应用全天发布位置,无论您是否在移动,而它建立的历史记录正是主要用途。偶尔共享表示人们在出行期间互相查看位置,历史记录只是没人要求的副作用。同一款软件可以通过不同设置满足这两种用途。
对于持续跟踪,应优先使用基于位移的上报,而不是较短的定时器;数据库应存放在实际执行备份的存储上;并应在第一天确定保留策略,而不是等磁盘空间耗尽后再决定。对于偶尔共享,只在需要时提高上报频率,之后将手机恢复为低功耗模式。OwnTracks 在应用中提供了相应控制,Traccar Client 则通过距离和间隔字段实现相同功能。
如果您希望为一次出行创建一个有效期两小时的共享链接,请在确定服务器之前查看其发行说明。不同版本之间变化最大的就是此功能,也是这里所有选项中最薄弱的部分。
是否真的需要 MQTT broker?
先不使用 broker。HTTP 模式只需要 URL、密码和 TLS(传输层安全),上文列出的每台服务器都支持这种模式。当多个对象需要在同一时刻读取同一数据流时,broker 才有必要。例如,Home Assistant 在某个区域发生变化时执行响应,同时录像程序写入历史记录,或者家庭成员彼此查看对方。
MQTT 是一种发布/订阅协议,Mosquitto 是通常使用的 broker。它在 1883 端口上监听明文连接,在 8883 端口上监听 TLS 连接,因此只应使用 8883。访问控制按主题设置,因此可以允许某个账户发布到 owntracks/alice/phone,但不允许读取其他内容。这是家庭环境使用 MQTT 的主要理由:broker 在应用层之下执行“谁能查看谁”的权限控制,因此应用程序的错误不会扩大访问范围。
代价是增加一个服务。它需要独立的证书、用户列表,并有独立的故障模式。broker 停止接受连接时,表现与手机没有信号完全相同;但这两种情况都不会直接告诉你原因。
一部手机会产生多少位置记录?
以下数字是算术结果,不是实测值。计算假设上报间隔固定、每次上报写入一行,且不包含其他数据。
The data behind this chart
[
{
"label": "Every 30 seconds",
"points_30d": "86,400",
"points_365d": "1,051,200"
},
{
"label": "Every 60 seconds",
"points_30d": "43,200",
"points_365d": "525,600"
},
{
"label": "Every 5 minutes",
"points_30d": "8,640",
"points_365d": "105,120"
},
{
"label": "Every 15 minutes",
"points_30d": "2,880",
"points_365d": "35,040"
}
]手机每分钟上报一次,每月会写入 43,200 行,每年会写入 525,600 行。将间隔延长到 15 分钟后,每年的数据量为 35,040 行。实际应用通常更复杂,因为它们不仅按时间上报,也会在位置发生移动时上报;手机静止时则会停止上报。因此,第一行数字只能作为上限,不能视为预测值。
每年 500000 行对 PostgreSQL 来说很少。首先遇到的成本通常来自绘制这些数据:如果地图需要以完整分辨率渲染一年的位置点,浏览器会在数据库感知到压力之前就卡住。这也是此类应用会将历史记录聚合为行程和地点的原因。不要直接相信博客文章中的数字,应在自己的服务器上测量实际数量:
SELECT pg_size_pretty(pg_total_relation_size('tc_positions'));这是 PostgreSQL 中 Traccar 的 positions 表。使用一个月后,在你选择的服务器上运行对应查询,再将结果乘以 12。这些服务器都不会自动删除旧位置点。你发送的数据会一直保留,直到手动删除。因此,应在表仍然较小时确定保留策略,并确保备份包含数据库,而不只是配置文件,因为位置历史无法从其他内容中重建。
电池消耗由手机决定,而不是服务器决定
自托管不会改变这一点。操作系统决定何时唤醒 GPS、后台应用可以运行多长时间,以及应用能否运行过夜。Android 会积极暂停后台任务,iOS 则提供显著位置变化,而不是连续的位置数据。服务器无法影响这些行为,因此,应用中设置更短的上报间隔只是一个请求,手机会自行决定如何处理。
您能控制的是手机上的设置:始终授予后台位置权限,将跟踪应用排除在电池优化之外,并根据当天的使用情况选择合适的上报模式。整周启用移动模式,会让手机在下午 4 点前就耗尽电量。
服务器端的表现是收到一批携带旧时间戳的记录。Traccar Client 和 OwnTracks 在离线时都会缓存位置点,并在网络恢复后将其发送出去,因此写入时间和定位时间是两个不同的值。当地图在城市中绘制出一条直线时,这表示定位点之间存在间隔,而不是那里有一条道路。
服务器端:TLS 与经过身份验证的数据接收路径
手机需要一个可以解析的主机名,以及它信任的证书。自签名证书是全新安装后完全收不到数据的最常见原因:应用的 TLS 握手失败后退出,服务器日志也保持为空,因为请求根本没有完成。空日志看起来像是手机端问题,但实际并非如此。
有两种清晰的方案。使用真实证书在 nginx 中终止 TLS,例如 在 nginx 前配置 Let's Encrypt 证书,并且只开放 443 端口。或者通过 Cloudflare Tunnel,使公网 IP 上没有任何监听端口,完全不开放入站端口。这适用于 Dawarich 和 OwnTracks,因为它们的数据接收使用普通 HTTP。
Tunnel 方案有一个限制。Traccar 的 OsmAnd 协议使用 HTTP,因此手机客户端通过反向代理或 Tunnel 访问时不会有问题。专用 GPS 硬件使用的二进制协议是各自端口上的原始 TCP,需要真正开放端口。该端口必须同时在 VPS 防火墙和服务商单独的网络防火墙上开放;在大多数主机上,这属于不同的控制面板。
服务响应后,不要根据应用表现猜测,而是使用一个请求验证整条路径:
curl -si -u alice:secret -H 'Content-Type: application/json' \
-d '{"_type":"location","lat":51.5,"lon":-0.12,"tst":1788480000}' \
https://track.example.com/pub | head -1正常工作的端点会返回 2xx 状态码。401 表示凭据错误,但传输正常。如果 curl 在收到任何状态行之前就报错,问题出在 DNS 或 TLS。先修复证书,再检查应用。
未经保护的采集端点会泄露您的位置
未进行身份验证的采集 URL 会将您的位置暴露给任何找到它的人,而确实有人会找到它。
- 任何持有该 URL 的人都可以向您的历史记录写入位置点。错误的位置点很难发现,删除起来也很麻烦。
- URL 不是秘密。它会出现在手机的应用设置、反向代理访问日志中;如果您曾在浏览器标签页中测试过,还会出现在浏览历史记录以及您求助时附带的截图中。
- 如果端点响应未经身份验证的 GET 请求,爬虫只要猜中路径,就能获取您最近的位置。
- 无论您采取什么措施,主机名都是公开的。新签发的证书会在几分钟内出现在证书透明度日志中,因此即使您从未发布某个地址,它仍然可以被发现。
对 OwnTracks 使用基于 TLS 的 HTTP Basic 身份验证,对 Dawarich 使用 API 密钥,对 Traccar 使用其账户模型,并为设备设置唯一标识符。让管理界面使用与采集路径相同的身份验证,因为完整轨迹可在该界面中读取。然后在第一周后查看哪些来源一直在发起请求:
sudo awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20列表中应主要是您手机使用的移动网络 IP 地址。其他高计数来源值得检查;在 nginx 中为采集位置配置速率限制也不会增加成本。
跟踪器还可能静默失效。容器停止运行,手机继续缓冲数据,而您直到数周后发现某次行程缺失时才会注意到。将容器健康检查或 systemd OnFailure= 指向您自己的 ntfy 服务器,这样即使负责上报的是同一部手机,您也能收到中断通知。
谁还可以查看轨迹,以及如何强制执行访问控制
Traccar 通过用户和设备权限控制访问:账号只能查看管理员授予它的设备,API 使用会话或令牌。通过 MQTT 使用 OwnTracks 时,访问控制由代理通过主题 ACL 执行。这是这些方案中最强的控制方式,因为无法订阅某个主题的账号,无论应用如何配置,都无法读取该主题。通过 HTTP 使用 OwnTracks 时,没有等效的控制层,因此端点后面的组件就是全部访问控制。Dawarich 的设计重点是让一个人查看自己的历史轨迹。如果需要多人实时查看彼此的位置,Traccar 或代理更适合。
家庭定位既涉及同意,也涉及技术。所有通过手机发布位置的人都应知道手机正在发布位置,也应知道如何停止发布。个人无法关闭的跟踪功能不属于家庭功能。
自托管无法解决的问题
- 手机操作系统决定何时唤醒 GPS,因此电池耗电和轨迹中的缺口主要由手机造成,而不是由服务器造成。
- Google 已更改 Timeline 数据的导出方式。计划迁移前,请先阅读 Dawarich 当前的导入文档,因为 Google 今天提供的文件与两年前用户导入的文件不同。
- 每次请求都会让您的终端看到移动 IP 地址,途经的每个网络也都能看到该地址。自托管只会改变由谁保存这条记录,不会删除这条记录。
- 位置历史记录无法替代。请备份数据库,并至少恢复一次以确认备份有效;同时对备份副本进行加密,因为它是服务器上最敏感的文件。
FAQ
哪个自托管应用可以替代 Google Maps Timeline?
Dawarich。它是 Google Timeline 的自托管替代方案,可导入 Google Maps Timeline 导出数据,以及 OwnTracks、Strava、Immich、GPX、GeoJSON 数据和照片 EXIF 信息。它将历史记录呈现为地点和行程,而不是实时地图。Traccar 也可以存储相同的位置点,但其界面是车队控制台,报告也是车队报告。规划迁移前,请查看 Dawarich 的导入文档,确认所需的导出格式,因为 Google 的导出格式发生过变化。
不使用 MQTT broker 可以使用 OwnTracks 吗?
可以。将应用设置为 HTTP 模式,并为其提供格式为 http[s]://[user[:password]@]host[:port]/path 的 URL;对于 OwnTracks Recorder,该 URL 为 /pub。它会发送相同的 JSON payload,并使用 HTTP Basic 进行身份验证。OwnTracks 强烈建议使用 https:// 方案。等到两个服务需要同时使用同一数据流,或需要按主题进行访问控制、由 broker 决定各方可查看哪些数据时,再添加 broker。
一年的位置历史需要多少存储空间?
应按行数规划,而不是按 gigabyte 规划。每部手机每分钟上报一次,一年会产生 525,600 行;每十五分钟上报一次,则会产生 35,040 行。对于 PostgreSQL 来说,这两种规模都很小。实际限制在于浏览器需要在一张地图上绘制一年的位置点,因此这些应用会进行聚合。运行一个月后,使用 pg_total_relation_size 测量自己的表大小,再乘以十二。
为什么我的位置历史记录会出现间隔?
手机操作系统决定何时唤醒 GPS,以及何时停止后台应用,因此大多数间隔都源于此处。授予应用始终允许后台位置访问的权限,将其从电池优化中排除,并检查上报模式。在 OwnTracks 中,Move 模式每移动 locatorDisplacement 米或经过 locatorInterval 秒就会发布一次,默认值为 100 米和 300 秒,并且耗电量相当于导航应用。如果数据行延迟后以一批旧时间戳集中到达,说明离线缓冲区正在刷新;位置修复已经完成,只是上传延迟了。
仅使用秘密 URL 足以保护位置端点吗?
不够。未经身份验证的 ingest 端点允许任何获得该 URL 的人向你的历史记录写入伪造位置点,而且 URL 并不是真正的秘密:它会出现在应用设置、反向代理访问日志、浏览器历史记录以及你分享的任何屏幕截图中。主机名本身也可以被发现,因为新证书签发后不久就会出现在证书透明度日志中。应通过 TLS 使用 HTTP Basic 或 API key,对 ingest 路径进行速率限制,并让管理界面使用相同的身份验证保护。