self-hosted位置情報追跡アプリ4種を比較
Dawarich、Traccar、OwnTracks、Home Assistantの違いを比較します。履歴保存、リアルタイム共有、geofence、自動化から選び、ingest endpointをopen internetに公開しない構成も確認できます。
自分のケースに合う self-hosted 位置情報追跡アプリ
self-hosted の位置情報追跡は、1 つの点で分かれます。自分用に履歴を保存するのか、それとも他の人とリアルタイムの位置情報を共有するのかです。前者には Dawarich が適しています。これは Google Maps Timeline の self-hosted 代替で、すでに保存している履歴をインポートできます。後者には Traccar が適しています。GPS ハードウェアとフリート向けに設計されており、スマートフォンも 1 台の tracker として扱います。OwnTracks はその両方の下位層に位置し、スマートフォンアプリとメッセージ形式を提供します。Home Assistant を運用している場合は、スマートフォンの位置をすでに把握しています。ただし、デフォルトでは 10 日後に移動履歴を削除します。
来月、何を確認したいかで選びます。
- 昨年の 3 月に訪れた場所と移動経路をすべて表示する地図: Dawarich
- 複数デバイスのリアルタイム地図、geofence、イベントルール: Traccar
- 1 台のスマートフォンから、すでに運用しているシステムへ位置情報を送信する: HTTP mode の OwnTracks
- 履歴は不要で、自宅と外出に応じた自動化だけを行う: Home Assistant 単体
スマートフォンから管理下の HTTPS endpoint に接続できなければ、どれも動作しません。そのため、サーバー側については以下で独立したセクションを設けます。サーバーに他に何を配置するか検討中であれば、今年 self-hosting する価値があるものの全体リストで、位置情報 tracker を他のサービスと並べて確認できます。
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 が適さない用途は、現在の状況を監視するコンソール以外の用途です。レポートは車両群、走行、停車、集計を対象とするため、自分の週末 1 年分の記録を時系列として確認したい場合は、タイムラインではなくクエリ結果のように表示されます。
OwnTracks: スマートフォンアプリとエンドポイントだけ
OwnTracks は、最小構成の定番です。アプリは、デバイスが移動したと判断するたびに小さな JSON ペイロードを発行します。MQTT(message queuing telemetry transport)または通常の 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 ファイルから実行します。動作する構成は、Rails アプリケーション、バックグラウンドジョブ用の Sidekiq worker、PostgreSQL、Redis の 4 コンテナです。アプリは port 3000 でサービスを提供します。インポート元には Google Maps Timeline、OwnTracks、Strava、Immich、GPX ファイル、GeoJSON ファイル、写真の EXIF データが含まれます。このインポート対象の広さにより、単なるライブマップではなくタイムラインとして利用できます。サーバーを導入する前の履歴も取り込めるためです。
データの取り込みには、アカウントページから取得した API key を付けた HTTP endpoint を使用します。OwnTracks は /api/v1/owntracks/points?api_key=... に投稿し、Overland は /api/v1/overland/batches?api_key=... に投稿します。GPSLogger は OwnTracks endpoint を再利用します。
その key がどこを通るかに注意してください。key は query string に含まれます。nginx のデフォルトの log format は完全な request line を記録するため、すべての位置情報について key が平文で /var/log/nginx/access.log に記録されます。この log は secret として扱ってください。ログを中央の保存先へ送信する場合は key をローテーションし、使用中の URL をサポートスレッドに貼り付けないでください。
compose file 自体の volumes と restart policy については、VPS で compose を実行するためのコンテナ運用を参照してください。このページでは判断までを扱います。
Home Assistant: 在宅状況の確認には適するが、履歴管理には不向き
すでに Home Assistant を運用している場合、デバイス追跡機能も利用できます。コンパニオンアプリが位置情報を報告し、OwnTracks integration では、アプリに貼り付ける webhook URL と暗号化キーが提供されます。MQTT を設定すると、この integration は HTTP の代わりに MQTT メッセージを待ち受けます。
制限となるのは保持期間です。Home Assistant の recorder のデフォルトは purge_keep_days: 10 で、データベースが無制限に増大しないよう、auto purge が毎晩 04:12 の現地時刻に実行されます。このデフォルト設定はホームオートメーション用データベースには適していますが、位置情報のアーカイブには適していません。Home Assistant は「誰かが在宅しているか」には答えられます。しかし、同じストリームをデータを保持する別のシステムにも送らない限り、「14 March に自分がどこにいたか」には答えられません。
継続的な追跡と、必要なときだけの共有
継続的な追跡では、移動中かどうかにかかわらず、アプリが終日位置情報を送信します。構築されるアーカイブ自体が目的です。必要なときだけの共有では、旅行中に互いの位置を確認します。アーカイブは、誰も求めていない副産物にすぎません。同じソフトウェアで両方に対応できますが、設定は異なります。
継続的な追跡では、短いタイマーではなく、移動距離に基づくレポートを優先します。データベースは、実際にバックアップしているストレージに置きます。保持期間は、ディスク容量が一杯になった日に決めるのではなく、初日に決めます。必要なときだけ共有する場合は、必要な間だけレポート頻度を上げ、その後はスマートフォンを低消費電力モードに戻します。OwnTracks では、この切り替えをアプリ内の設定として操作できます。Traccar Client でも、距離と間隔のフィールドを使って同じことができます。
1 回の旅行で 2 時間だけ共有リンクを使いたい場合は、採用するサーバーを決める前に、そのサーバーのリリースノートを確認してください。この機能はバージョンによる差が最も大きく、ここで扱うすべての選択肢において最も弱い部分です。
MQTT ブローカーは本当に必要ですか?
最初はブローカーなしで始めます。HTTP モードでは URL、パスワード、TLS(トランスポート層セキュリティ)が必要で、上記の各サーバーが対応しています。複数のものが同じストリームを同時に必要とする場合は、ブローカーを導入する価値があります。たとえば、Home Assistant がゾーンに反応しながら、レコーダーが履歴を書き込む場合や、家族がお互いを確認する場合です。
MQTT は publish/subscribe プロトコルで、Mosquitto が一般的なブローカーです。Mosquitto は暗号化されていない接続を 1883 で、TLS 接続を 8883 で待ち受けます。使用するのは 8883 だけにします。アクセス制御は topic 単位で行うため、あるアカウントには owntracks/alice/phone への publish だけを許可し、それ以外の読み取りを許可しない構成にできます。これが、家庭で MQTT を使う実質的な理由です。誰が誰を見られるかはアプリケーションより下の層でブローカーが強制するため、アプリケーションのバグによって範囲が広がることがありません。
代わりに、独自の証明書、ユーザー一覧、障害要因を持つサービスが 1 つ増えます。接続を受け付けなくなったブローカーは、電波の届かない電話とまったく同じように見えます。どちらの場合も、原因は通知されません。
1 台のスマートフォンは位置情報の行をいくつ生成するか?
以下の数値は測定値ではなく、算術計算によるものです。一定の報告間隔で、1 回の報告につき 1 行だけを書き込み、それ以外の処理はないものとします。
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"
}
]スマートフォンが 1 分に 1 回報告すると、1 か月に 43,200 行、1 年に 525,600 行を書き込みます。間隔を 15 分にすると、1 年分は 35,040 行になります。実際のアプリは、時間だけでなく移動距離に応じても報告し、スマートフォンが停止している間は報告を停止するため、ここで示した上限値は予測値ではなく目安として扱ってください。
1 年で 500000 行程度なら、PostgreSQL にとっては小規模です。先に問題になるのは描画です。1 年分の位置情報を完全な解像度で地図に表示しようとすると、データベースが負荷を認識するより前にブラウザーが停止します。そのため、この種のアプリは履歴を移動と場所に集約します。ブログ記事の数値をそのまま信用せず、実際の利用環境で実数を測定してください。
SELECT pg_size_pretty(pg_total_relation_size('tc_positions'));これは PostgreSQL 上の Traccar の positions テーブルです。選択したサーバーについても、通常の運用を 1 か月続けた後に同等のコマンドを実行し、その値を 12 倍してください。これらのサーバーは、いずれも古い位置情報を自動的には削除しません。送信したデータは削除するまで保持されるため、テーブルがまだ小さいうちに保持期間のルールを決めてください。また、バックアップには設定だけでなくデータベースも含めてください。位置情報の履歴は、ほかのデータから復元できないためです。
バッテリー消費はサーバーではなく、スマートフォン側で決まる
セルフホスティングでも変わらない部分があります。GPS を起動するタイミング、バックグラウンドアプリの実行時間、夜間も動作を維持するかどうかは、オペレーティングシステムが決定します。Android はバックグラウンド処理を積極的に停止し、iOS は連続的な位置情報ではなく、大きな位置変化を通知します。サーバーにはこれを制御する手段がないため、アプリで短い報告間隔を設定しても、それは要求にすぎず、実際の動作はスマートフォン側で決まります。
制御できるのはスマートフォン側の設定です。バックグラウンドでの位置情報の使用を常に許可し、追跡アプリをバッテリー最適化の対象外にして、日中の行動に合わせて報告モードを選択します。1 週間ずっと Move モードにすると、午後 4 時にはスマートフォンのバッテリーが切れます。
サーバー側では、古いタイムスタンプを持つ行が短時間にまとめて追加されることがあります。Traccar Client と OwnTracks は、オフライン中の位置情報をバッファーに保存し、ネットワークが復旧すると送信します。そのため、書き込み時刻と位置情報の取得時刻は異なります。地図上で都市を横断する直線が描かれる場合、それは道路ではなく、位置情報の取得間隔に空白があることを示しています。
サーバー側: TLS と認証済み取り込み経路
スマートフォンには、名前解決できるホスト名と、信頼できる証明書が必要です。新規インストールで何も受信できない最も一般的な原因は、自己署名証明書です。アプリが TLS ハンドシェイクに失敗して処理を中断するためです。リクエストが完了していないため、サーバーログも空のままです。空のログはスマートフォン側の問題に見えますが、実際には違います。
構成は2通りあります。nginx で実在する証明書を使って TLS を終端します。これは nginx の前段に Let's Encrypt 証明書を配置する方法 です。この場合は 443 だけを開きます。もう1つは、Cloudflare Tunnel を使ってパブリック IP で受信ポートを待ち受けない方法 です。Dawarich と OwnTracks では取り込みに通常の HTTP を使うため、この構成が適しています。
Tunnel を使う場合には1つ制限があります。Traccar の OsmAnd プロトコルは HTTP なので、スマートフォンクライアントはリバースプロキシまたは Tunnel を問題なく通過できます。一方、専用 GPS ハードウェアが使用するバイナリプロトコルは、それぞれのポートで raw TCP を使います。これらには実際に開いたポートが必要です。VPS の firewall と、プロバイダー側の別ネットワーク firewall の両方で開放してください。多くのホストでは、後者は別のコントロールパネルで設定します。
応答するようになったら、アプリの挙動から推測せず、1回のリクエストで経路を確認します。
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 を持つ誰もが、履歴に位置情報を書き込めます。偽の位置情報は見つけにくく、削除にも手間がかかります。
- URL は秘密情報ではありません。スマートフォンのアプリ設定、リバースプロキシのアクセスログ、タブでテストした場合のブラウザー履歴、サポートを依頼するときに添付するスクリーンショットなどに残ります。
- 認証なしの GET リクエストに応答するエンドポイントは、パスを推測したクローラーに最新の位置情報を渡します。
- 何をしてもホスト名は公開されます。新しく発行された証明書は数分以内に Certificate Transparency ログへ現れるため、公開していないアドレスでも発見可能です。
OwnTracks では TLS 上の HTTP Basic 認証、Dawarich では API key、Traccar では一意のデバイス識別子を使うアカウントモデルを使用します。管理インターフェースにも取り込みパスと同じ認証を設定します。インターフェースからは軌跡全体を読み取れるためです。次に、最初の 1 週間が過ぎたら、誰がアクセスを試みていたかを確認します。
sudo awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20この一覧では、スマートフォンのモバイル IP アドレスが大半を占めるはずです。それ以外で件数の多いものは確認してください。nginx で取り込み先にレート制限を設定しても、コストはかかりません。
トラッカーは、問題が起きても気付かないまま停止することがあります。コンテナが停止してもスマートフォンはバッファリングを続けるため、移動履歴が欠落した数週間後に初めて気付くことがあります。コンテナの health check または systemd の OnFailure= から 自分で運用する ntfy server に通知を送るようにします。そうすれば、報告を行っている同じスマートフォンに停止を通知できます。
誰が軌跡を見られるか、またそれをどのように強制するか
Traccar はユーザー権限とデバイス権限でアクセスを制御します。アカウントには、管理者が許可したデバイスだけが表示されます。API ではセッションまたはトークンを使用します。OwnTracks over MQTT では、broker の topic ACL でアクセスを制御します。これは、これらの方式の中で最も強力です。topic を subscribe できないアカウントは、アプリケーション側の動作にかかわらず、その topic を読み取れないためです。OwnTracks over HTTP には同等の層がありません。そのため、endpoint の背後にある仕組みがアクセス制御のすべてになります。Dawarich は、1 人の利用者が自分の履歴を読む構成を前提としています。複数人が互いの位置をリアルタイムで確認する必要がある場合は、Traccar または broker のほうが適しています。
家族の位置情報を追跡する場合、技術面だけでなく同意も問題になります。スマートフォンから位置情報を publish する全員が、publish されることと停止方法を知っていなければなりません。本人が停止できない tracker は、家族向けの機能とはいえません。
セルフホスティングで解決できないこと
- GPS を起動するタイミングはスマートフォンのオペレーティングシステムが決めます。そのため、バッテリー消費や軌跡の欠落は、サーバーではなく主にスマートフォン側が原因です。
- Google は Timeline データのエクスポート方法を変更しました。移行を計画する前に、Dawarich の最新のインポートドキュメントを確認してください。現在 Google から取得できるファイルは、2 年前にインポートされていたものとは異なります。
- エンドポイントには、リクエストのたびにモバイル IP アドレスが通知されます。経路上のすべてのネットワークにも同じ情報が見えます。セルフホスティングによって、その記録を保持する主体は変わります。ただし、記録自体がなくなるわけではありません。
- 位置履歴は代替できません。データベースをバックアップし、そのバックアップから 1 回復元して正常に機能することを確認してください。コピーは暗号化した状態で保管してください。位置履歴はサーバー上で最も機密性の高いファイルだからです。
FAQ
Google Maps Timeline の代替になる self-hosted アプリはどれですか?
Dawarich です。Google Timeline の代わりとして self-host できるように作られており、Google Maps Timeline のエクスポートに加えて、OwnTracks、Strava、Immich、GPX、GeoJSON のデータと写真の EXIF を取り込めます。履歴はライブマップではなく、場所と移動として表示します。Traccar でも同じ位置情報を保存できますが、インターフェースはフリート管理コンソールで、レポートもフリート向けです。移行を計画する前に、Dawarich のインポートドキュメントで想定されるエクスポート形式を確認してください。Google のエクスポート形式は変更されているためです。
MQTT ブローカーなしで OwnTracks を使用できますか?
はい。アプリを HTTP モードに設定し、http[s]://[user[:password]@]host[:port]/path 形式の URL を指定します。OwnTracks Recorder の場合は /pub です。同じ JSON ペイロードを HTTP Basic で認証して送信し、OwnTracks は https:// 方式を強く推奨しています。2 つのサービスで同じストリームを同時に利用する必要が生じた場合や、ブローカー側で閲覧者を制御するトピック単位のアクセス制御が必要になった場合は、後からブローカーを追加できます。
1 年分の位置情報履歴にはどの程度のストレージが必要ですか?
ギガバイト単位ではなく、行数で見積もってください。1 分ごとに 1 件記録すると、1 台の phone あたり年間 525,600 行になります。15 分ごとなら 35,040 行です。いずれも PostgreSQL では少ない量です。実際の制限要因は、1 年分の位置情報を 1 つのマップ上にブラウザーで描画することです。そのため、これらのアプリは集約処理を行います。pg_total_relation_size を使って 1 か月後に自分のテーブルを測定し、12 倍してください。
位置情報履歴に欠落があるのはなぜですか?
phone の OS が GPS を起動するタイミングと、バックグラウンドアプリを停止するタイミングを決めるため、多くの欠落はそこから発生します。アプリに常時のバックグラウンド位置情報権限を付与し、バッテリー最適化の対象外にして、記録モードを確認してください。OwnTracks の Move モードでは、locatorDisplacement メートルまたは locatorInterval 秒ごとに送信します。初期値は 100 メートルと 300 秒で、ナビゲーションアプリと同程度にバッテリーを消費します。古いタイムスタンプを含む行が遅れてまとめて届く場合は、オフラインバッファーがフラッシュされています。位置情報の取得自体は完了しており、アップロードだけが遅延しています。
秘密の URL だけで位置情報エンドポイントを保護できますか?
いいえ。認証のない取り込みエンドポイントでは、URL を知っている人なら誰でも履歴に偽の位置情報を書き込めます。また、URL は秘密ではありません。アプリ設定、リバースプロキシのアクセスログ、ブラウザー履歴、共有したスクリーンショットに残るためです。ホスト名だけでも発見できます。新しい証明書は、発行後まもなく certificate transparency ログに現れるためです。TLS 上で HTTP Basic または API key を使用し、取り込みパスにレート制限を設定して、管理インターフェースも同じ認証の背後に置いてください。