Architecture
System Overview
How TravelNet's components fit together — containers, storage, networking, and data flow.
TravelNet runs as three Docker containers on a Raspberry Pi 4B, collecting data 24/7 throughout a 2–3 year trip. The design prioritises reliability over complexity — everything is chosen to survive reboots, network drops, and months of unattended operation.
High-level diagram
1
2
3
4
5
6
7
8
9
10
11
iOS Devices Raspberry Pi 4B
────────────── ─────────────────────────────────────
Overland (GPS) ──────────► nginx (443)
iOS Shortcuts ──────────► └─► ingest (FastAPI :8000)
Health Auto Export ────────► └─► SQLite (external HDD)
Revolut / Wise ──────────►
dashboard (Flask :8080) ← Tailscale only
└─► SQLite (read)
Cloudflare Tunnel
└─► /public/stats ← public internet
The three containers
| Container | Image | Port | Purpose |
|---|---|---|---|
travelnet |
FastAPI (custom) | 8000 | Data ingest — all upload endpoints |
dashboard |
Flask (custom) | 8080 (internal) | Admin UI — view DB, logs, config |
nginx |
nginx:alpine | 80, 443 | TLS termination, reverse proxy |
All three are defined in server/docker-compose.yml and managed together. They share a Docker network (travelnet) so the dashboard can reach the ingest container internally.
Storage
A single SQLite file lives on an external HDD mounted at /mnt/linux/docker/services/travelnet/data/. The HDD is volume-mounted into the travelnet container at /data.
Networking
- Public ingest — nginx handles TLS (cert via Tailscale) on port 443, proxies to FastAPI on 8000
- Private dashboard — only reachable via Tailscale at
pi-server.tail186ff8.ts.net - Public stats endpoint — Cloudflare Tunnel exposes only
GET /public/statsatapi.travelnet.dev
Remote access
Tailscale provides the VPN layer. The Pi is always reachable at pi-server.tail186ff8.ts.net as long as it has internet — even across hotel WiFi, mobile data, or NAT.
Reliability features
- All containers have
restart: unless-stopped - Cloudflare Tunnel runs as a
systemdservice, starts on boot - Offsite backup to Cloudflare R2 runs on the 2nd of each month (age-encrypted, rclone)
- Email alerts fire on any cron failure via
CronJobMailer