自建 OwnTrack 追蹤個人 GPS 軌跡服務
- 雖然 Google Map 有提供這功能, 不過很多人想記錄自己軌跡, 但又不想讓 Google 掌握自己的隱私, 就會有這個需求
-
- GitHub - https://github.com/owntracks
架構圖
graph TB
%% External Components
Mobile[📱 OwnTracks Mobile App
MQTT + WebSocket] Browser[🌐 Web Browser] External[🌍 External MQTT Clients] WebApp[📱 Mobile Web App
WebSocket Only] %% Docker Services subgraph "Docker Environment" subgraph "Mosquitto MQTT Broker" MQTT[Eclipse Mosquitto
Port: 1883, 9001] end subgraph "OwnTracks Recorder" Recorder[OwnTracks Recorder
Port: 8083
HTTP API] end subgraph "Frontend Web Interface" Frontend[OwnTracks Frontend
Port: 80
Nginx Web Server] end end %% Storage subgraph "Data Storage" MosquittocConf[(Mosquitto Config
./mosquitto/config)] MosquittoData[(Mosquitto Data
./mosquitto/data)] MosquittoLog[(Mosquitto Logs
./mosquitto/log)] RecorderStore[(Location Data
./store)] AuthFile[(Authentication
./auth/users.auth)] end %% Data Flow - Location Publishing Mobile -.->|MQTT Publish
Location Data
Port 1883| MQTT Mobile -.->|WebSocket Publish
Location Data
Port 9001| MQTT External -.->|MQTT Publish
Location Data
Port 1883| MQTT WebApp -.->|WebSocket Publish
Location Data
Port 9001| MQTT %% Internal MQTT Subscription MQTT -->|MQTT Subscribe
Location Topics| Recorder %% Data Storage Flow Recorder --> RecorderStore MQTT --> MosquittoData MQTT --> MosquittoLog %% Web Interface Flow Browser -->|HTTP Request
Port 80| Frontend Frontend -->|HTTP API Calls
Port 8083| Recorder Recorder -->|JSON Response
Location Data| Frontend Frontend -->|HTML/JS/CSS| Browser %% Configuration and Authentication MQTT -.-> MosquittocConf Recorder -.-> AuthFile %% WebSocket for Real-time (optional) MQTT -.->|WebSocket
Port 9001| Browser %% Styling classDef service fill:#e1f5fe,stroke:#01579b,stroke-width:2px classDef storage fill:#f3e5f5,stroke:#4a148c,stroke-width:2px classDef external fill:#e8f5e8,stroke:#1b5e20,stroke-width:2px class MQTT,Recorder,Frontend service class MosquittoData,MosquittoLog,RecorderStore,AuthFile,ConfigFile storage class Mobile,Browser,External external
MQTT + WebSocket] Browser[🌐 Web Browser] External[🌍 External MQTT Clients] WebApp[📱 Mobile Web App
WebSocket Only] %% Docker Services subgraph "Docker Environment" subgraph "Mosquitto MQTT Broker" MQTT[Eclipse Mosquitto
Port: 1883, 9001] end subgraph "OwnTracks Recorder" Recorder[OwnTracks Recorder
Port: 8083
HTTP API] end subgraph "Frontend Web Interface" Frontend[OwnTracks Frontend
Port: 80
Nginx Web Server] end end %% Storage subgraph "Data Storage" MosquittocConf[(Mosquitto Config
./mosquitto/config)] MosquittoData[(Mosquitto Data
./mosquitto/data)] MosquittoLog[(Mosquitto Logs
./mosquitto/log)] RecorderStore[(Location Data
./store)] AuthFile[(Authentication
./auth/users.auth)] end %% Data Flow - Location Publishing Mobile -.->|MQTT Publish
Location Data
Port 1883| MQTT Mobile -.->|WebSocket Publish
Location Data
Port 9001| MQTT External -.->|MQTT Publish
Location Data
Port 1883| MQTT WebApp -.->|WebSocket Publish
Location Data
Port 9001| MQTT %% Internal MQTT Subscription MQTT -->|MQTT Subscribe
Location Topics| Recorder %% Data Storage Flow Recorder --> RecorderStore MQTT --> MosquittoData MQTT --> MosquittoLog %% Web Interface Flow Browser -->|HTTP Request
Port 80| Frontend Frontend -->|HTTP API Calls
Port 8083| Recorder Recorder -->|JSON Response
Location Data| Frontend Frontend -->|HTML/JS/CSS| Browser %% Configuration and Authentication MQTT -.-> MosquittocConf Recorder -.-> AuthFile %% WebSocket for Real-time (optional) MQTT -.->|WebSocket
Port 9001| Browser %% Styling classDef service fill:#e1f5fe,stroke:#01579b,stroke-width:2px classDef storage fill:#f3e5f5,stroke:#4a148c,stroke-width:2px classDef external fill:#e8f5e8,stroke:#1b5e20,stroke-width:2px class MQTT,Recorder,Frontend service class MosquittoData,MosquittoLog,RecorderStore,AuthFile,ConfigFile storage class Mobile,Browser,External external
安裝服務
Server 端
- 安裝與設定目錄檔案配置
. ├── auth │ └── users.auth ├── docker-compose.yml ├── .env └── mosquitto └── config ├── mosquitto.conf └── passwd
docker-compose.yml
services: mosquitto: image: eclipse-mosquitto:2 ports: - 1883:1883 - 9001:9001 volumes: - ./mosquitto/config:/mosquitto/config - ./mosquitto/data:/mosquitto/data - ./mosquitto/log:/mosquitto/log restart: unless-stopped otrecorder: image: owntracks/recorder ports: - 8083:8083 volumes: - ./store:/store - ./config:/config - ./auth:/auth restart: unless-stopped environment: - OTR_HOST=mosquitto - OTR_PORT=1883 - OTR_USER=${OTR_USER} - OTR_PASS=${OTR_PASS} - OTR_HTTPPORT=8083 - OTR_AUTHFILE=/auth/users.auth depends_on: - mosquitto owntracks-frontend: image: owntracks/frontend ports: - 80:80 environment: - SERVER_HOST=otrecorder - SERVER_PORT=8083 restart: unless-stopped
https://raw.githubusercontent.com/tryweb/docker-compose/refs/heads/main/owntracks/docker-compose.yml
- 到 .env 內設定 recoder 存取 MQTT 的帳號密碼
vi .env
.env
OTR_USER=recorder OTR_PASS=recorderpass
https://raw.githubusercontent.com/tryweb/docker-compose/refs/heads/main/owntracks/.env.example
- 產生 MQTT 的設定檔 mosquitto/config/mosquitto.conf
mkdir -p mosquitto/config/ vi mosquitto/config/mosquitto.conf
mosquitto/config/mosquitto.conf
listener 1883 protocol mqtt listener 9001 protocol websockets allow_anonymous true #allow_anonymous false #password_file /mosquitto/config/passwd log_dest file /mosquitto/log/mosquitto.log log_type error log_type warning log_type notice log_type information #log_type debug #log_type subscribe #log_type unsubscribe persistence true persistence_location /mosquitto/data/ max_inflight_messages 20 max_queued_messages 1000 allow_zero_length_clientid true connection_messages true log_timestamp true
- 產生可存取 MQTT 的帳號密碼檔
touch mosquitto/config/passwd chmod 0770 mosquitto/config/passwd
- 設定直接存取 OTRecorder 的帳號密碼檔 auth/users.auth (不透過 MQTT 直接存取)
mkdir -p auth vi auth/users.auth
auth/users.auth
# OwnTracks 用戶認證檔案 # 格式:username:password # 每行一個用戶 jonathan:mypassword
https://raw.githubusercontent.com/tryweb/docker-compose/refs/heads/main/owntracks/auth/users.auth
- 第一次啟動服務
docker compose pull docker compose up -d
- 查看相關紀錄
- docker compose 紀錄
docker compose logs -f
- MQTT 紀錄
tail -f mosquitto/log/mosquitto.log
- 建立 Recoder 存取 MQTT 的帳號密碼(定義在 .env 內) Exp. recorder / recorderpass
docker compose exec mosquitto chown root:root /mosquitto/config/passwd docker compose exec mosquitto mosquitto_passwd /mosquitto/config/passwd recorder
輸入兩次密碼 recorderpass 即可
- 建立 jonathan 存取 MQTT 的帳號密碼 Exp. jonathan / mypassword
docker compose exec mosquitto mosquitto_passwd /mosquitto/config/passwd jonathan
輸入兩次密碼 mypassword 即可
手機端
直接連 OTRecoder
- Connection 要選 HTTP
- 設定 Endpoint 的 URL Exp. http://192.168.11.200:8083/pub 一定要加上 /pub 才可以
- 設定 Credentials 的 Username 與 Password 要和 auth/users.auth 內設定的相同
連 MQTT (才可看到 Friends)
- Connection 要選 MQTT
- 設定 Endpoint
- Host/Port Exp. 192.168.11.200 / 9001 (WebSocket)
- Client ID Exp. jonathan (建議和 Credentials 的 Username 相同
- Use WebSockets : 啟用
- 設定 Identification
- Device ID Exp. pixel9
- Tracker ID Exp. jt (會顯示在 Android 的地圖上)
- 設定 Credentials (設定在 mosquitto/config/passwd)
- Username Exp. jonathan
- Password Exp. mypassword
- TLS 關閉
- Parameters
- Keepalive : 3600
- Clean Session : 啟動
管理端可以看到畫面
設定 Reverse Proxy 方式
- 這樣的設定好處是, 手機連上哪個網路都可以立即回報, 不用等回到可連上內網的網路才統一回報
透過 Cloudflare Tunnel 設定 OTRecorder 對外服務
目前 MQTT + WebSocket 服務還無法成功透過 Cloudflare Tunnel 設定方式提供對外服務
透過 Nginx Proxy Manager 設定 MQTT + WebSocket 對外服務
常見問題
1. 合併兩個 rec 作法
- 因為在相同裝置 Exp. Android, 修改設定中的 Device ID, Username 就會在主機端自動建立不同的目錄 Exp. Device ID : pixel9 , Username : jonathan 就會在 store/rec 與 store/last 都會建立出 jonathan/pixel9 的目錄
- 如果想要將 store/rec 內的紀錄檔 Exp. 2025-05.rec 進行合併 Exp. jonathan/jonathan/2025-05.rec → jonathan/pixel9/2025-05.rec
- 備份與合併 2025-05.rec
cd store/rec/jonathan/pixel9 cp 2025-05.rec 2025-05.rec.bak cat ../jonathan/2025-05.rec >> 2025-05.rec
- 將合併後的檔案依照資料時間進行排序
sort -s -k 1 2025-05.rec -o 2025-05.rec
- 刪除 data.mdb 資料檔
cd rm -f store/ghash/data.mdb
- 重起 otrecorder 服務
docker compose restart otrecorder docker compose logs -f otrecorder
2. 刪除特定 user 或 device 作法
- 因為在相同裝置 Exp. Android, 修改設定中的 Device ID, Username 就會在主機端自動建立不同的目錄 Exp. Device ID : jonathan , Username : jonathan 就會在 store/rec 與 store/last 都會建立出 jonathan/jonathan 的目錄
- 針對這檔案型的作法就是直接刪除檔案 Exp. 刪除 jonathan/jonathan
cd rm -rf store/rec/jonathan/jonathan/ rm -rf store/last/jonathan/jonathan/ rm -f store/ghash/data.mdb
- 重起 otrecorder 服務
docker compose restart otrecorder
- 刪除 MQTT 的資料後, 重起服務
rm mosquitto/data/mosquitto.db docker compose restart mosquitto
- 在 Android 需要進入 Friends→ 點選要刪除的項目 Exp. jt → 將畫面底下訊息上拉 → 點選 CLEAR