Docker Compose Deployment
Deploy Streaklet using Docker Compose for easy management and configuration.
Quick Start
1. Create docker-compose.yml
services:
streaklet:
image: ghcr.io/ptmetcalf/streaklet:latest
container_name: streaklet
restart: unless-stopped
ports:
- "8080:8080"
volumes:
- ./data:/data
environment:
- APP_TIMEZONE=America/Chicago
- DB_PATH=/data/app.db
# Optional: Fitbit integration
# - FITBIT_CLIENT_ID=your_client_id
# - FITBIT_CLIENT_SECRET=your_client_secret
# - APP_SECRET_KEY=your_random_32_char_secret
2. Prepare Data Directory
The Docker image runs as non-root user (uid 1000) for security:
3. Start the Container
4. Access the Application
Open your browser to: http://localhost:8080
Configuration
Environment Variables
| Variable | Required | Default | Description |
|---|---|---|---|
APP_TIMEZONE |
No | America/Chicago |
IANA timezone for daily tracking |
DB_PATH |
No | /data/app.db |
SQLite database file path |
PORT |
No | 8080 |
Application port |
FITBIT_CLIENT_ID |
No | - | Fitbit OAuth client ID |
FITBIT_CLIENT_SECRET |
No | - | Fitbit OAuth client secret |
APP_SECRET_KEY |
No* | - | Secret key for token encryption |
* Required if using Fitbit integration
Timezone Configuration
Set your local timezone for accurate daily tracking:
Common timezones:
- America/New_York - Eastern Time
- America/Chicago - Central Time
- America/Denver - Mountain Time
- America/Los_Angeles - Pacific Time
- Europe/London - UK
- Europe/Paris - Central European Time
- Asia/Tokyo - Japan
Fitbit Integration
To enable Fitbit integration, add all three environment variables:
environment:
- FITBIT_CLIENT_ID=ABC123
- FITBIT_CLIENT_SECRET=xyz789secretkey
- APP_SECRET_KEY=your_secure_random_32_character_string
Important: Keep APP_SECRET_KEY consistent. Changing it will invalidate all encrypted Fitbit tokens.
Generate a secure key:
Storage
Volume Mounts
Streaklet stores all data in /data inside the container. Mount this directory to persist data:
Named Volumes
For named volumes, define at the bottom of docker-compose.yml:
services:
streaklet:
# ... configuration ...
volumes:
- streaklet-data:/data
volumes:
streaklet-data:
List volumes:
Inspect volume location:
Permissions
The container runs as appuser (uid 1000, gid 1000). Ensure the data directory is writable:
Networking
Port Mapping
Change the host port:
Reverse Proxy
For production use with reverse proxy (nginx, Traefik, Caddy):
services:
streaklet:
# ... other config ...
expose:
- "8080" # Don't publish ports directly
labels:
- "traefik.enable=true"
- "traefik.http.routers.streaklet.rule=Host(`streaklet.example.com`)"
See Homelab Integration for reverse proxy examples.
Updates
Pulling Updates
This pulls the latest image and recreates the container.
Viewing Logs
# Follow logs in real-time
docker compose logs -f
# View last 100 lines
docker compose logs --tail=100
# View logs for specific time range
docker compose logs --since=1h
Restarting
# Restart the service
docker compose restart
# Stop and start (recreates containers)
docker compose down
docker compose up -d
Backup
Database Backup
Recommended: Use the built-in backup API (see Backup & Restore)
Alternative: Copy the SQLite database directly:
# Stop the container first
docker compose stop
# Copy database
cp ./data/app.db ./backups/app-$(date +%Y-%m-%d).db
# Start the container
docker compose start
Automated Backups
Add a backup service to docker-compose.yml:
services:
streaklet:
# ... streaklet config ...
backup:
image: alpine:latest
container_name: streaklet-backup
volumes:
- ./data:/data:ro # Read-only
- ./backups:/backups
command: |
sh -c "
while true; do
cp /data/app.db /backups/app-\$(date +%Y-%m-%d).db
sleep 86400 # Daily
done
"
Health Checks
Docker Compose health check:
services:
streaklet:
# ... other config ...
healthcheck:
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8080/health')"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
Check health status:
Troubleshooting
Container won't start
Check logs:
Common issues:
- Permission denied: Run chown -R 1000:1000 ./data
- Port already in use: Change port mapping or stop conflicting service
- Invalid environment variable: Check .env file syntax
Cannot write to database
Ensure data directory is writable by uid 1000:
Fitbit connection fails
Verify environment variables are set:
Check callback URL in Fitbit app settings matches:
Container keeps restarting
View restart count:
Check logs for error messages:
Common causes:
- Database corruption (restore from backup)
- Filesystem issues (check disk space: df -h)
- Invalid configuration
Advanced Configuration
Resource Limits
Limit CPU and memory usage:
services:
streaklet:
# ... other config ...
deploy:
resources:
limits:
cpus: '0.5'
memory: 512M
reservations:
memory: 256M
Custom Network
Create isolated network:
Read-Only Root Filesystem
For extra security:
Complete Example
Full-featured docker-compose.yml:
version: '3.8'
services:
streaklet:
image: ghcr.io/ptmetcalf/streaklet:latest
container_name: streaklet
restart: unless-stopped
ports:
- "8080:8080"
volumes:
- streaklet-data:/data
environment:
- APP_TIMEZONE=America/Chicago
- DB_PATH=/data/app.db
- FITBIT_CLIENT_ID=${FITBIT_CLIENT_ID}
- FITBIT_CLIENT_SECRET=${FITBIT_CLIENT_SECRET}
- APP_SECRET_KEY=${APP_SECRET_KEY}
healthcheck:
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8080/health')"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
deploy:
resources:
limits:
cpus: '0.5'
memory: 512M
labels:
- "com.example.description=Streaklet habit tracker"
- "com.example.version=1.0"
volumes:
streaklet-data:
driver: local
networks:
default:
driver: bridge
With .env file:
FITBIT_CLIENT_ID=your_client_id
FITBIT_CLIENT_SECRET=your_secret
APP_SECRET_KEY=your_secure_random_key_32_chars_min
Next Steps
- Homelab Integration - Integrate with reverse proxies and home servers
- Configuration Guide - Detailed configuration options
- Backup & Restore - Data backup strategies