セルフホスト位置追跡アプリ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 mode の OwnTracks
- 履歴は不要で、自宅と外出の自動化だけを行う: Home Assistant 単体
スマートフォンから管理下の HTTPS endpoint に接続できるようになるまで、どのアプリも動作しません。そのため、サーバー側については以下に独立したセクションを設けています。サーバーに他に何を配置するか検討中であれば、今年セルフホストする価値があるものの全体リストで、位置追跡アプリを他のサービスと並べて確認できます。
Traccar: GPS ハードウェア、フリート、現在位置
Traccar は追跡サーバーです。専用 GPS トラッカーが使用するプロトコルに対応しており、現在のプロトコル一覧では各プロトコルが個別のポートを使用し、5001 から 5266 まで割り当てられています。また、Traccar の Docker 手順では、TCP と UDP の両方について 5000 から 5300 までの範囲全体を公開します。さらに、現在位置を表示する地図、ジオフェンス、イベントルール、レポートを提供します。Web インターフェースは、デフォルトでポート 8082 で待ち受けます。
スマートフォン向けには、Android と iOS 用の Traccar Client アプリがあります。このアプリは OsmAnd プロトコルで位置情報を送信し、デフォルトではポート 5055 で待ち受けます。バッテリー持続時間と記録行数を決めるのは、アプリの設定です。Distance は移動中に N メートル進むたびに更新を要求します。Interval は、距離が 0 の場合に時間ベースの送信を行います。この機能は Android でのみ動作します。また、精度を最高に設定した場合にも動作します。Angle は、精度を最高に設定した場合に、方位が指定した角度だけ変化すると更新を開始します。Stationary Heartbeat は、デバイスが移動していない場合の動作を指定します。Traccar の公式ドキュメントによると、スマートフォン側が判断するため、これらの設定で正確な結果が保証されるわけではありません。
Traccar には組み込みの H2 データベースが付属しているため、インストーラーは設定なしで動作します。ただし、プロジェクトは本番環境での H2 の使用を推奨していません。公式ドキュメントでは、移行先として MySQL、MariaDB、PostgreSQL を扱っており、大規模な環境には 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 は通常の自動モードです。iOS の Move は、デバイスが locatorDisplacement メートル移動するか、locatorInterval 秒が経過した時点のうち、先に到達した時点で送信します。デフォルトは 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 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 に保存されます。このログは secret として扱ってください。ログを中央の保存先へ送信する場合は key を rotate し、動作中の 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 で、データベースが無制限に増加しないよう、auto purge が現地時間の毎晩 04:12 に実行されます。このデフォルト値はホームオートメーション用データベースには適切ですが、位置情報アーカイブには適していません。Home Assistant は「誰か在宅しているか」には答えられます。しかし、対象の日が purge される前に purge_keep_days を増やさない限り、「14 March に自分はどこにいたか」には答えられません。purge_keep_days を増やすと recorder データベース全体が大きくなるため、同じストリームを、データを保持できる別のシステムにも送信します。
継続的な追跡か、時折の共有か
継続的な追跡では、移動中かどうかに関係なく、アプリが終日データを公開します。構築されるアーカイブこそが主な目的です。時折の共有では、旅行中に互いの位置を確認します。アーカイブは、誰も求めていない副次的な結果にすぎません。同じソフトウェアで両方に対応できますが、設定は異なります。
継続的な追跡では、短いタイマーよりも移動距離に基づくレポートを優先します。データベースは、実際にバックアップしているストレージに置きます。保持期間は、ディスクが満杯になった日に決めるのではなく、初日に決めます。時折の共有では、必要な間だけレポート頻度を上げ、その後はスマートフォンを低消費電力モードに戻します。OwnTracks では、これをアプリ内の設定として変更できます。Traccar Client でも、距離と間隔のフィールドを通じて同じことができます。
1 回の旅行で 2 時間だけ共有リンクを使いたい場合は、導入する前に、選択したサーバーのリリースノートを確認してください。この機能はバージョン間で最も変化しやすく、この一覧のどの選択肢でも最も弱い部分です。
MQTT broker は本当に必要ですか?
MQTT broker なしで始めてください。HTTP モードでは URL、パスワード、TLS(transport layer security)が必要で、前述したすべてのサーバーが HTTP を受け付けます。同じストリームを同じ時刻に複数の機器が必要とする場合は、broker を導入する価値があります。たとえば、Home Assistant がゾーンに反応しながら recorder が履歴を書き込む場合や、家族がお互いを確認する場合です。
MQTT は publish と subscribe のプロトコルで、Mosquitto が一般的な broker です。Mosquitto は暗号化されていない接続を 1883 で、TLS 接続を 8883 で待ち受けます。使用するのは 8883 だけにしてください。アクセス制御は topic 単位で行うため、1 つのアカウントに owntracks/alice/phone への publish だけを許可し、その他の読み取りを禁止できます。これが家庭内の構成で MQTT を使う実質的な理由です。誰が誰を見られるかを broker がアプリケーションより下の層で制御するため、アプリケーションのバグによって範囲が広がることがありません。
その代わり、証明書、ユーザー一覧、障害要因を個別に持つサービスが 1 つ増えます。接続を受け付けなくなった broker は、電波の届かない電話とまったく同じように見えます。どちらの場合も、原因は通知されません。
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 行になります。実際のアプリは、時間だけでなく移動距離に応じても報告し、電話が静止している間は報告を停止するため、実際の動作はこの計算より複雑です。そのため、先頭の行は予測値ではなく上限値として読んでください。
1 年で 50 万行なら、PostgreSQL にとっては小規模です。先に問題になるのは、その行を描画する処理です。1 年分の位置情報を完全な解像度で表示するよう地図に要求すると、データベースが負荷を認識するより前にブラウザーが停止します。そのため、この種のアプリは履歴を移動単位や場所単位に集約します。ブログ記事の数値をそのまま信頼せず、実際の値を自分のサーバーで測定してください。
SELECT pg_size_pretty(pg_total_relation_size('tc_positions'));これは PostgreSQL 上の Traccar の positions テーブルです。通常の運用を 1 か月続けた後、選択したサーバーに対して同等の確認を実行し、その値を 12 倍してください。これらのサーバーは、古い位置情報を自動的には削除しません。送信したデータは削除するまで保持されるため、テーブルがまだ小さいうちに保持期間のルールを決めてください。また、バックアップには設定だけでなくデータベースも含めてください。位置履歴は、ほかの情報から復元できないためです。
バッテリー消費はサーバーではなくスマートフォンが決める
これは、self-hosting によって変わらない部分です。GPS を起動するタイミング、バックグラウンドアプリの実行を許可する時間、夜間に動作を継続できるかどうかは、オペレーティングシステムが決めます。Android の電源管理はバックグラウンド処理を積極的に停止します。iOS は位置情報を連続的に送信せず、大きな位置変化を通知します。サーバーはこれらに関与できません。そのため、アプリで短い報告間隔を指定しても、それは要求にすぎず、実際の動作はスマートフォン側の判断に左右されます。
制御できるのはスマートフォン側の設定です。バックグラウンド位置情報の権限を常に許可し、追跡アプリをバッテリー最適化の対象外にします。報告モードは、その日の行動に合わせて選びます。1 週間ずっと移動モードにすると、午後 4 時にはスマートフォンのバッテリーが切れることがあります。
サーバー側では、古いタイムスタンプを持つ行がまとめて追加される症状として現れます。OwnTracks と、オフラインバッファリングを有効にした Traccar Client は、オフライン中に位置情報を保持し、ネットワークが復旧するとまとめて送信します。そのため、書き込み時刻と位置情報の取得時刻は異なります。地図上で都市を横切る直線が描画される場合、それは道路ではなく、位置情報の欠落を示しています。
サーバー側: TLS と認証済み取り込み経路
スマートフォンには、名前解決できるホスト名と、信頼できる証明書が必要です。新規インストールでまったく受信できない原因として最も多いのは、自己署名証明書です。アプリが TLS ハンドシェイクに失敗して処理を中止するためです。リクエストが完了しないので、サーバーログも空のままです。空のログはスマートフォン側の問題に見えますが、実際にはそうではありません。
方法は2つあります。nginx で実際の証明書を使って TLS を終端します。これは nginx の前段に Let's Encrypt 証明書を配置する方法です。この場合は 443 だけを開きます。もう1つは、Cloudflare Tunnel を使ってパブリック IP で何も待ち受けない方法です。Dawarich と OwnTracks の取り込みは通常の HTTP なので、この方法が適しています。
Tunnel を使う方法には制限があります。Traccar の OsmAnd プロトコルは HTTP です。そのため、スマートフォンのクライアントはリバースプロキシまたは Tunnel を問題なく通過できます。一方、専用 GPS ハードウェアが使用するバイナリプロトコルは、それぞれのポートで raw TCP を使用します。この場合は実際にポートを開く必要があります。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 は秘密情報ではありません。スマートフォンのアプリ設定、reverse proxy のアクセスログ、タブでテストした場合のブラウザー履歴、サポートを依頼するときに添付するスクリーンショットなどに残ります。
- 認証なしの GET リクエストに応答するエンドポイントは、パスを推測した crawler に最新の位置情報を渡します。
- 何をしても hostname は公開されます。新しく発行された証明書は数分以内に certificate transparency ログへ記録されるため、公開していないアドレスでも発見可能です。
OwnTracks には TLS 上の HTTP Basic、Dawarich には API key、Traccar には一意の device identifier とアカウントモデルを使用します。管理インターフェースも取り込みパスと同じ認証の背後に置きます。インターフェースからは履歴全体を読み取れるためです。最初の 1 週間が過ぎたら、誰がアクセスを試みているかを確認します。
sudo awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20この一覧では、スマートフォンのモバイル IP アドレスが大半を占めるはずです。それ以外で件数の多いものは調査してください。nginx で取り込み先にレート制限を設定しても、コストはかかりません。
tracker は静かに停止することもあります。コンテナが停止してもスマートフォンはデータをバッファーし続けるため、旅行の記録が欠落して数週間後に初めて気付くことがあります。コンテナの health check または systemd の OnFailure= を 自分で運用する ntfy server に送信するよう設定してください。そうすれば、報告に使用している同じスマートフォンに停止を通知できます。
誰が軌跡を見られるのか、そしてそれをどのように制御するのか
Traccar は、ユーザーとデバイスの権限によってアクセスを制御します。アカウントには、管理者が許可したデバイスが表示され、API ではセッションまたはトークンが使用されます。OwnTracks over MQTT では、ブローカーの topic ACL によってアクセスを制御します。これは、ここで挙げた方式の中で最も強力です。あるアカウントに topic の subscribe が許可されていなければ、アプリケーション側の処理に関係なく、その topic を読み取れないためです。OwnTracks over HTTP には同等の制御層がありません。そのため、endpoint の背後にある仕組みが、そのままアクセス制御のすべてになります。Dawarich は、1 人のユーザーが自分の履歴を確認する用途を中心に設計されています。複数人がお互いの位置をリアルタイムで確認する必要がある場合は、Traccar またはブローカーのほうが適しています。
家族の追跡には、技術面だけでなく同意の問題もあります。スマートフォンから位置情報を公開する人は全員、公開されていることと停止方法を把握していなければなりません。本人が停止できない 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 を使用し、取り込みパスにレート制限を設定し、管理インターフェースも同じ認証の背後に置いてください。