All apps · 0 apps
HealthTrack
Docker app from EzekielTheMad's Repository
Overview
Readme
View on GitHubHealthTrack
Self-hosted personal & family health tracker. Medications, conditions, allergies, labs, vitals, procedures, vaccines, appointments and notes — for you and your dependents — in a single Docker container. All data stays in one SQLite database and an uploads folder on your server. No cloud services required.

Features
- Medications — dosages, schedules, active/inactive history, AI interaction checks*
- Conditions, allergies, procedures, vaccines — full clinical history
- Labs — visits and results with reference ranges, plus AI-powered PDF parsing of lab and vaccine reports*
- Vitals — blood pressure, heart rate, weight, glucose and more, with reference ranges and trend charts
- Family & dependents — track children or family members under your account, with a transition flow when they grow up
- Sharing — share selected sections of your (or a dependent's) health record with another user, with expiry
- Delegates — grant another user read-only or read-write access to manage a record
- Medical-history import* — upload a doctor-provided history PDF (yours or a dependent's) and AI extracts medications, conditions, allergies, procedures, vaccines, and lab results in one pass; large documents are processed in page chunks, and a review screen flags anything already on record before import
- Fitness tracking — log workouts manually or via the API (set shorthand like
185x5 x3), with history, trend charts (est. 1RM, PR badges), weekly rollups, goals, and an exercise catalog - API access — personal access tokens with scoped permissions for the REST API (
/api/v1/...), including read endpoints for every domain - Device integrations / bring your own bridge — push metrics from any device (CPAP, smart scale, watch, CGM, …) via
POST /api/v1/vitalsand/api/v1/vitals/batchwith idempotent upserts; see docs/API.md. Every instance self-documents at/docs/api(human cookbook) and/api/v1/openapi.json(OpenAPI 3.1) — both public, API-shape only - AI health assistant* — natural-language questions about your data, health summaries
- Oura Ring sync* — sleep, heart rate and activity data
- Sign in with Google* — alongside built-in email/password auth
* Optional. AI features need an ANTHROPIC_API_KEY, Google login needs a Google OAuth client, Oura sync needs an Oura OAuth client. Each feature is hidden in the UI until its keys are configured — the core tracker is fully functional without any of them.
Screenshots
All screenshots show a demo account with fictional data.
Vitals & wearables — every metric grouped by domain, with trend sparklines, reference-range bars, and per-device source badges. Data flows in from bridges, the API, or manual entry:

Focus view — the day-to-day question ("how am I doing?") answered at a glance: recovery verdict, body-composition trend, and weekly activity vs. your 30-day norms:

Medications — active/past history with categories, plus AI interaction checks that run automatically when your med list changes:

Fitness — workout history with per-set detail, working-set summaries, trends with estimated 1RM and PR badges, weekly rollups and goals:

Medical-history import — upload a doctor-provided PDF; AI extracts medications, conditions, allergies, procedures, vaccines and labs in one pass, deduplicates against what's already on record, and imports only what you approve:

Quick start
docker run -d \
--name healthtrack \
-p 3000:3000 \
-v ./data:/data \
-e APP_URL=http://localhost:3000 \
ghcr.io/ezekielthemad/healthtrack:latest
Open http://localhost:3000 and register — the first user becomes the instance admin. After that, registration is invite-only by default: create single-use invite links under Settings → Invites to add family members. (Set SIGNUPS_ENABLED=true to open registration to anyone, or false to close it completely.)
Port 3000 already in use? Map a different host port and point
APP_URLat it, e.g.-p 3005:3000with-e APP_URL=http://localhost:3005. The container always listens on 3000 internally; only the host side changes.
Docker Compose
See docker-compose.yml:
docker compose up -d
Unraid
HealthTrack ships a Community Applications template: unraid/healthtrack.xml. See docs/UNRAID.md for install instructions (via CA or manual template URL).
Adding users & family
Registration is invite-only by default — nobody can create an account on your instance without your say-so. The ways to bring people in:
- Invite links (new accounts) — as admin, go to Settings → Invites,
create a link (optionally note who it's for) and send it. Each link is
single-use and expires after 7 days. The recipient registers with email
- password; afterwards they can also use "Sign in with Google" on the same email (accounts link automatically). Revoke unused links any time.
- Dependents (no account needed) — track children or family members as profiles under your own account (Settings → Family & Dependents), with a transition flow to hand the record over when they grow up.
- Delegates — grant another account read-only or read-write access to manage your record (Settings → Delegate Access).
- Sharing — share selected sections of a record with another account, with expiry (Settings → Health Sharing).
Prefer open registration (e.g. a trusted LAN)? Set SIGNUPS_ENABLED=true.
Set SIGNUPS_ENABLED=false to close registration completely, invites included.
Configuration
All configuration is via environment variables (see .env.example):
| Variable | Required | Default | Purpose |
|---|---|---|---|
APP_URL |
yes | http://localhost:3000 |
Absolute URL of the instance — must match what users browse to; used for auth callbacks |
AUTH_SECRET |
no | auto-generated to /data/keys |
Session signing secret |
ENCRYPTION_KEY |
no | auto-generated to /data/keys |
Encrypts stored OAuth tokens |
SIGNUPS_ENABLED |
no | invite-only | Unset: first account registers freely (becomes admin), everyone after needs a single-use invite link (Settings → Invites). true: open registration. false: fully closed |
ANTHROPIC_API_KEY |
no | — | Enables AI features (summaries, queries, PDF parsing, interaction checks) |
ANTHROPIC_MODEL |
no | current Sonnet | Claude model for reasoning features (summaries, queries, interaction checks) — e.g. claude-opus-4-8 |
ANTHROPIC_MODEL_EXTRACTION |
no | current Sonnet | Claude model for PDF extraction — a cheaper model like claude-haiku-4-5 works well |
GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET |
no | — | Enables "Sign in with Google" |
OURA_CLIENT_ID / OURA_CLIENT_SECRET |
no | — | Enables Oura Ring sync |
PUID / PGID |
no | 99 / 100 |
Ownership of files under /data (Unraid conventions) |
TZ |
no | Etc/UTC |
Container time zone |
Reverse proxy / HTTPS
HealthTrack serves plain HTTP on port 3000; put your reverse proxy of choice (Nginx Proxy Manager, Caddy, Traefik, Cloudflare Tunnel, …) in front of it for HTTPS. APP_URL must exactly match the URL users browse to (scheme, host, port) or login callbacks will fail.
Authentication & OAuth
Built-in email/password sign-in works anywhere, including a bare LAN IP — nothing extra to configure. The optional social logins have provider-specific requirements:
Sign in with Google requires a public HTTPS URL on a real hostname. Google's OAuth rejects
http://and raw IP-address redirect URIs (onlyhttp://localhostis exempt), so it will not work against something likehttp://192.168.1.50:3000— you must front the app with HTTPS (reverse proxy or tunnel) and setAPP_URLto that address. Then, in the Google Cloud Console OAuth client, add this Authorized redirect URI:APP_URL/api/auth/callback/googleOura Ring is more permissive (a LAN IP is fine). Register its redirect URI in the Oura developer portal — not the Google console; they are separate providers with separate credentials:
APP_URL/api/oura/callback
In every case APP_URL must exactly match the URL your browser uses (scheme, host and port), or the OAuth round-trip fails. Restart the container after changing APP_URL. The first user to sign in — by any method — becomes the admin.
Backup
Everything lives under /data: the SQLite database (healthtrack.db), uploaded PDFs (uploads/) and auto-generated secrets (keys/). To back up: stop the container, copy the /data directory, start it again. Restore is the reverse.
Development
npm install
npm run dev # http://localhost:3000, state lands in ./data
npm test # vitest
npm run lint
npm run build
Local development state (SQLite db, uploads, generated keys) lives in ./data (override with DATA_DIR). Database tests run in the node environment — see existing *.test.ts files for the environment pragma convention.
Disclaimer
HealthTrack helps you organize health information. It is not a medical device and does not provide medical advice, diagnosis or treatment — always consult a qualified healthcare professional. AI-generated content can be wrong. You run this software self-hosted, on your own infrastructure, at your own responsibility; review SECURITY.md before exposing an instance to the internet.
License
Media gallery
1 / 3Install HealthTrack on Unraid in a few clicks.
Find HealthTrack in Community Apps on your Unraid server, review the template, and click Install. Unraid handles the Docker app or plugin setup from the published template.
Categories
Related apps
Explore more like this
Explore allDetails
ghcr.io/ezekielthemad/healthtrack:latestRuntime arguments
- Web UI
http://[IP]:[PORT:3000]- Network
bridge- Shell
sh- Privileged
- false
Template configuration
Host port for the HealthTrack web interface. If another container already uses 3000 (Mealie, Grafana, etc.), change this to a free port and update APP_URL to match — the container always listens on 3000 internally.
- Target
- 3000
- Default
- 3000
- Value
- 3000
All application state: SQLite database (healthtrack.db), uploaded lab PDFs (uploads/) and auto-generated secrets (keys/). Back up this folder to back up everything.
- Target
- /data
- Default
- /mnt/user/appdata/healthtrack
- Value
- /mnt/user/appdata/healthtrack
The URL users browse to — required for login/auth callbacks to work. Use your server's address and the mapped port, e.g. http://YOUR_SERVER_IP:3000 (or your reverse-proxy HTTPS URL if you use one).
Registration policy. Leave EMPTY (recommended): the first account registers freely and becomes admin, then new accounts require a single-use invite link (Settings > Invites). Set to true to open registration to anyone, or false to close it completely.
Optional. Anthropic API key — enables AI features: health summaries, natural-language health queries, medication interaction checks and lab/vaccine PDF parsing. AI features are hidden when unset.
Optional. Google OAuth client ID — enables the 'Sign in with Google' button. REQUIRES serving over HTTPS on a real hostname (reverse proxy or tunnel): Google rejects http:// and LAN-IP redirect URIs, so this will NOT work on a bare 192.168.x.x address. In the Google Cloud Console, add this authorized redirect URI: APP_URL/api/auth/callback/google (email/password login works without any of this).
Optional. Google OAuth client secret (pairs with GOOGLE_CLIENT_ID).
Optional. Oura OAuth client ID — enables Oura Ring sync (sleep, heart rate, activity). In the Oura developer portal, set the redirect URI to: APP_URL/api/oura/callback
Optional. Oura OAuth client secret (pairs with OURA_CLIENT_ID).
Advanced. Session signing secret. Leave empty — one is auto-generated into /data/keys on first start. Set only if you want to manage it yourself.
Advanced. Key used to encrypt stored OAuth tokens (Oura). Leave empty — one is auto-generated into /data/keys on first start. Set only if you want to manage it yourself.
User ID that owns files under /data (Unraid default: 99 / nobody).
- Default
- 99
- Value
- 99
Group ID that owns files under /data (Unraid default: 100 / users).
- Default
- 100
- Value
- 100
Container time zone, e.g. America/New_York.
- Default
- Etc/UTC
- Value
- Etc/UTC