Production Setup¶
Configure Tempo for production deployment with best practices.
Overview¶
This guide covers production configuration, environment variables, and deployment considerations.
Environment Variables¶
Configure the following in docker-compose.prod.yml or via environment files:
Required Configuration¶
JWT Secret Key¶
CRITICAL: Must be set to a secure random value.
Set in docker-compose.prod.yml:
Or use an environment variable:
Database Configuration¶
ConnectionStrings__DefaultConnection: "Host=postgres;Port=5432;Database=tempo;Username=postgres;Password=YOUR_SECURE_PASSWORD"
Important: Change the default database password in production.
CORS Configuration¶
Configure allowed origins:
For multiple origins, use comma-separated list.
Media Storage¶
Elevation Calculation¶
JWT Configuration¶
JWT__SecretKey: "YOUR_SECRET_KEY" # REQUIRED
JWT__Issuer: "Tempo"
JWT__Audience: "Tempo"
JWT__ExpirationDays: "7"
Production Checklist¶
Security¶
- JWT secret key is set and secure (minimum 32 characters)
- Database password changed from default
- HTTPS configured (required for secure cookies)
- CORS origins configured correctly
- Firewall rules configured
- Regular security updates applied
Configuration¶
- Environment variables set correctly
- Database connection string configured
- Media storage path configured and writable
- Ports configured appropriately
- Health checks enabled
Data Management¶
- Backup strategy in place
- Media directory included in backups
- Database backup automated
- Sufficient disk space allocated
Monitoring¶
- Health check endpoint accessible
- Logs configured and monitored
- Resource usage monitored
- Error tracking in place
Reverse Proxy Setup¶
Nginx Example¶
server {
listen 80;
server_name yourdomain.com;
# Redirect to HTTPS
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name yourdomain.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
# Frontend
location / {
proxy_pass http://localhost:3004;
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;
}
# API
location /api/ {
proxy_pass http://localhost:5001/;
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;
# Large file upload support (bulk imports up to 500MB)
client_max_body_size 500M;
proxy_read_timeout 600s;
proxy_connect_timeout 600s;
proxy_send_timeout 600s;
}
}
Caddy Example¶
# API endpoint for iOS app (direct API access)
api-tempo.yourdomain.com {
tls {
protocols tls1.2 tls1.3
}
# Large file uploads (bulk imports up to 500MB)
reverse_proxy localhost:5001 {
transport http {
read_timeout 30m
write_timeout 30m
}
}
}
# Web frontend with API routing
tempo.yourdomain.com {
tls {
protocols tls1.2 tls1.3
}
# Large file uploads (bulk imports) - route to API (strip only /api prefix)
@large_upload {
path /api/workouts/import/*
}
handle @large_upload {
uri strip_prefix /api
reverse_proxy localhost:5001 {
transport http {
read_timeout 30m
write_timeout 30m
}
header_up Host {host}
header_up X-Real-IP {remote}
header_up X-Forwarded-For {remote_host}
header_up X-Forwarded-Proto {scheme}
}
}
# Other API routes (strip only /api prefix)
handle_path /api/* {
reverse_proxy localhost:5001 {
header_up Host {host}
header_up X-Real-IP {remote}
header_up X-Forwarded-For {remote_host}
header_up X-Forwarded-Proto {scheme}
}
}
# Frontend
handle {
reverse_proxy localhost:3004 {
header_up Host {host}
header_up X-Real-IP {remote}
header_up X-Forwarded-For {remote_host}
header_up X-Forwarded-Proto {scheme}
}
}
}
Note:
- Adjust the hostnames and ports based on your deployment setup
- The api-tempo.yourdomain.com subdomain is optional and only needed if you have a mobile app that requires direct API access
- The uri strip_prefix /api directive ensures that /api/workouts/import/bulk is forwarded to the API as /workouts/import/bulk (stripping only the /api prefix)
Traefik Example¶
labels:
- "traefik.enable=true"
- "traefik.http.routers.tempo.rule=Host(`yourdomain.com`)"
- "traefik.http.routers.tempo.entrypoints=websecure"
- "traefik.http.routers.tempo.tls.certresolver=letsencrypt"
# Large file upload support
- "traefik.http.middlewares.tempo-buffering.buffering.maxRequestBodyBytes=524288000" # 500MB
- "traefik.http.middlewares.tempo-timeout.buffering.retryExpression=IsNetworkError() && Attempts() < 2"
- "traefik.http.routers.tempo.middlewares=tempo-buffering,tempo-timeout"
Large File Upload Requirements¶
Tempo supports bulk imports of up to 500MB (Strava exports, Tempo exports). Your reverse proxy must be configured to handle these large uploads:
Required Settings¶
- Maximum body size: 500MB minimum
- Read timeout: 10-30 minutes (depending on expected upload speed)
- Write timeout: 10-30 minutes
- Connect timeout: 10 minutes
These settings are already configured in the API (Program.cs) and only need to be set at the reverse proxy level.
Testing Large Uploads¶
After configuration, test with a large file: 1. Export your data from Strava (typically 50-200MB) 2. Import via Settings > Import > Bulk Strava Import 3. Monitor reverse proxy logs for timeout errors
SSL/TLS Configuration¶
Let's Encrypt (Certbot)¶
# Install Certbot
sudo apt-get install certbot python3-certbot-nginx
# Obtain certificate
sudo certbot --nginx -d yourdomain.com
# Auto-renewal
sudo certbot renew --dry-run
Self-Signed Certificate (Development Only)¶
Note: Self-signed certificates are not recommended for production.
Performance Optimization¶
Resource Limits¶
Configure Docker resource limits:
Database Optimization¶
- Configure PostgreSQL shared_buffers
- Set appropriate max_connections
- Enable query logging for optimization
- Regular VACUUM and ANALYZE
Media Storage¶
- Use fast storage for media directory
- Consider object storage for large deployments
- Implement cleanup for old media files
Monitoring and Logging¶
Health Checks¶
Monitor the health endpoint:
Logging¶
View container logs:
docker-compose -f docker-compose.prod.yml logs -f api
docker-compose -f docker-compose.prod.yml logs -f frontend
Resource Monitoring¶
Monitor resource usage: