A mobile-first workout tracker for a small group of friends. Sign in with your name, log what you trained (Arms, Legs, Back, Chest, Shoulders, Core, or Cardio), track sets/reps/weight (or duration/distance), see progress charts, and battle it out on the weekly leaderboard. Includes a single admin account that manages members, resets passwords, and leaves star ratings + feedback.
Dark, energetic theme. Designed to be used on your phone at the gym.
- Frontend: React + Vite, mobile-first, Recharts for progress graphs.
- Backend: Node + Express.
- Database: SQLite via Node's built-in
node:sqlite— no native compilation, no extra database server. - Deploy: one Node process serves both the API and the built React app.
Node 22.5+ is required (for the built-in SQLite module). Check with
node -v.
Gaym/
├── server/ # Express API + SQLite + serves the built frontend
│ ├── server.js
│ ├── db.js
│ ├── seed.js
│ ├── passwords.js
│ └── data/ # gym.db lives here (created automatically, not in git)
├── client/ # React app
│ ├── src/
│ └── dist/ # production build (generated by `npm run build`)
├── start.sh # build + run in one command
└── README.md
./start.shBuilds the frontend and starts the server on http://localhost:3001. On first run it creates the admin account and prints its credentials to the console (default Coach / gymboss — change the password right away in the Admin tab).
Two terminals:
# Terminal 1 — backend on :3001
cd server
npm install
npm start
# Terminal 2 — frontend dev server on :5173 (proxies /api to :3001)
cd client
npm install
npm run devOpen http://localhost:5173.
- Members and accounts are the same thing: one person, one name, one password.
- There is exactly one admin. Only the admin can add, rename, or remove members and reset passwords.
- When the admin adds a member without a starting password, the first password that member types at sign-in becomes their password ("claim on first login").
- Forgot your password? The admin resets it from the Admin tab — either to a temporary password or to blank so you can re-claim it.
- Passwords are hashed server-side (scrypt). This is a friendly app for a small crew, not bank-grade security — don't reuse important passwords.
| Variable | Default | Purpose |
|---|---|---|
PORT |
3001 |
Port the server listens on. |
ADMIN_NAME |
Coach |
Name of the admin account (used on first run only). |
ADMIN_PASSWORD |
gymboss |
Initial admin password (first run only). Change it! |
DATA_DIR |
server/data |
Where gym.db is stored. Set to a persistent path. |
cd client
npm install
npm run build # outputs client/distThe server automatically serves client/dist when it exists, so in production you only run the server (it hosts both the app and the API on one port).
cd server
npm install
npm start # serves app + API on http://localhost:3001-
Install Node 22 LTS (Node 22.5+ needed):
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash - sudo apt-get install -y nodejs node -v # must be >= 22.5
-
Upload the project (e.g. via
scporgit clone) to/opt/gaym. -
Build the frontend:
cd /opt/gaym/client npm install npm run build -
Install backend deps:
cd /opt/gaym/server npm install -
Run it with pm2 (keeps it alive + restarts on reboot):
sudo npm install -g pm2 cd /opt/gaym/server ADMIN_PASSWORD="your-secret" DATA_DIR=/opt/gaym/server/data \ pm2 start "npm start" --name gaym pm2 save pm2 startup # follow the printed command to enable on boot
The app is now on
http://<server-ip>:3001. -
(Recommended) Put nginx in front so you can use port 80/443 and a domain:
server { listen 80; server_name your-domain.com; # or the server IP location / { proxy_pass http://127.0.0.1:3001; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }
sudo cp gaym.conf /etc/nginx/conf.d/gaym.conf # paste the block above sudo nginx -t && sudo systemctl reload nginx
-
Open the firewall (Security Group): allow inbound TCP 80 (and 443 if you add HTTPS). For HTTPS, use certbot with nginx.
-
Open the site on your phone and start logging. 💪
Sign in with the admin account and open the Admin tab (bottom bar). You can:
- Add / rename / remove members.
- Reset any member's password (set a temporary one, or blank it so they re-set it themselves).
- See every member's full workout history.
- Give each member a 1–5 star rating + written feedback. Ratings default to 5 stars; stars show on the name picker and leaderboard.
All data is in a single SQLite file at DATA_DIR/gym.db. To back up, just copy that file:
cp server/data/gym.db ~/gaym-backup-$(date +%F).db- Default exercises are seeded automatically on first run; members can add custom exercises in any category.
- The leaderboard's week resets every Monday. Volume = sets × reps × weight, summed across the week; cardio is ranked by total minutes.