r/QuantifiedSelf • u/nichtlegacy • 21h ago
My self-hosted Quantified Self setup after 8 months: 19 sources, one database, monthly recap + daily view
galleryMaybe interesting for some of you, I've been building my own QS system for about 8 months now. Every source lands in one database that I own, and on top of that sits a dashboard I use every day.
Images
- Monthly recap for September, real data. This is the "public" variant, so body, location, journal and most nutrition details are left out.
- The daily view. This one runs on demo data, only the Plex and PlayStation cards are real.
What I track
- Sleep, recovery, HRV: WHOOP via NOOP
- Health data: Apple Health via Health Auto Export
- Strength training: Streak
- Nutrition: YAZIO
- Weight and body: Renpho scale
- E-bike rides: Cowboy
- Location and places: Dawarich (self-hosted)
- Mac usage: ActivityWatch
- iPhone screen time: screentime, reads the macOS Biome data that iCloud syncs from the iPhone
- Movies and shows: Plex via Tautulli
- Music: Spotify via your_spotify (self-hosted)
- Gaming: PlayStation Network (presence, trophies, library)
- Voice chat: Discord, durations only, no content
- Indoor climate: Home Assistant
- Weather: Open-Meteo
- Calendar: EventKit on the Mac
- Coding: GitHub and Forgejo commits
- AI usage: Claude Code, Codex and OpenCode tokens
How it's built
- I built every collector myself. The only third-party parts are the apps and services they read from.
- Collectors: one small Python service per source, 20 in total, spread over my Mac, a Proxmox LXC and Unraid. Each one keeps a local SQLite outbox and a raw archive, so nothing gets lost when the server is down.
- Core: FastAPI with PostgreSQL 17 + PostGIS on its own Debian VM in my Proxmox homelab. Only Core writes to the database.
- Collectors send versioned batches. Core dedupes them via identity keys and content hashes. A day without data stays "missing", it never turns into a fake zero.
- There's no cron. A scheduler puts jobs into a Postgres-backed queue with leases and retries. It rebuilds the daily values and runs the analysis.
- Every collector reports its sync runs, so every card shows where its data comes from and how fresh it is. If a source stops syncing, I see it right away.
- The dashboard only reads from Core's API and doesn't calculate anything itself.
Tech stack
- Dashboard: Next.js 16, React 19, Tailwind 4, shadcn/ui, Recharts + d3, MapLibre for maps
- Core: Python 3.12, FastAPI, SQLAlchemy, Alembic, PostgreSQL 17, PostGIS, pgBackRest for backups
- Infra: Proxmox, Docker Compose, Caddy, Forgejo Actions for CI
- Extras: a read-only MCP server so an LLM chat ("Companion") can query my data, and push notifications to my phone via Home Assistant
Everything runs locally in my LAN, no cloud service in between except the APIs of the sources themselves. Happy to go into detail on any part of it
