openGym
Self-hosted gym and body-weight tracker with workout planning and muscle recovery tracking
Alternative to: strong, hevy, fitnotes


v1.3.2
2026-09-11Seventeen community merge requests from twelve contributors, each read, rebased onto a staging instance and put through a tester round before landing, plus the fixes that round turned up. The web bundle and the images change; the API only gains the Coach schema tightening. The Android APK ships with a real version code again (16 — the last two releases had reused 15).
Active workout
- 📋 List view. Settings → During a workout → Workout view: Cards (as before) or List, the whole session stacked and scrollable, the current exercise outlined, “Set current” to move the marker, supersets grouped with their own Unpair. (dpatrongomez, !96 — issues #28, #44, #50)
- ⚖️ The working weight is captured, not asked. Finishing an exercise no longer opens the “confirm your working weight” sheet: the heaviest completed work set becomes the working weight, for both members of a superset, and the current marker stays where you are until you press Next. Working weights are stored when the workout is finished, so a typo corrected before that, or a discarded session, never becomes your “Best”. (mflova, !92 — issue #18)
- 🔢 One tap, one step — in supersets too. The +/− on weight, reps and RIR moved two steps per tap on the second member of a superset. (ErrorUsernameAlreadyTaken, !97 — issue #41)
- ➕ The weight stepper uses the exercise’s own increment (1.25 kg plates, 5 lb, whatever you set), with progression’s rounding, on set rows and drop-set rows; timed sets keep their seconds. A value off the increment grid steps by exactly one increment rather than snapping. (mflova, !84)
- 🎯 Routines with progression off keep their targets. The same exercise as 2×15 in one routine and 4×8 in another no longer opens the second with the first’s reps — also when adding or swapping an exercise mid-session and when saving the progression sheet. (mflova, !71)
- 📉 No deload spiral. A weight change starts a new stall streak, so one bad day at the lighter weight after a deload cannot trigger the next one. (arhx91, !93)
- 🛡️ The progression sheet cannot save into the wrong exercise after the list shifted under it or the workout was replaced. (Space-Hermes, !77)
- ⏱️ Timer flash blinks the theme instead of a black/white overlay and settles back on your theme. A timer that ended while the app was in the background no longer replays the beep and flash when you come back; a quiet toast still says the rest is over. (mzspicoli, !89)
Planning and library
- ✨ Four starter plans. Home, Plan and Settings → Load starter plan open a chooser: Push/Pull/Legs, Upper/Lower, Full Body, 5×5. Free weekdays load at once, occupied ones ask first; existing routines are never touched. (lordlukem, !94)
- 💪 Muscle explorer. Library → “By muscle” (and “By muscle” inside Add exercise): tap a muscle on the body map or its chip, filter by search, body part and equipment, open details or plan it. Follows your equipment profile like the library does. (prasad_gade05, !87)
- 📄 Copy routine at the bottom of the routine editor. (ErrorUsernameAlreadyTaken, !85)
- ➕ The ”+” in the exercise picker adds immediately with the default config (freestyle: last session’s) and says so in a toast; tapping the row still opens the config sheet. (surohsusej, !72)
- 📐 Stats → Exercise progress: long exercise names use the full width. (mflova, !80)
MCP and Coach
- 🔍
preview_session— a ninth MCP tool: what a routine will actually open with after the progression policy, the confirmed weight and history have had their say, with the source of every number and a list of exercises where plan and screen disagree. Built on the app’s own session builder, so progression-off and deload routines preview exactly what the screen shows. (koyosan, !90) - ⏲️
get_routinereports each exercise’s own rest asrest_sec. (TheophileDiot, !81 — issue #36) - 🧠 The Coach’s create schema requires a routine id and a week and caps the arrays, so a small local model cannot answer with a plan the validator can only reject. (subdee, !99)
- 🧪 BodyMap tests import the geometry before the first render — no more one-off failures on a slow runner. (TheophileDiot, !82 — issue #35)
Self-hosting and infrastructure
- 🔁 nginx re-resolves the api container per request. Recreating only
api(new IP) no longer leaveswebanswering 502 until it is restarted too. (issue #16) - 🔒 The published
apiimage drops npm and both images take alpine’s package fixes at build time; the image scan reports nothing. - 🏗️ CI: JUnit and coverage in every merge request, a bundle-size diff against main, a compose smoke test of the pushed images, a Trivy scan with SBOMs attached to the release, a tag preflight (versions and changelog checked before anything builds), and pipelines for contributors’ fork MRs started automatically for returning contributors.
- 🤖 Renovate runs again: its configuration had two unknown keys and had stopped opening MRs. (issue #37)
Install or upgrade
docker compose pull && docker compose up -d
Images registry.gitlab.com/duartesantos8/opengym/{api,web} tagged 1.3.2, 1.3, latest (linux/amd64 + linux/arm64). Android: openGym-1.3.2.apk, checksum next to it. Built from cf46301.
Published on GitHub on 2026-09-11, mirroring the GitLab release of the same tag, where the artefacts above were built (!NN numbers are GitLab merge requests). The same images are on GHCR, built from this release by GitHub Actions: ghcr.io/duartesantos8/opengym-api:1.3.2 and ghcr.io/duartesantos8/opengym-web:1.3.2.