Self-hosted gym & body-weight tracker — plan routines, log workouts (supersets, warm-ups, cardio), see which muscles are trained, fatigued or detrained, import from FitNotes/Strong/Hevy, passkey login. Your data, your server.
A self-hosted gym and body-weight tracker you actually own.
Plan your week, run guided workouts, log every set and your body weight,<br>
on your phone, synced across your devices, behind your own passkey login.
<a href="https://trendshift.io/repositories/88268?utm_source=repository-badge&utm_medium=badge&utm_campaign=badge-repository-88268" target="_blank" rel="noopener noreferrer"><img src="https://trendshift.io/api/badge/repositories/88268" alt="DuarteSantos8%2FopenGym | Trendshift" width="250" height="55"/></a>
<a href="https://trendshift.io/repositories/88268?utm_source=trendshift-badge&utm_medium=badge&utm_campaign=badge-trendshift-88268" target="_blank" rel="noopener noreferrer"><img src="https://trendshift.io/api/badge/trendshift/repositories/88268/weekly?language=JavaScript" alt="DuarteSantos8%2FopenGym | Trendshift" width="250" height="55"/></a>
Website ·
Live demo ·
Android APK ·
Self-hosting guide ·
Roadmap ·
Changelog
<a href="https://buymeacoffee.com/duartesantos" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" height="45" width="163"></a>
<a href="https://ko-fi.com/opengym" target="_blank"><img src="https://ko-fi.com/img/githubbutton_sm.svg" alt="Support openGym on Ko-fi" height="45"></a>
Most workout apps keep your data on their servers, push you towards a subscription, or vanish
when the company does. openGym runs on your own box, keeps your data in a folder you control, and
is yours to fork. It still behaves like a modern app: installable on the home screen, passkey
sign-in, works offline, syncs between your phone and your laptop.
No account on someone else's server, no subscription, no ads, no telemetry. Onedocker compose up and it's running.
The in-browser demo is the real app with example data,
if you want to try it before installing anything.
Planning
Training
Progress
Accounts and data
Optional extras, off by default
The full list of what changed release by release is in the changelog.
You need Docker with Compose.
git clone https://github.com/DuarteSantos8/openGym
cd openGym
cp .env.example .env
docker compose pull # prebuilt images, amd64 + arm64 (skip this to build from source)
docker compose up -dOpen http://localhost:8080, tap Create profile, and you're in. The exercise animations are
part of the image, so there's nothing else to download.
To reach it from your phone with passkeys you need HTTPS on a domain; that's a two-line change in.env. The self-hosting guide walks through Cloudflare Tunnel, Caddy,
Traefik and nginx, and there are separate guides for
HTTPS on a LAN and Kubernetes.
<details> <summary><b>Configuration reference</b> (all through <code>.env</code>)</summary>[!NOTE]
Images are published from the same tag toghcr.io/duartesantos8/opengym-{api,web}
(whatdocker-compose.ymlpulls) andregistry.gitlab.com/duartesantos8/opengym/{api,web}. Swap
theimage:lines if you prefer GitLab's registry, or rundocker compose up -d --buildto build
locally. Either way you don't need Node on the host.
| Variable | What it does | 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 |
NGINX_PORT | Port the web container listens on inside the container | 80 |
BACKEND | Name of the API service that /api is proxied to | api |
PORT | Port the API listens on; the web container proxies to the same value | 3000 |
RP_NAME | Name shown in the passkey prompt | openGym |
SESSION_DAYS | How long a sign-in lasts, in days | 90 |
ADMIN_UIDS | User ids that get the admin dashboard, comma-separated | (none) |
FIRST_USER_ADMIN | 1: the first profile created on an empty instance becomes its admin | (off) |
INVITE_ONLY | Require an invite code to create a profile | (off) |
ALLOW_GUEST | Offer "Continue without account"; 0 requires a profile | (on) |
PASSWORD_LOGIN | Offer name-and-password sign-in next to passkeys | (off) |
TRUST_PROXY | Let the sign-in throttle read the client address from proxy headers | 1 in docker-compose.yml |
AUDIT_LOG | Record sign-ins and admin actions; 0 records nothing | (on) |
AUDIT_MAX | Events kept in the activity log; 0 for no limit | 5000 |
AUDIT_DAYS | Days kept in the activity log; 0 keeps until AUDIT_MAX | 90 |
AUDIT_IP | Record the caller's address: off, net (network only) or full | off |
ALLOWED_PRIVATE_IPS | Private addresses push endpoints may resolve to (FakeDNS, split-horizon DNS): comma-separated IPs, a-b ranges or CIDR blocks | (none) |
VAPID_SUBJECT | Contact URL sent with push notifications | your ORIGIN |
API_TARGET | API image to build: default, or coach with the Claude Agent SDK and Codex CLI | default |
COACH_DISABLED | 1 forces the AI coach off instance-wide | (unset) |
Push-notification keys are generated on first run into ./data/vapid.json. DATA_DIR is pinned to/data inside the container and mapped to ./data on the host; change the volume, not the
variable. The self-hosting guide covers every option in detail.
The same codebase builds a standalone app with Capacitor: no account, no server, everything stays
on the phone, with native reminders and a rest countdown in the notification shade.
.sha256, andDetails and build instructions: docs/MOBILE.md.
frontend/ is React 19 and Vite (React Router, Zustand), built to static files inside Docker.api/ is plain node:http with two dependencies: @simplewebauthn/server for passkeys andweb-push for notifications. Everything is stored as JSON under ./data.web/ builds the frontend and serves it with nginx, proxying /api so the whole app sits onThe training logic (progression rules, 1RM, how a logged session is read back) lives in pure
functions under frontend/src/lib/ with tests beside them. The HTTP API is documented as an
OpenAPI spec in api/openapi.yaml, browsable at
opengym.ch/api.html.
Each profile's data is one document with a server revision. A device sends the revision it last
saw along with its changes; if another device wrote in between, the server refuses and returns the
current document so the device can merge and retry. Every change carries its own stamp, down to a
single setting or routine field, so the merge keeps the newest edit of each one and a deletion stays
deleted.
Nothing that hasn't reached the server is discarded on disconnect or sign-out, and the app shows a
banner whenever it's working offline.
Everything lives in ./data on your host:
| File | Contents |
|---|---|
db.json | Profiles and public passkey data |
db.json.bak | A copy of the last good db.json, used if the main file can't be read |
state-<user>.json | Each user's plan, workouts, body weight and settings |
audit.log | Admin activity log (no IP addresses unless you turn that on) |
secret | Session-cookie signing key |
Back up ./data and you've backed up everything. Passkey private keys never reach the server; they
stay in your phone's secure hardware or your password manager.
The documentation index sorts every guide by who it's for. The most used ones:
| I want to | Read |
|---|---|
| Get a quick answer | FAQ |
| Set up my own instance | Self-hosting |
| Use the Android or iPhone app | Phone app |
| Bring my history from another app | Importing data |
| Turn on the AI coach | AI coach |
| Contribute code | Contributing |
| Report a security problem | Security |
A release roughly every two weeks, each small and themed. The full plan is in
ROADMAP.md, and the issues sit in the
GitHub milestones. The next two releases
finish the community wishes, then comes a new exercise database and a themed release every
two weeks after it.
| Release | When | Theme |
|---|---|---|
| v1.3.10 | released Oct 2026 | New design, rotation, swipe actions, safer sync |
| v1.3.11 | next | Fixes, about thirty community pull requests, Health Connect, measurements |
| v1.3.12 | Nov 2026 | Community features: focus view, widget, timer rework |
| v1.4.0 | Nov 2026 | A new exercise database |
| v1.4.1 to v1.4.4 | Dec 2026 and Jan 2027 | Programmes, the progression engine, cardio |
| v1.4.5 to v1.4.10 | Jan to Apr 2027 | Search, accounts, the iOS app, Android and health, looks |
| later | Database storage |
RP_ID/ORIGIN mismatch; theGitHub is the home of the project. gitlab.com/DuarteSantos8/opengym
is a mirror, updated by a GitHub Actions workflow on every push to main and every release tag. It
exists because its CI builds the release artifacts: the signed APK, the multi-arch images and the
SBOMs. Nothing is merged there by hand. In the changelog, !NN refers to a GitLab merge request
from the weeks in August and September 2026 when the project lived there.
People have asked about this, so plainly: openGym is developed with
Claude Code, Anthropic's coding agent. A large share of the
code, tests and documentation is drafted in Claude Code sessions, and the repository carries aCLAUDE.md with the project context those sessions start from.
What that does and doesn't mean:
Community pull requests are written by their authors, with whatever tools they like, and reviewed
the same way.
openGym is free and stays free: AGPL, no paid tier, nothing held back for sponsors. If it replaced a
paid tracker for you and you'd like to chip in, there's a coffee button below. A star, a bug report
or a pull request helps just as much.
openGym's own code is licensed under the GNU AGPL v3.0. You can self-host, use,
modify and share it; if you run a modified version as a network service, you have to offer that
version's source under the same license.
[!IMPORTANT]
The exercise media is not covered by that license. The stills and animations are
© Aliaksandr Makatserchyk, Gym visual, licensed for use in openGym only:
180 px in this repository and on self-hosted servers, larger only inside the app packages. A
non-commercial fork may keep them unmodified with the notice; anything else needs your own licence
from gymvisual.com. The exercise text incatalogue/is openGym's and open to contributions.
Contributing: pull requests are signed off once with the Contributor License Agreement:
you keep the copyright in your work, openGym may use and relicense it (so the app can stay in the
App Store and Google Play long term), and every version released under the AGPL stays AGPL.
Full third-party notices, including the body-diagram geometry, are in NOTICE.md.
DuarteSantos8/openGym
July 18, 2026
October 12, 2026
JavaScript