Track Every Rep, Set, and PR
A self-hosted weightlifting tracker.
No accounts, no telemetry, no cloud sync unless you opt in.
Coming to Apple devices: the Trace apps have no iPhone app yet, because building and testing one needs a Mac and an iPhone. Chip in on Ko-fi. Self-hosting stays free either way.
Jump to: What it is · Features · Install · Env vars · Docs
LiftTrace runs entirely in a single Docker container on your own hardware, with a PWA for the browser and a native Android app for your phone. No accounts on external services, no data leaving your network, no subscriptions. Log workouts, run programs, browse a multi-source exercise library, and ask an AI coach for form checks, all against your own SQLite file.
- Self-hosting is free. No paid tiers, no premium-only features behind a subscription, no nag screens.
- No telemetry. LiftTrace never phones home. There is no central server that receives your data, no analytics, no crash reporting, no fingerprinting.
- Your data, your hardware. Everything lives in a single SQLite file and an uploads folder on your machine. Back up either with
cp, restore withcp, migrate to a new server by moving the volume. - AGPL-3.0. The source is open and the network-use clause keeps it that way.
- Diary and set logging. Weight, reps, RPE, warm-up marking, Smart Add prose parser, superset-aware rest timer, floating workout mode bar, workout completion share card. → full guide
- Programs and templates. Starter templates (Push/Pull/Legs, Upper/Lower, Full Body 3x), multi-week progression with Sessions or Calendar advance modes (v1.0.1). → full guide
- Exercise library. Four sources: wger (~600, CC-BY-SA), Free Exercise DB (~870, public domain), ExerciseDB (~1,300, RapidAPI BYO key), and ExerciseDB Open Source (self-hostable mirror, no key). Plus custom exercises and XLSX bulk import. → full guide
- Statistics and PRs. Metric-pill layout with Overview, Exercise Progress, Records, Volume, Frequency, and Body Weight views; automatic PR detection. → full guide
- Progress photos. Dated photos alongside your body stats, with a drag-to-compare before/after view for any two dates. Photos never expire, sync across devices, and are included in full backups. → full guide
- Trace AI coach. Reads your workouts, programs, PRs, body stats, and coach prescriptions; can log a workout, prescribe (if you are a coach), start a program template, or update your active program, all conversationally. 18 tools total. Multi-provider (Claude / OpenAI / Gemini / any OpenAI-compatible endpoint). Hold-to-record voice log, FFT visualizer. → full guide
- Radio player. Stream from Subsonic (Navidrome, Airsonic, Funkwhale, Gonic), Jellyfin, Plex, Emby, plus Icecast/Shoutcast/HLS internet radio with now-playing metadata. → full guide
- Coaching with consent. A coach asks and the client accepts before anything is shared, and either side can end it. The coach assigns programs, prescribes workouts the client accepts or declines, leaves notes on workouts and exercises, and talks with the client in one conversation (text and photos, read receipts, works offline). → full guide
- Set videos. Film a set in the app or pick a clip, and your coach leaves a note stamped at the moment that matters. Only you and your coach can open a clip. → full guide
- Workout history import. Bring your old log in from Strong, Hevy, FitNotes, Jefit (CSV) or Garmin FIT files. → full guide
- Multi-user and OIDC SSO. Role-based access (admin/trainer/member), invites, session policy, plus OIDC 1.0 SSO via Authentik, Keycloak, Pocket ID, Authelia, Google, Auth0, or any compliant provider. → full guide
- Works without a connection. In a gym with no signal the web app still opens, today's workout is there, and you keep logging: sets, body stats, cardio, your own exercises and settings. Changes go up on their own when the connection returns, merged set by set. → full guide
- MCP and REST API. AI clients and scripts read your workouts, records, progress and body stats over a date range, and can log sets and body stats. Off by default. → full guide
- Native Android app. Local-only mode with on-device SQLite, or connect to your self-hosted server for sync. Media3 ExoPlayer, WorkManager reminders, biometric sign-in. → full guide
- Wear OS. A watch app for Wear OS 3 and up: the set you are on with weight and reps already filled in, what you lifted last time, the session timer, started, paused and stopped from either device, any set in the session open to change, sets added or put back to not done, supersets alternating properly, holds counted down and logged, and the rest timer buzzing on your wrist even with the watch face back, plus a tile and a watch face complication. Finish the workout without touching the phone. It talks to your server itself, so it keeps logging in a basement gym with the phone in a locker, and it pairs itself when you sign in on the phone. → full guide
- Foldables (preliminary). Half open like a book, Settings puts its section list on one side of the crease and the section on the other, and dialogs, sheets, the rest timer and Trace keep off the fold. Opened flat or on a tablet, Programs and Exercises split into a list and a preview pane, and the Diary keeps its session column beside the exercises.
- Web (PWA). Install from any modern browser via the address bar (Chrome) or share menu (Safari). Keeps working with no connection, in a tab or installed.
- Android. Sideload the signed APK from the Releases page. Release APKs require HTTPS to your server; debug APKs accept plain HTTP. See DEPLOY.md for the four supported paths.
- Wear OS. A separate APK for the watch, on the same Releases page. Sideload it onto a Wear OS 3 watch; it pairs itself the next time you open LiftTrace on the phone, and from then on it updates itself to the phone's release.
- iOS. No native build yet. Install the PWA from Safari (Share → Add to Home Screen).
Published to two registries with identical tag sets: ghcr.io/traceapps/lifttrace (primary) and traceapps/lifttrace on Docker Hub (mirror). The snippet below uses GHCR; swap in traceapps/lifttrace:latest if that suits your setup.
Unraid: LiftTrace is in Community Applications. Search for LiftTrace in the Apps tab; the template sets the port, the appdata folders and the settings. Fill in JWT Secret before the first start.
services:
lifttrace:
image: ghcr.io/traceapps/lifttrace:latest
container_name: lifttrace
ports:
- "3002:3002"
volumes:
- ./data/db:/data/db
- ./data/uploads:/data/uploads
environment:
- DB_PATH=/data/db/lifttrace.db
- UPLOADS_PATH=/data/uploads
- JWT_SECRET=change-me-to-a-long-random-string
restart: unless-stoppeddocker compose up -dOpen http://localhost:3002 and you're lifting.
See DEPLOY.md for image tag conventions, reverse proxies, subpath mounting, Cloudflare Tunnel, Docker secrets, and the four Android HTTPS paths.
Pre-release testers can grab the rolling dev-latest APK; occasional milestone builds also get numbered -devNN pre-releases. See DEPLOY.md for details.
The essentials. Full reference in DEPLOY.md, .env.example, and the env vars docs page.
| Variable | Default | Purpose |
|---|---|---|
DB_PATH |
./lifttrace.db |
SQLite database file path |
UPLOADS_PATH |
./uploads |
Uploaded exercise media directory |
JWT_SECRET |
(required in prod) | JWT signing secret; server refuses to start in prod with the dev default |
TOKEN_ENC_KEY |
derived from JWT_SECRET |
At-rest encryption key for OIDC client secrets |
PORT |
3002 |
Server port inside the container (3003 before 1.3.0) |
LOG_LEVEL |
info |
error | warn | info | debug |
EXERCISE_SOURCES |
wger,free-db |
Sources to auto-seed on first boot (wger, free-db, exercisedb, exercisedb-oss) |
EXERCISEDB_OSS_URL |
(upstream) | Point the OSS exercise source at your own mirror |
INSECURE_COOKIES |
0 |
Set 1 to send the sign-in cookie over plain HTTP too. The cookie is HTTPS-only by default, so at a plain http:// address other than localhost (a LAN IP, an Unraid WebUI link) the browser drops it and signing in loops back to the sign-in page; the sign-in and setup screens say so. Use 1 only on a trusted LAN, and leave it 0 behind HTTPS. See Plain-HTTP LAN. |
BASE_URL |
(none) | Mount at a subpath (e.g. /lifttrace) instead of root |
PUBLIC_URL |
(none) | The full address people open the app at, subpath included (e.g. https://lift.example.com). Links in emails (password reset, invites, coaching, the weekly summary) use it. Unset, they use an address an admin has opened the app at. |
UPDATE_CHECK |
(none) | Set off to stop the server asking GitHub about new releases for good. Unset, update checks stay off until setup or Settings, Updates turns them on. |
RECOVERY_TOKEN |
(none) | Token for the "Disable user management" recovery endpoint |
SMTP_* |
(none) | SMTP for password reset emails and user invites |
AI_* |
(none) | Server-side AI proxy; AI_PROVIDER accepts claude | openai | gemini | oai-compat |
OIDC_* / OIDC_PROVIDER_N_* |
(none) | OIDC SSO declared in env instead of the UI |
OIDC_ENABLE_EMAIL_PASSWORD_LOGIN |
(none) | Set to 0 to disable password login server-wide (SSO-only) |
All data lives in two bind-mounted directories: /data/db/lifttrace.db (SQLite) and /data/uploads/ (exercise media and backup ZIPs). Back up both with cp -r, restore by stopping the container and putting them back. Update with docker compose pull && docker compose up -d; volumes persist across updates.
- Frontend: Svelte 5 (Svelte-4 compatibility mode), svelte-spa-router, Vite 6, custom SVG charts
- Backend: Express 5, better-sqlite3, bcryptjs 3, cookie-based JWT auth
- PWA: vite-plugin-pwa, installable on any device
- Android: Capacitor 8, Media3 ExoPlayer, WorkManager, @capacitor-community/sqlite
- AI: Multi-provider (Claude, OpenAI, Gemini, OpenAI-compatible) with multimodal image support
- Deploy: Docker multi-stage build, GitHub Actions CI → GHCR
Part of the TraceApps family. Sister apps: CookTrace for recipes and pantry, NutriTrace for nutrition tracking, NoteTrace for notes, tasks and reminders. Docs for the family at traceapps.github.io/docs.
LiftTrace is translated with Weblate, a free web platform for libre translation projects. No coding required: pick your language, translate the strings, submit. Missing strings fall back to English, so a partial translation is safe to use.
Start translating at hosted.weblate.org/projects/lifttrace/. See CONTRIBUTING.md for guidance (lifting terms, proper nouns, tone).
- ROADMAP.md for what's next.
- CHANGELOG.md for release history.
- CONTRIBUTING.md for pull-request guidance.
- AGPL-3.0: see LICENSE. By contributing you agree your contributions are licensed under the same.
LiftTrace is free to self-host and always will be. No paid tier, nothing behind a donation, no telemetry. It's built and maintained by one person.
The current goal is a Mac and an iPhone. None of the Trace apps run properly on an iPhone, because building and testing for iOS needs Apple hardware, plus the developer accounts for both app stores. That comes to about $1,300, and the itemised breakdown is on the Support page.
Helping doesn't have to cost anything: starring the repo, reporting bugs with detail, and translating all count, and stars are how self-hosted projects get found.
LiftTrace is not medical, health, or fitness-professional software. Exercise library content, Trace AI coaching, program templates, rest-timer guidance, and any analytical output are for informational and self-tracking purposes only. Resistance training carries inherent injury risk; consult a qualified healthcare professional or certified coach before starting a new program, returning from injury, or making significant changes. Trace AI answers can be incorrect; treat them as a starting point, not a substitute for professional advice. Use at your own risk.
