セルフホスト位置追跡アプリ4種を比較
Dawarich、Traccar、OwnTracks、Home Assistantの違いを比較します。履歴保存、現在地共有、ジオフェンスなど用途別に選び、ingest endpointを公開インターネットに置かない構成も確認できます。
自分のケースに合うセルフホスト型位置情報追跡アプリ
セルフホスト型の位置情報追跡は、1 つの点で分かれます。自分用に履歴を保存するのか、それとも他の人とリアルタイムの位置を共有するのかです。前者には Dawarich が適しています。これは Google Maps Timeline のセルフホスト型代替で、すでに保持している履歴をインポートできます。後者には Traccar が適しています。GPS ハードウェアと車両フリート向けに設計されており、スマートフォンも 1 台のトラッカーとして扱います。OwnTracks はその両方を支えるスマートフォンアプリおよびメッセージ形式です。Home Assistant を運用している場合、スマートフォンの現在位置はすでに把握できます。ただし、デフォルトでは 10 日後に軌跡を削除します。
来月、何を確認したいかで選びます。
- 去年の 3 月に訪れたすべての場所と移動経路の地図: Dawarich。
- 複数デバイスのリアルタイム地図、ジオフェンス、イベントルール: Traccar。
- 既存の環境に 1 台のスマートフォンから位置情報を送信: HTTP モードの OwnTracks。
- 在宅・外出の自動化だけが必要で、履歴は不要: Home Assistant 単体。
HTTP で位置情報を送信する場合、スマートフォンから管理下の HTTPS エンドポイントへ接続できる必要があります。そのため、サーバー側については下記に独立したセクションを設けています。サーバーに何を追加するかまだ検討中であれば、今年セルフホストする価値があるものの全体リストで、位置情報追跡アプリを他のサービスと比較できます。
Traccar: GPS ハードウェア、車両群、現在位置
Traccar は追跡サーバーです。専用 GPS トラッカーが使用するプロトコルに対応しており、各プロトコルは個別のポートを使用します。現在のプロトコル一覧では、ポート番号は 5001 から 5267 までです。Traccar の Docker 手順では、TCP と UDP の両方について 5000 から 5300 までの範囲全体を公開します。同じ手順では、公開するポートごとに独自の docker-proxy プロセスが起動し、多くのメモリを消費すると説明しています。そのため、デバイスが使用するポートだけを公開するか、Docker の userland proxy を無効にして iptables NAT を使用する方が軽量です。さらに、ライブマップ、ジオフェンス、イベントルール、レポートも提供します。Web インターフェースは、デフォルトでポート 8082 で待ち受けます。
スマートフォン向けには、Android と iOS 用の Traccar Client アプリがあります。このアプリは OsmAnd プロトコルで送信し、デフォルトではポート 5055 で待ち受けます。バッテリー持続時間と位置情報の記録件数は、アプリの設定で決まります。Distance は、移動中に N メートルごとの更新を要求します。Interval は、距離を 0 に設定した場合、または精度を highest に設定した場合に、時間ベースのレポートを行います。距離を 0 にした時間ベースのレポートは Android でのみ機能します。Angle は、精度を highest に設定した場合に、方位の変化を度単位で検出して更新します。Stationary Heartbeat は、デバイスが移動していない場合の動作を指定します。Traccar の公式ドキュメントによると、いずれの場合も正確な結果は保証されません。動作を決めるのはスマートフォンだからです。
Traccar には組み込みの H2 データベースが含まれているため、セットアップなしでインストールできます。ただし、プロジェクトでは本番環境での H2 の使用を推奨していません。公式ドキュメントでは、移行先として MySQL、MariaDB、PostgreSQL を扱っており、大規模な環境には PostgreSQL を基盤とする時系列データベースの TimescaleDB を推奨しています。履歴が蓄積する前にデータベースを移行してください。後から移行する場合は、既存のデータを移行先のデータベースに移す必要があります。
Traccar が適していない用途は、現在の状況を監視するコンソールとしての利用です。レポートは車両群、走行、停止、集計を対象とするため、自分の週末 1 年分の記録は、時系列ではなくクエリ結果のように表示されます。
OwnTracks: スマートフォンアプリ、Recorder、Frontend
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 は、通常の自動モードです。iOS では、デバイスが locatorDisplacement メートル移動するか、locatorInterval 秒が経過すると、いずれか早い時点で Move が公開します。デフォルトは 100 メートルと 300 秒です。Android では、Move はデフォルトで 10 秒ごとに位置情報を取得します。OwnTracks のドキュメントでは、Move モードはナビゲーションアプリと同程度にバッテリーを消費すると説明しています。日常的な設定ではなく、旅行中または充電中に使用することを推奨しています。
OwnTracks Recorder は受信したデータを保存し、小さな地図を提供します。別の OwnTracks Frontend を重ねると、ヒートマップ、日付範囲によるフィルタリング、点または線で表示する履歴を備えた、より充実した Web インターフェースを利用できます。アプリの送信先を Recorder、Dawarich、Home Assistant のいずれにしてもペイロードは同じであるため、Recorder を導入しない人も多くいます。まずここから始める理由はそこにあります。アプリは安定した単純なデータ送信元であり、後から受信側を変更できるためです。
Dawarich: 自己ホスト型の Google Maps Timeline
Dawarich は AGPL-3.0 で、Docker Compose ファイルから実行します。現在のデフォルト構成は、Rails アプリケーション(内部で Sidekiq のバックグラウンドジョブを実行)、PostgreSQL、Redis の 3 コンテナです。アプリケーションはポート 3000 を提供します。インポート元には、Google Maps Timeline、OwnTracks、Strava、Immich、GPX ファイル、GeoJSON ファイル、写真の EXIF データがあります。このインポート機能により、Dawarich はライブマップではなくタイムラインとして機能します。サーバーを構築する前の履歴も取り込めるためです。
データ取り込みには、アカウントページから取得した API key を付けた HTTP エンドポイントを使用します。OwnTracks は /api/v1/owntracks/points?api_key=... に、Overland は /api/v1/overland/batches?api_key=... に POST します。GPSLogger は OwnTracks のエンドポイントを再利用します。
この key がどこを通過するかに注意してください。key は query string に含まれます。また、nginx のデフォルトのログ形式ではリクエスト行全体が記録されるため、1 件の位置情報を送信するたびに、key が平文で /var/log/nginx/access.log に保存されます。このログは Secret として扱ってください。ログを中央の場所へ送信する場合は key をローテーションし、使用中の URL をサポートスレッドに貼り付けないでください。
compose ファイル自体、volumes、restart policy については、VPS で compose を実行するためのコンテナの仕組みを参照してください。このページでは判断までを扱います。
Home Assistant: 在宅状況には適しているが、履歴には不向き
Home Assistant をすでに運用している場合、デバイス追跡機能もすでに利用できます。コンパニオンアプリが位置情報を報告し、OwnTracks integration では、アプリに貼り付ける webhook URL と暗号化キーが提供されます。MQTT を設定すると、この integration は HTTP ではなく MQTT メッセージを待ち受けます。
制限になるのは保持期間です。Home Assistant の recorder はデフォルトで purge_keep_days: 10 に設定されており、データベースが無制限に肥大化しないよう、ローカル時間の毎晩 04:12 に自動 purge が実行されます。このデフォルト設定はホームオートメーション用のデータベースには適していますが、位置情報のアーカイブには適していません。Home Assistant は「誰か在宅しているか」には答えられます。「14 March に自分がどこにいたか」に答えるには、削除される前に履歴を保持してください。purge_keep_days を増やす、auto_purge を無効にする、または同じストリームを履歴保持機能のある別のシステムにも送信する方法があります。履歴をより長く保持すると recorder データベースが大きくなります。auto_purge を無効にする場合、ドキュメントでは recorder.purge の呼び出しを自分で定期的にスケジュールすることを推奨しています。
継続的な追跡と、必要なときだけの共有
継続的な追跡では、移動中かどうかにかかわらず、アプリが終日データを公開します。構築されるアーカイブ自体が目的です。必要なときだけの共有では、旅行中に互いの位置を確認します。アーカイブは、利用者が求めていない副次的な結果にすぎません。同じソフトウェアで両方に対応できますが、設定は異なります。
継続的な追跡では、短いタイマーよりも移動距離に基づく送信を優先します。データベースは、実際にバックアップしているストレージに置きます。保持期間は、ディスクが満杯になった日ではなく、初日に決めてください。必要なときだけの共有では、必要な時間だけ送信頻度を上げ、その後はスマートフォンを低消費電力モードに戻します。OwnTracks では、これをアプリ内の設定として変更できます。Traccar Client でも、距離と間隔のフィールドを通じて同じ設定ができます。
1 回の旅行で 2 時間だけ共有するリンクが必要な場合は、利用するサーバーを決める前に、そのサーバーのリリースノートを確認してください。Traccar は一時的なデバイス共有リンクを提供します。Dawarich は、任意のパスフレーズ、失効日、即時取り消しに対応した現在地共有リンクを提供します。
MQTT ブローカーは本当に必要ですか?
まず、MQTT ブローカーなしで始めます。HTTP モードでは URL、パスワード、TLS(トランスポート層セキュリティ)を使用し、上記の各サーバーが対応しています。複数の機器が同じストリームを同時に必要とする場合は、ブローカーを導入する価値があります。たとえば、Home Assistant がゾーンに反応しながらレコーダーが履歴を保存する場合や、家族がお互いを確認する場合です。
MQTT はパブリッシュ・サブスクライブ型のプロトコルで、Mosquitto が一般的なブローカーです。デフォルトのリスナーはポート 1883 を平文で使用します。8883 で TLS リスナーを設定し、そのリスナーだけを外部公開します。アクセス制御はトピック単位で行うため、1 つのアカウントに owntracks/alice/phone のパブリッシュだけを許可し、それ以外の読み取りを禁止できます。これが家庭環境で MQTT を使う実質的な理由です。誰が誰を確認できるかを、アプリケーションより下の層でブローカーが強制するため、アプリケーションのバグで範囲が広がることはありません。
代わりに、証明書、ユーザー一覧、障害モードをそれぞれ持つサービスが 1 つ増えます。接続を受け付けなくなったブローカーは、携帯電話の電波がない状態とまったく同じように見えます。どちらも、その理由までは知らせてくれません。
1 台のスマートフォンで生成される位置情報の行数
以下の数値は実測値ではなく、算術計算によるものです。一定の報告間隔で、報告 1 回につき 1 行だけを書き込み、その他の書き込みはないものとします。
このグラフのデータ
[
{
"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 行になります。実際のアプリはこれほど単純ではありません。時間だけでなく移動量に応じても報告し、スマートフォンが停止している間は報告を止めるためです。そのため、ここでの行数は予測値ではなく、一定間隔で報告した場合の例として見てください。
年間 50 万行程度なら、PostgreSQL にとっては小規模です。先に問題になるのは描画コストです。1 年分の位置情報を完全な解像度で地図に描画させると、データベースが負荷を認識するより先にブラウザーが停止します。そのため、この種のアプリは履歴を移動単位や場所単位に集約します。ブログ記事の数値を信頼するのではなく、次のように自分のサーバーで実際の行数を測定してください。
SELECT pg_size_pretty(pg_total_relation_size('tc_positions'));これは PostgreSQL 上の Traccar の positions テーブルです。通常の運用を 1 か月続けた後、選択したサーバーで同等の確認を実行し、その値を 12 倍してください。Home Assistant はデフォルトで古い履歴を自動的に削除します。テーブルがまだ小さいうちに保持期間を決め、バックアップに設定ファイルだけでなくデータベースも含めてください。位置情報の履歴は、他の情報から再構築できないためです。
バッテリー消費はサーバーではなくスマートフォンが決める
これは、self-hosting によって変わらない部分です。GPS を起動するタイミング、バックグラウンドアプリの実行を許可する時間、夜間に動作を継続できるかどうかは、オペレーティングシステムが決めます。Android の電源管理はバックグラウンド処理を積極的に停止します。iOS は位置情報を連続的に送信せず、大きな位置変化を通知します。サーバーはこれらに関与できません。そのため、アプリで短い報告間隔を指定しても、それは要求にすぎず、実際の動作はスマートフォン側の判断に左右されます。
制御できるのはスマートフォン側の設定です。バックグラウンド位置情報の権限を常に許可し、追跡アプリをバッテリー最適化の対象外にします。報告モードは、その日の行動に合わせて選びます。1 週間ずっと移動モードにすると、午後 4 時にはスマートフォンのバッテリーが切れることがあります。
サーバー側では、古いタイムスタンプを持つ行がまとめて追加される症状として現れます。OwnTracks と、オフラインバッファリングを有効にした Traccar Client は、オフライン中に位置情報を保持し、ネットワークが復旧するとまとめて送信します。そのため、書き込み時刻と位置情報の取得時刻は異なります。地図上で都市を横切る直線が描画される場合、それは道路ではなく、位置情報の欠落を示しています。
サーバー側: TLS と認証付き取り込み経路
スマートフォン側では、名前解決でき、信頼できる証明書が必要です。新規インストール後に何も受信できない最も一般的な原因は、自己署名証明書です。アプリが TLS ハンドシェイクに失敗して処理を中断するためです。リクエストが完了していないので、サーバーログも空のままです。空のログはスマートフォン側の問題に見えますが、実際には違います。
適切な方法は 2 つあります。nginx で実際の証明書を使って TLS を終端します。これは nginx の前段に Let's Encrypt 証明書を配置する方法です。そのうえでポート 80 と 443 を開放します。certbot の nginx プラグインは HTTP-01 チャレンジでドメインを検証します。Let's Encrypt はこの検証をポート 80 でのみ実行するため、ポート 80 は到達可能な状態に保つ必要があります。ポート 80 を閉じるのではなく、HTTPS にリダイレクトしてください。443 だけで運用できるのは、DNS-01 または TLS-ALPN-01 チャレンジに切り替えた場合だけです。もう 1 つは、Cloudflare Tunnel を使って受信ポートを一切公開せず、パブリック IP で待ち受けない方法です。取り込みに通常の HTTP を使う Dawarich や OwnTracks には、この方法が適しています。
Tunnel を使う場合の制限が 1 つあります。Traccar の OsmAnd プロトコルは HTTP なので、スマートフォンのクライアントはリバースプロキシまたは Tunnel を問題なく通過できます。一方、専用 GPS ハードウェアが使用するバイナリプロトコルは、専用ポート上の raw TCP または UDP です。これらには実際に開放したポートが必要です。VPS のファイアウォールだけでなく、プロバイダーが別に提供するネットワークファイアウォールでも開放してください。多くのホストでは、これは別のコントロールパネルで管理します。
応答が返るようになったら、アプリの挙動から推測せず、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 は秘密情報ではありません。スマートフォンのアプリ設定、リバースプロキシのアクセスログ、タブでテストした場合のブラウザー履歴、サポートを依頼するときに添付するスクリーンショットなどに残ります。
- OwnTracks Recorder の
/api/0/lastのような保護されていない読み取り API は、クエリを送った誰にでも最新の位置情報を渡します。 - 何をしてもホスト名は公開されます。新しく発行された証明書は数分以内に証明書透明性ログへ現れるため、公開していないアドレスでも発見可能です。
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 で取り込み先にレート制限を設定しても、コストはかかりません。
トラッカーは静かに停止することもあります。コンテナが停止してもスマートフォンはデータをバッファーし続けるため、移動記録が欠落して数週間後に初めて気付くことがあります。コンテナのヘルスチェックまたは systemd OnFailure= を 自分で運用する ntfy サーバー に送信するよう設定してください。そうすれば、報告を行っている同じスマートフォンで停止を確認できます。
誰が位置情報の履歴を確認でき、それをどのように制御するか
Traccar は、ユーザーとデバイスの権限によってアクセスを制御します。アカウントからは、管理者が許可したデバイスだけを確認できます。API では、セッションまたはトークンを使用します。OwnTracks を MQTT 経由で使用する場合は、ブローカーの topic ACL でアクセスを制御します。これは、ここで挙げる方法の中で最も強力です。topic を subscribe できないアカウントは、アプリケーションの動作に関係なく、その topic を読み取れないためです。OwnTracks を HTTP 経由で使用する場合、このような同等の層はありません。エンドポイントの背後にある仕組みが、アクセス制御のすべてを担います。Dawarich は家族間のリアルタイム位置情報共有にも対応しており、各メンバーが自分の共有を管理できます。
家族の追跡には、技術面だけでなく同意の問題もあります。スマートフォンから位置情報を公開する全員が、公開されることと停止方法を理解していなければなりません。本人が停止できない tracker は、家族向けの機能とはいえません。
自己ホスティングで解決できないこと
- GPS を起動するタイミングはスマートフォンの OS が決めるため、バッテリー消費と軌跡の欠落は、ほとんどの場合サーバーではなくスマートフォン側が原因です。
- Google は Timeline データのエクスポート方法を変更しました。移行を計画する前に、Dawarich の最新のインポートドキュメントを確認してください。現在 Google から提供されるファイルは、2 年前にインポートしていたものとは異なります。
- エンドポイントにはリクエストのたびにモバイル IP アドレスが通知され、その間にあるすべてのネットワークにも通知されます。自己ホスティングによって、その記録を保持する主体は変わります。記録自体がなくなるわけではありません。
- 位置履歴は代替できません。データベースをバックアップし、バックアップから 1 度復元して正常に機能することを確認してください。さらに、コピーは暗号化して保管してください。位置履歴はサーバー上で最も機密性の高いファイルです。
FAQ
Google Maps Timeline の代替になる self-hosted アプリはどれですか?
Dawarich です。Google Timeline の代替として self-hosting できるように作られており、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 台のスマートフォンあたり年間 525,600 行です。15 分ごとの場合は 35,040 行です。どちらも PostgreSQL では少ない量です。実際の制限要因は、1 年分の地点を 1 つの地図上でブラウザーが描画することです。そのため、これらのアプリはデータを集約します。pg_total_relation_size を使って 1 か月後に自分のテーブルを測定し、12 倍してください。
位置履歴に欠落があるのはなぜですか?
スマートフォンのオペレーティングシステムが、GPS を起動するタイミングとバックグラウンドアプリを停止するタイミングを決めます。そのため、欠落の多くはここで発生します。アプリに常時のバックグラウンド位置情報権限を付与し、バッテリー最適化の対象から除外して、報告モードを確認してください。iOS の OwnTracks では、Move モードが locatorDisplacement メートルまたは locatorInterval 秒ごとに公開します。デフォルトは 100 メートルと 300 秒です。Android では、デフォルトで 10 秒ごとに測位を要求します。いずれの場合も、ナビゲーションアプリと同程度にバッテリーを消費します。古いタイムスタンプを持つ行が遅れてまとまって届く場合は、オフラインバッファーがフラッシュされています。測位は実行済みで、アップロードだけが遅延していたということです。
秘密の URL だけで位置情報エンドポイントを保護できますか?
いいえ。認証されていない取り込みエンドポイントでは、URL を知っている人なら誰でも履歴に偽の地点を書き込めます。また、URL は秘密ではありません。アプリの設定、リバースプロキシのアクセスログ、ブラウザー履歴、共有したスクリーンショットなどに残るためです。ホスト名だけでも発見できます。新しい証明書は、発行後まもなく certificate transparency のログに現れるためです。TLS 上で HTTP Basic または API key を使用し、取り込みパスにレート制限を設定し、管理インターフェースも同じ認証の背後に置いてください。