# openGym
**Repository Path**: Super_Rainbow/openGym
## Basic Information
- **Project Name**: openGym
- **Description**: No description available
- **Primary Language**: Unknown
- **License**: AGPL-3.0
- **Default Branch**: main
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2026-08-21
- **Last Updated**: 2026-08-21
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
**A self-hosted gym & body-weight tracker you actually own.**
Plan your week, run guided workouts, track every set and your body weight over time β
on your phone, synced across devices, behind your own passkey login.
No account on someone else's server, no subscription, no ads. Just `docker compose up`.
[](LICENSE)






[](https://github.com/DuarteSantos8/openGym/stargazers)
[](https://github.com/DuarteSantos8/openGym/issues)
 Home β today's workout & weight |
 Guided workout β animated demos & sets |
 Stats β heatmap, charts & PRs |
### [π opengym.duarte-santos.ch](https://opengym.duarte-santos.ch) Β· [βΆ Try the live demo](https://duartesantos8.github.io/openGym/)
No signup, nothing to install β it runs entirely in your browser on example data.
There's no server behind the demo, so passkey sign-in, sync across devices and the
admin dashboard only exist in a self-hosted instance.
## Why
Most workout apps lock your data behind a login on their servers, nag you to upgrade, or
disappear when the startup does. openGym is the opposite: **it runs on your box, your data
stays in a folder you control, and it's yours to fork.** It still feels modern β installable
as a home-screen app, passkey sign-in, offline support, sync across your phone and laptop.
## Features
- βοΈ **Body-weight tracking** β interactive chart with a goal line you set, gains/losses colored by whether they move toward it
- ποΈ **Weekly plan** β a routine per weekday, over a library of **1,324 exercises** (searchable, with animated demos)
- ποΈ **Reschedule any day** β sick, missed a session, or fewer gym days this week? Move a workout to another day without touching your weekly plan
- βΆοΈ **Guided workouts** β it knows what day it is and starts today's session; asks your body weight first, pre-fills your weights from last time, rest timer, PR detection, per-exercise weight tracking
- βοΈ **The screen stays awake while you train** β no unlocking the phone and finding your place again between every set. On for as long as a workout is running, released the moment you finish it, and switchable off in Settings
- π **Supersets** β build them, and log them back-to-back with a rest only after the pair
- β±οΈ **Timed exercises** β planks, hangs, wall sits and loaded carries are logged by time, not reps, with a work timer that counts the set itself (separate from the rest timer) and logs the time you actually held. They can carry weight too
- π **Progression that follows a rule** β pick one per routine, override it per exercise: linear, **Greyskull LP** (AMRAP top set, double jumps, 10 % resets), double progression through a rep range, or adding time. Your weights are already right when the session opens, and every target says *why* it's that number. Missed reps never advance the load, stalls trigger a deload, and bodyweight exercises progress in reps instead
- πͺ **Estimated 1RM** β per exercise, from your best eligible set (it names which one), with its own progress curve and a calculator for sets you haven't done. Won't guess above 12 reps
- π― **Effort per set, in your scale** β an optional third column rating how hard a set was, as **RIR** (reps left in the tank) or **RPE** (the same judgement on a 10-point scale). Off by default; each set keeps the scale it was logged with, and nothing else reads the value β your progression and 1RM are unaffected
- πͺ **Bodyweight exercises, logged as bodyweight** β push-ups, pull-ups, dips and 300-odd others arrive knowing they carry no load, so there's no weight column and no working-weight prompt: one stepper, log the reps. Add a dip belt and it reads as an addition, and progression goes back to following the weight. Without one, reps climb β and past a ceiling you set, a set is added instead of a rep, up to the point where the honest advice is load or a harder variation
- βοΈ **Reps per side** β for lunges, single-arm rows and the rest. You log the total, the app shows the split ("8 per side"), and the target steps in twos so it never lands on a number one side can't have
- π **Cardio** β log time + speed, not just weight Γ reps
- π€ **Share a plan** β send someone your routines and week schedule as a small file (no workouts, no weigh-ins), or print it as a clean PDF. Importing merges, so their plan is never overwritten
- π§ **Filter by equipment** β narrow the library to what you actually own; the options adapt to what you've picked, so every combination on screen has results behind it
- β¨ **Your own exercises** β a name and a body part is enough; they behave like built-in ones everywhere, with an optional description instead of an animation
- π© **Activity heatmap** β a GitHub-style year view, shaded by time spent training
- πͺ **Muscle map** β a front-and-back body diagram shaded by how much work each muscle got, over a week, a month or all time. It names the muscles you *haven't* trained in that period, previews what a routine hits while you build it, and shows what you just trained when you finish. Male or female figure, your pick
- π **Push notifications** β rest-timer alerts even with the app closed, plus an optional reminder on days you have a workout planned but haven't logged one. Opt in per profile; keys are generated on first run, nothing to configure
- π **Passkeys, not passwords** β Face ID / Touch ID / fingerprint login; each profile keeps its own data, synced across devices
- π οΈ **Admin dashboard** (optional) β for whoever runs the instance: who's training right now, per-user history, disable accounts, and invite-only signup. Off by default, so a fresh instance stays open with no admin
- π¨ **Designed, not assembled** β light/dark themes and 8 accent colors saved to your profile, over a hand-drawn icon set instead of emoji, so it looks the same on every phone
- π **12 languages** β full UI translation (EN, DE, ES, FR, IT, PT, PL, TR, RU, ZH, KO, HI); exercise instructions localized in 10 of them, loaded on demand so the app stays fast
- π₯ **Bring your history with you** β import from **FitNotes** (Android and iOS), **Strong** and **Hevy**, or body weight straight out of an **Apple Health** export. Exercise names are matched against the library and anything unrecognised becomes one of your own exercises, so nothing in the file is dropped
- π¦ **Yours to keep** β one-tap JSON export/import, guest mode, **no telemetry**
- π± **Standalone Android app** β the whole tracker as a sideloadable APK: no account, no server, data on the phone, native workout reminders ([download](https://opengym.duarte-santos.ch))
## Quick start (self-host)
You need [Docker](https://docs.docker.com/get-docker/) with Compose.
```bash
git clone https://github.com/DuarteSantos8/openGym
cd openGym
cp .env.example .env
docker compose pull # grab prebuilt images (amd64 + arm64) β skip to build from source instead
docker compose up -d
```
Open **http://localhost:8080**, tap **Create profile**, and you're in. First launch downloads
the exercise media (~140 MB) once. Prefer building the images yourself instead of pulling from
`ghcr.io`? Drop the `pull` step and run `docker compose up -d --build` β you don't need Node or
a build step locally either way.
> Want it reachable from your phone over the internet with passkeys? You'll need an HTTPS
> domain β a two-line change in `.env`. See **[docs/SELF_HOSTING.md](docs/SELF_HOSTING.md)**.
## Mobile app (no server at all)
The same codebase also builds a **standalone mobile app** (Capacitor): no account, no sync,
no backend β everything stays on the phone, with native workout-day reminders and share-sheet
backups. Self-hosting gets you multi-device sync and profiles for friends & family; the
mobile app is the install-and-done flavor.
- **Android:** [**download the APK**](https://opengym.duarte-santos.ch) and sideload it β
openGym is deliberately not on the Play Store. Or build it yourself: **[docs/MOBILE.md](docs/MOBILE.md)**.
- **iPhone:** Apple doesn't allow installing apps outside the App Store, so there is no iOS
download. Self-host and add it to your home screen from Safari (it's a full PWA), or build
the native app onto your own device from Xcode β see **[docs/MOBILE.md](docs/MOBILE.md)**.
## How it works
```
βββββββββββββββ ββββββββββββββββββββββββββββββββ
β Your phone βββHTTPSββΆβ web (nginx) β
β / laptop β β ββ serves the built app β
βββββββββββββββ β ββ proxies /api ββββββββββββ
βββββββββββββββββββββββββββββββββ
βΌ
ββββββββββββββββββββββββββββ
β api (Node + WebAuthn) β
β ββ ./data (JSON files) β
ββββββββββββββββββββββββββββ
```
- **frontend/** β React + Vite (React Router + Zustand), built to static files **inside Docker**
- **api/** β Node with no framework, one dependency (`@simplewebauthn/server`), storing everything as plain JSON files under `./data`
- **web/** β a multi-stage image that builds the frontend and serves it with nginx, proxying `/api` to the backend so it's all on **one origin** (passkeys require this)
## Your data
Lives in `./data` on your host: `db.json` (profiles + public passkeys), `state-.json`
(each user's plan, workouts, body weight, settings), and `secret` (the session-cookie key).
**Back up `./data` and you've backed up everything.** Passkey private keys never touch the
server β they stay in your phone's secure hardware / your password manager.
## Configuration
All via `.env` (see `.env.example`):
| Variable | What it is | Default |
|---------------|------------------------------------------------------|-------------------------|
| `RP_ID` | Hostname passkeys are bound to | `localhost` |
| `ORIGIN` | Full URL the app is served from | `http://localhost:8080` |
| `WEB_PORT` | Host port for the web UI | `8080` |
| `RP_NAME` | Name shown in the passkey prompt | `openGym` |
| `ADMIN_UIDS` | User ids that get the admin dashboard (comma-separated) | *(none)* |
| `INVITE_ONLY` | Require an invite code to create a profile | *(off)* |
Push notification keys are generated on first run and saved to `./data/vapid.json` β nothing to set.
## Roadmap
Rough, community-driven β ideas and PRs welcome:
- [x] Standalone mobile app β Android APK to sideload ([download](https://opengym.duarte-santos.ch)); on iOS as a self-hosted PWA (no store listings planned)
- [x] Automatic progression programs (linear, Greyskull LP, double progression) with stalls and deloads
- [x] Estimated 1RM per exercise
- [ ] Percentage / training-max programming (5/3/1-style) on top of the progression engine
- [ ] More starter plans (upper/lower, full-body, 5Γ5)
- [x] Importers from FitNotes / Strong / Hevy (including the RPE they record), and body weight from Apple Health
- [x] Effort per set β RIR or RPE, whichever scale you think in
- [ ] Body measurements (waist, armsβ¦) alongside weight
- [ ] Per-exercise notes & plate calculator
- [ ] Exercise instructions in German & Portuguese (UI is translated; upstream dataset doesn't ship these yet)
## Tech
React 19 + Vite (React Router, Zustand) Β· Node (no framework) Β· nginx Β· Docker Compose Β·
WebAuthn Β· exercise data from [hasaneyldrm/exercises-dataset](https://github.com/hasaneyldrm/exercises-dataset).
No database server, no cloud dependencies β the frontend builds inside Docker, so self-hosting
stays a one-command `docker compose up`.
The training logic β progression rules, 1RM estimation, how a logged session is read back β
lives in pure functions under `frontend/src/lib/` with tests next to them: `npm test` in
`frontend/`. Vitest is a dev dependency; the app itself ships no runtime dependencies beyond
React, the router and Zustand.
## Community
- **[Q&A](https://github.com/DuarteSantos8/openGym/discussions/categories/q-a)** β self-hosting
help, passkey/login trouble, "how do Iβ¦". Most login problems turn out to be an `RP_ID`/`ORIGIN`
mismatch.
- **[Ideas](https://github.com/DuarteSantos8/openGym/discussions/categories/ideas)** β features
worth talking through before anyone writes code.
- **[Show and tell](https://github.com/DuarteSantos8/openGym/discussions/categories/show-and-tell)**
β your setup, your plan templates, whatever you built on top.
- **[Issues](https://github.com/DuarteSantos8/openGym/issues)** β bugs, and work that's already
been agreed on.
## Contributing
Issues and PRs welcome β see [CONTRIBUTING.md](CONTRIBUTING.md). Good first issues: more starter
plans, exercise-data languages, import from other trackers. **A β helps more people find it.**
openGym is free and stays free: AGPL, no subscription, no paid tier, nothing held back for
sponsors. If it replaced a paid tracker for you and you want to chip in, the Sponsor button at the
top of the page is there β a star, a bug report or a PR is worth just as much.
## License
[GNU AGPL v3.0](LICENSE) β free and open source. You can self-host, use, modify and share it;
if you run a modified version as a network service, you must offer that version's source under
the same license. Nobody can turn openGym into a closed, proprietary product.
Exercise images/GIFs are fetched from the upstream dataset and keep their own terms β see [NOTICE.md](NOTICE.md).