Self-hosted sleep and energy tracker. Meridian turns sleep data into alertness forecasts, tracks sleep debt, and sends timed notifications via ntfy.
One Go binary. One SQLite file.
docker run -d \
--name meridian \
-p 8090:8090 \
-v meridian_data:/pb_data \
ghcr.io/drewbitt/meridian:latestThen open http://localhost:8090.
On first launch, PocketBase will print a one-time setup URL to create a superuser account — open it in your browser. The log also suggests a CLI command, but that won't work because the image is distroless (no shell). Use the browser URL instead.
After that, register a regular user account at /register and log your first night of sleep.
Once you've registered, disable public signups:
docker run -d \
--name meridian \
-p 8090:8090 \
-e ALLOW_REGISTRATION=false \
-v meridian_data:/pb_data \
ghcr.io/drewbitt/meridian:latestservices:
meridian:
image: ghcr.io/drewbitt/meridian:latest
ports:
- "8090:8090"
volumes:
- meridian_data:/pb_data
environment:
- ALLOW_REGISTRATION=false # set to true for first run
- TZ=America/New_York # your timezone
restart: unless-stopped
volumes:
meridian_data:docker build -t meridian .
docker run -d -p 8090:8090 -v meridian_data:/pb_data meridianData lives in /pb_data (SQLite database + uploads). Back up this directory.
- FIPS Three Process Model for alertness prediction (homeostatic pressure, circadian rhythm, post-lunch dip, sleep inertia)
- 14-day weighted sleep debt -- recent nights count more, accuracy improves with more data
- Energy zones: morning peak, afternoon dip, evening peak, wind-down, melatonin window
- Notifications via ntfy -- caffeine cutoff, focus windows, energy dips, bedtime
- 5 data sources: manual entry, Fitbit (OAuth2, auto-sync every 4h), Health Connect, Apple Health, Gadgetbridge
- Dark-themed dashboard with real-time energy curve (Chart.js + Datastar SSE)
| Source | Method | Sync |
|---|---|---|
| Manual | Web form | -- |
| Fitbit | OAuth2 | Every 4 hours |
| Health Connect | JSON upload | Manual |
| Apple Health | ZIP/XML upload | Manual |
| Gadgetbridge | SQLite upload | Manual |
- Create a Personal app at dev.fitbit.com/apps/new
- Callback URL:
https://your-domain/auth/fitbit/callback(orhttp://localhost:8090/auth/fitbit/callbacklocally) - Default Access Type: Read-Only (other URL fields can be placeholders)
- Copy Client ID and Client Secret into Settings, save, click Connect
Backfills the last 30 days on first connect.
Fitbit's Web API is scheduled for deprecation in September 2026.
| Variable | Default | Description |
|---|---|---|
ALLOW_REGISTRATION |
true |
Set to false (or no, off, 0) to disable new account creation |
TZ |
UTC |
Timezone for cron jobs (e.g. America/New_York) |
Per-user settings are on the Settings page:
- Timezone (e.g.
America/New_York) — auto-detected from Fitbit or browser, falls back to serverTZ - Sleep need (default 8 hours)
- ntfy server, topic, and access token
- Fitbit OAuth2 credentials
- File imports for Health Connect, Apple Health, and Gadgetbridge
PocketBase admin panel is at /_/ for superuser operations.
| Layer | Technology |
|---|---|
| Backend | PocketBase (Go) -- auth, cron, SQLite, admin |
| Frontend | Datastar + Templ + Tailwind CSS |
| Engine | FIPS Three Process Model in Go (~250 lines) |
| Notifications | ntfy (single HTTP POST) |
| Database | SQLite (embedded) |
./bin/mise install # installs Go, templ, air, tailwind
./bin/mise run dev # templ watch + tailwind watch + hot reloadNo setup beyond cloning. The mise bootstrap script in bin/ handles everything. Git hooks auto-configure on directory entry.
http://localhost:8090 for the app, /_/ for the PocketBase admin.
./bin/mise run test
./bin/mise run build # -> ./meridian
./bin/mise tasks # list all commandsThis repo includes samber/cc-skills-golang as a Git submodule at .agents/skills/. These skills are auto-discovered by OpenCode, Claude Code, and other agents that follow the agentskills.io spec.
git submodule update --remote # pull latest skill updates35 skills covering Go best practices — tested against a baseline, +44pp improvement in agent task quality (98% vs 54%). See individual skills in .agents/skills/skills/<name>/SKILL.md.