Your cycle, your data, your privacy. No ads. No tracking. No BS.
Bloom is a free, open-source, self-hosted period tracking app. One binary. No cloud. No subscriptions. Just a simple, beautiful tool that respects your privacy.
Built for people who want to understand their bodies — and partners who want to be supportive.
- 📅 Period logging with start/end dates and cycle history
- 📝 30+ symptoms across physical, emotional, flow categories — plus custom symptoms
- 🔢 Multi-symptom logging — select several symptoms at once, all saved with one click
- 🌡️ Basal body temperature (BBT) daily tracking
- 💧 Cervical mucus tracking (symptothermal method support)
- 😴 Daily wellness — sleep quality, stress level, energy level (1-5 scales)
- 📔 Daily journal with mood emoji picker
- 🔮 Cycle predictions — next period, ovulation, fertile window
- 📊 Trends & analytics — cycle length charts, symptom patterns, month-by-month grid
- 🔬 Phase-symptom correlation heat map
- 🌡️ BBT temperature chart with mucus overlay
⚠️ Smart alerts — irregular cycles, late periods, severity spikes- 🧘 Wellness tips — phase-specific exercise, nutrition, and nutrient guidance
- 💛 Encouragement — affirmations and treat ideas per cycle phase
- 💑 Partner dashboard — invite your partner with a code, they get their own view
- 📧 Weekly partner emails — phase updates, action items, treat suggestions
- 🚨 Instant symptom alerts — partner is notified immediately when severity is high (4-5)
- 🔄 Phase change alerts — partner is emailed when your cycle phase changes
- 🔒 100% local — SQLite database, nothing leaves your server
- 🛡️ Database encryption — optional AES-256-GCM at-rest encryption with your own key
- 🔐 Encrypted backups — AES-256-GCM with password protection
- 📦 Data export — download everything as CSV or JSON
- 🗑️ Account deletion — full data wipe with password confirmation
- 📖 Transparent calculations — see exactly how predictions are made
- 🌿 Fertility toggle — hide fertility data if you don't want it
- 🏳️🌈 Inclusive — pronoun settings (she/her, he/him, they/them)
- 📥 Bulk import — paste period history in flexible date formats
- 📄 Import template — downloadable CSV template
- 📤 Backup restore — restore encrypted backups to any Bloom instance
Don't want to install anything? We host a free instance you can use right now:
⚠️ Privacy note: This is a shared hosted instance. While we encrypt the database at rest and don't sell or share data, your information lives on our server — not yours. For maximum privacy, self-host Bloom. It takes about 5 minutes.
git clone https://github.com/waldmannly/bloom-tracker.git
cd bloom
cp .env.example .env
docker compose up -dOpen http://localhost:8080 and create your account.
git clone https://github.com/waldmannly/bloom-tracker.git
cd bloom
go build -o bloom .
./bloomGrab the latest release from the Releases page. Run it. Done.
All settings are optional. Bloom works with zero configuration.
| Variable | Default | Description |
|---|---|---|
PORT |
8080 |
Server port |
DB_PATH |
period_tracker.db |
SQLite database file path |
ENCRYPTION_KEY |
(disabled) | Passphrase for database-at-rest encryption (8+ chars). See Database Encryption |
EMAIL_API_URL |
(disabled) | URL for HTTP email service (e.g. http://127.0.0.1:3100/send) |
EMAIL_API_KEY |
(disabled) | API key for the email service |
SMTP_HOST |
smtp.gmail.com |
SMTP server (fallback if no API configured) |
SMTP_PORT |
587 |
SMTP port |
SMTP_EMAIL |
(disabled) | Gmail address for sending notifications |
SMTP_PASS |
(disabled) | Gmail App Password (create one here) |
Copy .env.example to .env and fill in what you need.
Bloom is intentionally simple:
bloom (single Go binary)
├── SQLite database (local file)
├── Embedded HTML templates
├── Embedded CSS + JS
└── No external dependencies at runtime
- Language: Go (1.21+)
- Database: SQLite (via modernc.org/sqlite — pure Go, no CGO)
- Auth: bcrypt passwords, secure session cookies
- Templates: Go html/template with embedded filesystem
- Email: SMTP or HTTP API (optional, for partner notifications)
- Encryption: AES-256-GCM with PBKDF2 key derivation (for backups and database-at-rest encryption)
No React. No Node. No Docker required. No cloud services. Just one binary that embeds everything.
- Self-hosted = full privacy. Data on your hardware, under your control. Nobody else can access it.
- Hosted instances are convenient but your data lives on someone else's server. You're trusting the operator.
- No analytics, telemetry, or tracking scripts
- No third-party API calls or cloud sync
- No data selling — ever
- Optional database-at-rest encryption with your own key
- Encrypted backups use AES-256-GCM (your password never leaves your browser)
- Full data export (CSV/JSON) and account deletion at any time
- Bloom is fully open-source — verify what the code does, then self-host for maximum privacy
- See
/privacyin the app for the full policy
Bloom supports encrypting the entire SQLite database file at rest using AES-256-GCM with a key you provide. When enabled:
- Startup: The encrypted
.encfile is decrypted to a working.dbfile - Running: Periodic encrypted snapshots every 5 minutes (crash resilience)
- Shutdown: The database is encrypted and the plaintext
.dbfile is removed
# Set a strong passphrase (8+ characters)
export ENCRYPTION_KEY="your-secret-passphrase-here"
./bloomOr in your .env file:
ENCRYPTION_KEY=your-secret-passphrase-here
- Key derivation: PBKDF2 with SHA-256, 100,000 iterations, random 16-byte salt
- Encryption: AES-256-GCM (authenticated encryption)
- The encrypted file uses a
BLOMDheader format (distinct from backup files) - If the app crashes, the
.encfile is at most 5 minutes old - If the unencrypted
.dbfile exists on startup (crash recovery), it's used directly
- If you lose your encryption key, your data is gone. There is no recovery.
- The key minimum is 8 characters, but use something much stronger.
- The database is only encrypted at rest — while Bloom is running, the working copy is unencrypted in memory/on disk.
Partners get their own login and a dedicated dashboard showing:
- Current cycle phase with supportive tips
- Action items ("bring her a heating pad", "plan a fun date")
- Treat ideas matched to the current phase
- Wellness tips (exercise, nutrition)
- Words of encouragement
Partners cannot edit data — they can only view what's shared.
Bloom uses transparent, documented math — no black-box AI:
- Ovulation: Cycle length − 14 (calendar method)
- Fertile window: Ovulation day −5 through +1
- Advanced: BBT temperature shift + cervical mucus patterns (symptothermal method, 99.6% effective)
Full methodology at /methodology in the app.
Bloom is a single binary with zero runtime dependencies. You can run it on anything — a Raspberry Pi, an old laptop, a NAS, or a cloud VPS.
- A computer or server that stays on (Linux, macOS, or Windows)
- ~20 MB of disk space
- Go 1.21+ (only if building from source)
- (Optional) A domain name, if you want to access it from outside your network
- (Optional) Docker, if you prefer containers
Pick whichever method you prefer:
# Download the latest release (Linux example)
curl -L https://github.com/waldmannly/bloom-tracker/releases/latest/download/bloom-linux-amd64 -o bloom
chmod +x bloomgit clone https://github.com/waldmannly/bloom-tracker.git
cd bloom
cp .env.example .env # edit .env if you want email or encryption
docker compose up -dSkip to Step 4 — Docker handles the rest.
Requires Go 1.21+.
git clone https://github.com/waldmannly/bloom-tracker.git
cd bloom
go build -o bloom .Bloom works with zero configuration. But if you want email, encryption, or a custom port:
cp .env.example .env
nano .env # or open in any text editorKey settings:
PORT=8080 # change if 8080 is taken
DB_PATH=./data/bloom.db # where your data lives
ENCRYPTION_KEY=my-secret-key # encrypt the database at rest (optional)
# Email — pick ONE method:
# Gmail SMTP:
SMTP_EMAIL=you@gmail.com
SMTP_PASS=abcd-efgh-ijkl-mnop # Gmail App Password, NOT your real password
# Or an HTTP email service:
EMAIL_API_URL=http://127.0.0.1:3100/send
EMAIL_API_KEY=your-api-keyGmail App Passwords: Go to https://myaccount.google.com/apppasswords, generate one for "Mail", and paste it as
SMTP_PASS. This only works if you have 2FA enabled on your Google account.
./bloomYou should see:
🌸 Bloom Period Tracker running on http://localhost:8080
Open a browser and go to http://localhost:8080. Create your account. That's it — you're tracking.
By default, Bloom is only reachable from the machine it runs on. Here's how to open it up — from simplest to most robust.
No configuration needed. Just find the IP address of the machine running Bloom:
# Linux/macOS
hostname -I
# Windows
ipconfigThen from any device on the same Wi-Fi/network, open:
http://192.168.1.XXX:8080
Replace 192.168.1.XXX with your machine's local IP.
Tip: This works for phones, tablets, and your partner's laptop — as long as everyone is on the same network.
This is the easiest and most secure way to reach Bloom from anywhere — without exposing it to the public internet.
- Install Tailscale (free for personal use) on the machine running Bloom and on your phone/laptop.
- Both devices join the same Tailscale network automatically.
- Access Bloom via your Tailscale IP:
http://100.x.y.z:8080
Why this is great:
- 🔒 Encrypted tunnel — nobody else can see it
- 🚫 No ports to open on your router
- 📱 Works from anywhere (coffee shop, work, travel)
- ⚡ Takes about 5 minutes to set up
If you want a real URL like https://bloom.yourdomain.com — for example, to share with a partner who lives elsewhere — you'll need:
- A domain name (or subdomain) pointing to your server's public IP
- A reverse proxy (Nginx or Caddy) to handle HTTPS
- Port forwarding on your router (if hosting at home) — or use a cloud VPS
# Install nginx and certbot
sudo apt install nginx certbot python3-certbot-nginxCreate /etc/nginx/sites-available/bloom:
server {
listen 80;
server_name bloom.yourdomain.com;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}Enable it and get a free SSL certificate:
sudo ln -s /etc/nginx/sites-available/bloom /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
sudo certbot --nginx -d bloom.yourdomain.comCertbot will automatically configure HTTPS and set up auto-renewal. You're done.
Caddy handles SSL automatically with zero configuration:
# Install Caddy
sudo apt install caddyEdit /etc/caddy/Caddyfile:
bloom.yourdomain.com {
reverse_proxy 127.0.0.1:8080
}
sudo systemctl reload caddyThat's literally it. Caddy auto-provisions SSL certificates.
If you're hosting at home (not a VPS), you need to forward ports through your router:
- Log into your router (usually
192.168.1.1or192.168.0.1) - Find Port Forwarding (sometimes under NAT, Firewall, or Advanced)
- Forward port 80 and 443 (TCP) to your server's local IP
- Point your domain's DNS A record to your home's public IP
Dynamic IP? Most home ISPs change your IP periodically. Use a free dynamic DNS service like DuckDNS or Cloudflare Tunnels to keep your domain pointing to the right place.
If you don't want to deal with your home network, rent a small VPS:
| Provider | Cheapest Plan | Notes |
|---|---|---|
| Hetzner | ~€3.50/mo | Best value, EU-based (GDPR ✓) |
| DigitalOcean | $4/mo | Simple, great docs |
| Linode | $5/mo | Reliable |
| Oracle Cloud | Free tier | ARM instance, always free |
| Fly.io | Free tier | Good for small apps |
Setup on a fresh VPS:
# Upload Bloom
scp bloom you@your-server:/opt/bloom/bloom
# SSH in
ssh you@your-server
# Create data directory
sudo mkdir -p /opt/bloom/data
sudo chown $USER:$USER /opt/bloom /opt/bloom/data
# Create .env
cat > /opt/bloom/.env << 'EOF'
PORT=8080
DB_PATH=/opt/bloom/data/bloom.db
EOF
# Run it (see "Run as a Service" below to keep it running)
cd /opt/bloom && ./bloomThen set up Nginx or Caddy (see Option 3 above) for HTTPS.
Don't run Bloom in a terminal that closes when you log out. Set it up as a system service so it starts on boot and restarts if it crashes.
Create /etc/systemd/system/bloom.service:
[Unit]
Description=Bloom Period Tracker
After=network.target
[Service]
Type=simple
ExecStart=/opt/bloom/bloom
WorkingDirectory=/opt/bloom
EnvironmentFile=/opt/bloom/.env
Restart=always
RestartSec=5
# Security hardening (optional but recommended)
NoNewPrivileges=true
ProtectSystem=strict
ReadWritePaths=/opt/bloom/data
[Install]
WantedBy=multi-user.targetsudo systemctl daemon-reload
sudo systemctl enable bloom # start on boot
sudo systemctl start bloom # start now
sudo systemctl status bloom # check it's runningCreate ~/Library/LaunchAgents/com.bloom.plist:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key><string>com.bloom.tracker</string>
<key>ProgramArguments</key>
<array><string>/opt/bloom/bloom</string></array>
<key>WorkingDirectory</key><string>/opt/bloom</string>
<key>RunAtLoad</key><true/>
<key>KeepAlive</key><true/>
</dict>
</plist>launchctl load ~/Library/LaunchAgents/com.bloom.plistUse NSSM to run Bloom as a Windows service:
nssm install Bloom C:\bloom\bloom.exe
nssm set Bloom AppDirectory C:\bloom
nssm start BloomOr just run bloom.exe at startup (simpler but less robust).
Go to Settings → Encrypted Backup. This downloads a password-protected .enc file you can store wherever you want. Restore it on any Bloom instance via Settings → Restore Backup.
# Daily backup at 3 AM, keep 30 days
0 3 * * * cp /opt/bloom/data/bloom.db /backups/bloom-$(date +\%Y\%m\%d).db
0 4 * * * find /backups -name "bloom-*.db" -mtime +30 -deleteIf database encryption is enabled (ENCRYPTION_KEY), the .enc file is safe to sync anywhere:
# Sync encrypted database to cloud storage
0 3 * * * cp /opt/bloom/data/bloom.db.enc ~/Dropbox/backups/bloom.encIf you're exposing Bloom to the internet, go through this list:
- HTTPS only — use Nginx/Caddy with SSL (Let's Encrypt is free)
- Strong passwords — Bloom enforces minimum 8 characters, but use more
- Database encryption — set
ENCRYPTION_KEYso the database file is encrypted at rest - Firewall — only open ports 80 and 443 (block 8080 from the public)
- Updates — keep your server OS and Bloom binary up to date
- Backups — automate daily backups (see above)
# UFW firewall example (Ubuntu)
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow ssh
sudo ufw enableBloom is built with love. Contributions welcome!
- Fork the repo
- Create a feature branch (
git checkout -b feature/amazing) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing) - Open a Pull Request
Q: Do I need Docker? No. Bloom is a single binary — just download and run it. Docker is an option, not a requirement.
Q: Does it work on Raspberry Pi?
Yes! Cross-compile with GOOS=linux GOARCH=arm64 go build -o bloom . or GOARCH=arm for older Pi models.
Q: Can multiple users share one instance? Yes. Each user creates their own account. Data is isolated per user. Partners link via invite codes.
Q: What happens if I forget my encryption key? Your data is gone. There is no recovery, no backdoor, no reset. This is by design — we can't access your data even if we wanted to.
Q: Is this a medical device? No. Bloom is an awareness tool. It uses documented math for predictions but is not FDA-approved or medically validated. Always consult a healthcare provider for medical decisions.
Q: Can I migrate from Clue/Flo/another app?
Export your data from the other app (most support CSV), then use Bloom's Import page to paste your period dates. The format is flexible — 2024-01-15 to 2024-01-20 works, so does Jan 15 2024 - Jan 20 2024.
Q: How do partner notifications work? The cycle owner enables notifications in Settings. The partner gets a weekly summary email every Monday, plus instant alerts when severe symptoms (4-5) are logged or the cycle phase changes.
MIT — do whatever you want with it. Free forever.
Made with 💛 for anyone who wants to understand their body without giving up their privacy.