Skip to content

bndct-devops/lifty

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

60 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

lifty

A self-hosted, privacy-first workout tracker inspired by Strong. No accounts, no cloud, no subscriptions — just your data on your own server.

FastAPI · React PWA · SQLite · Docker Compose


Screenshots

Home tab Active workout History Workout detail Progress Settings


Features

Workouts

  • Start a workout, add exercises on the fly, log sets with weight + reps
  • Previous session's sets shown inline as reference
  • Reorder exercises during a workout
  • Rename workouts, add notes
  • Mark rest days from the home screen

Rest Timer

  • Configurable duration (60 / 90 / 120 / 180s) saved per profile
  • Web Audio ding + vibration on finish
  • Background-accurate — stays correct after screen lock or tab switch
  • Push notification fires even when the screen is off (Android + iOS PWA)

Progress & History

  • Full workout log with monthly calendar view
  • Per-workout detail sheet — stats, muscle group breakdown, sets with estimated 1RM
  • PRs per exercise (Epley 1RM), grouped by body part
  • Weekly / daily volume bar chart (sets or tonnage)
  • Muscle group donut chart
  • 26-week activity heatmap

Profiles & Settings

  • Multiple profiles on a single instance
  • Per-profile: unit (kg / lbs), theme, avatar colour, week start day, rest duration, ding toggle
  • Themes: Dark, Light, AMOLED, Tokyo Night, Dracula, Nord, Gruvbox, Rosé Pine, Catppuccin Mocha / Macchiato / Frappé / Latte

Import & Export

  • Strong CSV import — bring in your full workout history
  • CSV export per profile

PWA

  • Installable on iOS and Android
  • Offline support via service worker
  • Flamingo barbell icon, themed status bar

PWA & Service Worker

The service worker provides two things:

Feature How it works
Offline support Static assets cached on first load; API falls back to cached responses when offline
Background notifications Rest timer end time posted to SW on start; SW fires showNotification at the right time regardless of whether the page is suspended

Platform support:

Platform Offline Background notification
Android
iOS 16.4+ (PWA) ✅ — add to home screen first
iOS < 16.4 / desktop ❌ — in-page ding still works when visible

Stack

Layer Tech
Backend FastAPI + SQLModel + SQLite, Python 3.11
Frontend React 18 + Vite + plain CSS
Serving nginx (frontend), uvicorn (backend)
Containers Docker + Docker Compose
CI GitHub Actions → ghcr.io (amd64 + arm64)

Running locally

./scripts/dev_up.sh

Builds both containers and seeds the exercise library.

Service URL
Frontend http://localhost:5173
Backend API http://localhost:8000
API docs http://localhost:8000/docs

Reset the database:

rm data/lifty.db && docker compose restart backend

Self-hosting

Images are built and pushed to ghcr.io on every push to main (amd64 + arm64).

Create a docker-compose.yml on your host:

services:
  backend:
    image: ghcr.io/bndct-devops/lifty-backend:latest
    restart: unless-stopped
    expose:
      - "8000"
    volumes:
      - /mnt/user/appdata/lifty:/data   # adjust path as needed
    environment:
      - LIFTY_DB=/data/lifty.db
      # - LIFTY_PASSWORD=your-password-here
  frontend:
    image: ghcr.io/bndct-devops/lifty-frontend:latest
    restart: unless-stopped
    ports:
      - "3420:80"
    depends_on:
      - backend

Then:

docker compose pull
docker compose up -d

Update the volume path to wherever you want the SQLite database stored on your host.


Security

By default the API has no authentication — fine for local/VPN use, but set a password before exposing to the internet.

Enable instance auth

Uncomment LIFTY_PASSWORD in your docker-compose.yml:

environment:
  - LIFTY_DB=/data/lifty.db
  - LIFTY_PASSWORD=your-strong-password

Restart the backend. The app will show a password screen on load. Once unlocked, a 30-day JWT is stored in the browser — you won't be prompted again until it expires.

  • Change/reset password: Settings → Instance Auth → Change Password (or set LIFTY_PASSWORD env var again and restart to override)
  • Sign out: Settings → Instance Auth → Sign Out

Project structure

backend/
  main.py              # FastAPI app, all endpoints
  models.py            # SQLModel table definitions
  schemas.py           # Pydantic request/response types
  db.py                # engine + table creation
  seed_exercises.py    # built-in exercise library (runs on startup)
frontend/
  public/
    sw.js              # service worker — offline cache + background notifications
    manifest.json      # PWA manifest
    favicon.svg        # flamingo barbell icon
  src/
    App.jsx            # entire frontend
    api.js             # fetch wrappers for all backend endpoints
    styles.css         # CSS custom properties + layout
  nginx.conf           # proxies /api/* to backend
scripts/
  dev_up.sh            # one-command local dev start
docker-compose.yml         # local dev (builds from source)
.github/workflows/
  build-push.yml       # CI: build multi-arch images, push to ghcr.io

Acknowledgements

  • Inspired by Strong — the best commercial workout tracker, which lifty aims to self-host-replace
  • Catppuccin theme palette by Catppuccin

About

Self-hosted personal workout tracker. PWA, multi-profile, themes, rest timer, analytics.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors