Sign inSign up

andreuinyu/periodt

By andreuinyu

•Updated 3 months ago

selfhostable, dead simple, period tracker as a progressive web app | github.com/andreuinyu/periodt

Image
Web servers
0

5.2K

andreuinyu/periodt repository overview

⁠ Periodt — Period Tracker

Smoke Test CodeQL & Eslint Trivy Vuln Scan Docker Pulls Docker Image Size

A privacy-first, self-hosted, dead simple period tracking Progressive Web App. Data stays in your server of choice and you can use it on the web or on your phone as an app.

View screenshots

⁠PWA Features

Periodt is built as a Progressive Web App⁠, which is basically a website that can be "installed" on a device as a standalone application. This allows the users to have a nice mobile experience, without having to develop Android and iOS standalone applications and the hassle of publishing them on app stores.

  • Offline support — service worker caches the app shell; API calls fall back gracefully when offline
  • Home screen install — an install banner appears automatically in supported browsers (Chrome, Edge, Safari on iOS via "Add to Home Screen")
  • Push notifications — Opt in in the Settings page; the backend stores subscriptions in SQLite

⚠️IMPORTANT⚠️: this will only work if you access your Periodt service via HTTPS⁠.


⁠🐋 Setup with Docker Compose

Create a (or add to your existing) docker-compose.yml file in a folder of your choosing:

services:
  periodt:
    container_name: periodt
    image: ghcr.io/andreuinyu/periodt:latest # or, andreuinyu/periodt:latest
    restart: unless-stopped
    ports:
      - "3111:8000"
    volumes:
      - ./periodt_data:/data
    environment:
      - TZ=UTC
      - NOTIFY_DAYS_BEFORE=3
      - NOTIFY_HOUR=9

and bring it up with

docker compose up -d
⁠Configuration

Edit docker-compose.yml to change the port:

  ports:
    - "2333:8000"   # expose on port 2333

or modify the environment variables to the values that suit you the best (if not provided at all, the values will default to these values shown above):

  environment:
    - TZ=Europe/Dublin
    - NOTIFY_DAYS_BEFORE=5
    - NOTIFY_HOUR=7
  • TZ should be something like America/New_York or Europe/Dublin. Find out which one suits you best here⁠.
  • NOTIFY_DAYS_BEFORE configures how many days before the period is supposed to arrive (based off of the historic average) should notifications be sent out to subscribed users.
  • NOTIFY_HOUR configures at what time will the server check if notifications are to be sent out.
⁠Data Persistence

If instead of useing a real path of your choosing /path/to/your/periodt_data:/data to map the database file out of Docker, you are using a docker volume in the docker-compose.yml⁠ (like periodt_data:/data), you can still back it up with:

docker run --rm -v periodt_data:/data -v $(pwd):/backup alpine \
  cp /data/tracker.db /backup/tracker_backup.db

⁠Installing on mobile

  • Android (Chrome): tap the install banner or browser menu → "Add to Home Screen"
  • iOS (Safari): Share → "Add to Home Screen"

⚠️IMPORTANT⚠️: this will only work if you access your Periodt service via HTTPS⁠.

⁠HTTPS

There are many ways to route your self-hosted services through HTTPS. Amongst them:


⁠Development

Any help is welcome, but especially:

  • Translations: please, copy one of the existing .json in frontend/static/translations⁠ and translate all its entries to a missing language. Then, also add the necessary option in index.html⁠ (where value is the name of each .json):
    <select id="lang-select" class="settings-select">
      <option value="en">English</option>
      <option value="cat">Català</option>
      <option value="es">Español</option>
      ...
    </select>
    
    as well as in script.js⁠, add an entry to the locale map for that language, so that dates get translated right:
    const localeMap = { en: 'en-US', cat: 'ca-ES', es: 'es-ES', ... };
    
  • Design: Icons, styles, hell, even the name of this thing.
  • Annoyingly obvious features a simple period tracker should have that this one doesn't.
⁠Stack
LayerTech
BackendPython 3.14 + FastAPI
DatabaseSQLite
FrontendVanilla JS PWA
ContainerDocker + Docker Compose

Set yourself up with:

# 1. Clone / download this folder
cd periodt

# 2. Build and start
docker compose up --build

# 3. Open your browser
open http://localhost:3111
⁠API Endpoints
MethodPathDescription
GET/api/cyclesList all cycles
POST/api/cyclesStart a new cycle
PATCH/api/cycles/{id}Update cycle (e.g. set end date)
DELETE/api/cycles/{id}Delete a cycle
GET/api/symptomsList symptom logs
POST/api/symptomsLog symptoms
DELETE/api/symptoms/{id}Delete a symptom log
GET/api/predictionsGet next period prediction
GET/api/push/vapid-public-keyGet key for push notifications
POST/api/push/subscribeRegister push subscription
POST/api/push/unsubscribeDelete push subscription
GET/api/versiondev for local or vX.Y.Z for release
GET/healthfor Docker integration

Interactive API docs: http://localhost:3111/docs⁠


⁠Project Structure

period-tracker/
├── Dockerfile
├── docker-compose.yml # for local developing
├── README.md
├── backend/
│   ├── main.py          # FastAPI app
│   ├── notifications.py # Notification handling
│   ├── log_config.json
│   └── requirements.txt
└── frontend/
    ├── index.html       # PWA shell
    ├── sw.js        # Service worker
    └── static/
        ├── manifest.json
        ├── scripts.js
        ├── styles.css
        ├── translations/LANGUAGE.json
        └── icons/
            ├── icon-192.png
            └── icon-512.png

Tag summary

Content type

Image

Digest

sha256:0ae9f6ca9…

Size

53.8 MB

Last updated

3 months ago

docker pull andreuinyu/periodt