HealthTrack

HealthTrack

Docker app from EzekielTheMad's Repository

Overview

Self-hosted personal health tracker. Track medications, conditions, allergies, labs, vitals, procedures, vaccines, appointments and notes for yourself and your dependents — all data stays in a single SQLite database and uploads folder under /data on your server. No cloud services required. The first registered user becomes the instance admin; after that, registration is invite-only by default (create single-use invite links under Settings, or set SIGNUPS_ENABLED to true to open registration). Optional integrations (each enabled only when its keys are set): Anthropic API key for AI health summaries, natural-language queries and lab/vaccine PDF parsing; Google OAuth for "Sign in with Google"; Oura for ring data sync. Back up /data to back up everything.

HealthTrack

License: MIT CI

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.

HealthTrack dashboard — AI health overview, quick stats with trends, active medications and lab flags

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/vitals and /api/v1/vitals/batch with 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:

Vitals — all metrics view with trends and reference ranges

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:

Vitals — focus view with recovery, body composition and activity summaries

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

Medications with AI interaction status bar

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

Fitness history with expanded strength session

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:

Import medical history review screen with dedupe badges

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_URL at it, e.g. -p 3005:3000 with -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:

  1. 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.
  2. 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.
  3. Delegates — grant another account read-only or read-write access to manage your record (Settings → Delegate Access).
  4. 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 (only http://localhost is exempt), so it will not work against something like http://192.168.1.50:3000 — you must front the app with HTTPS (reverse proxy or tunnel) and set APP_URL to that address. Then, in the Google Cloud Console OAuth client, add this Authorized redirect URI:

    APP_URL/api/auth/callback/google
    
  • Oura Ring is more permissive (a LAN IP is fine). Register its redirect URI in the Oura developer portalnot 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

MIT

Media gallery

1 / 3

Install 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.

Open the Apps tab on your Unraid server Search Community Apps for HealthTrack Review the template variables and paths Click Install

Related apps

Details

Repository
ghcr.io/ezekielthemad/healthtrack:latest
Last Updated2026-07-17
First Seen2026-07-16

Runtime arguments

Web UI
http://[IP]:[PORT:3000]
Network
bridge
Shell
sh
Privileged
false

Template configuration

WebUI PortPorttcp

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
DataPathrw

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
APP_URLVariable

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).

SIGNUPS_ENABLEDVariable

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.

ANTHROPIC_API_KEYVariable

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.

GOOGLE_CLIENT_IDVariable

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).

GOOGLE_CLIENT_SECRETVariable

Optional. Google OAuth client secret (pairs with GOOGLE_CLIENT_ID).

OURA_CLIENT_IDVariable

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

OURA_CLIENT_SECRETVariable

Optional. Oura OAuth client secret (pairs with OURA_CLIENT_ID).

AUTH_SECRETVariable

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.

ENCRYPTION_KEYVariable

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.

PUIDVariable

User ID that owns files under /data (Unraid default: 99 / nobody).

Default
99
Value
99
PGIDVariable

Group ID that owns files under /data (Unraid default: 100 / users).

Default
100
Value
100
TZVariable

Container time zone, e.g. America/New_York.

Default
Etc/UTC
Value
Etc/UTC