Deploying Endurain
Introduction
Endurain is a self-hosted fitness tracking service designed to give users complete control over their data and hosting environment. Think of it as your own private Strava alternative, focused on privacy, customization, and data ownership. Built with modern technologies including Vue.js for the frontend and Python FastAPI for the backend, Endurain provides a comprehensive platform for tracking workouts, managing gear, analyzing performance, and connecting with fellow fitness enthusiasts.
Endurain excels at helping athletes and fitness enthusiasts track their journey, whether you’re a runner, cyclist, swimmer, triathlete, or engage in any other fitness activity. The platform supports importing activities from Strava and Garmin Connect, manual activity logging, and direct file uploads in GPX, TCX, and FIT formats.
Why Endurain?
Endurain stands out with its comprehensive feature set and privacy-first approach:
- Complete Data Ownership: Your fitness data stays on your server under your complete control
- Multi-Sport Support: Track running, cycling, swimming, hiking, skiing, and many other activities
- File Format Flexibility: Import activities via GPX, TCX, and FIT files with FIT files being the preferred format
- Platform Integration: Seamless sync with Strava and Garmin Connect for activities, gear, and body composition
- Gear Management: Track multiple gear items (bikes, shoes, wetsuits, skis) and individual components
- Component Tracking: Monitor wear on gear components like bike chains, brake pads, and running shoe mileage
- Activity Analytics: Comprehensive statistics with weekly and monthly views, pace zones, and performance trends
- Social Features: Follow other users, view their activities, and build a fitness community
- Privacy Controls: Granular activity privacy settings to control who sees your workouts
- Body Composition Tracking: Log weight, steps, sleep, and other health metrics
- Goal Setting: Define and track personal fitness goals with progress monitoring
- Multi-Language Support: Available in 10+ languages including English, Spanish, German, French, and Chinese
- Metric and Imperial Units: Full support for both measurement systems
- Dark and Light Themes: Choose the interface that works best for you
- Multi-User Platform: Support for multiple users with admin and regular user roles
- Notification System: Stay informed about activities, followers, and achievements
- MFA Security: Two-factor authentication (TOTP) for enhanced account security
- SSO Support: Single sign-on via OIDC and SAML protocols
- Mobile Friendly: Responsive design that works beautifully on all devices
- Open Source: AGPL-3.0 licensed with active community development
Endurain is ideal for individual athletes, training groups, families, and fitness communities who value privacy and want full control over their training data.
Why Deploy on Klutch.sh?
Deploying Endurain on Klutch.sh offers several advantages:
- Simplified Deployment: Deploy directly from your GitHub repository with automatic builds
- Persistent Storage: Built-in volume support for your activity data and uploaded files
- Scalable Resources: Easily adjust resources as your fitness tracking needs grow
- HTTPS by Default: Automatic SSL certificates for secure access to your fitness data
- Database Support: Easy integration with PostgreSQL for reliable data storage
- Environment Management: Secure configuration of API keys and credentials
- High Availability: Reliable infrastructure ensures your fitness data is always accessible
- Custom Domains: Use your own domain for a professional fitness tracking platform
Prerequisites
Before deploying Endurain, ensure you have:
- A Klutch.sh account
- A GitHub account with a repository for your deployment configuration
- A PostgreSQL database (see our PostgreSQL deployment guide)
- (Optional) Strava API credentials for activity sync
- (Optional) Garmin Connect credentials for device integration
- (Optional) Email service credentials for notifications (Endurain uses Apprise)
- Basic familiarity with Docker and environment variables
Preparing Your Repository
Create a new GitHub repository for your Endurain deployment with the following structure:
endurain-deploy/├── Dockerfile├── .env.example├── .dockerignore├── README.md└── .gitignoreDockerfile
Create a production-ready Dockerfile for Endurain:
FROM ghcr.io/endurain-project/endurain:latest
# Set environment variablesENV TZ=UTC \ PYTHONUNBUFFERED=1
# Expose the application portEXPOSE 8000
# Health checkHEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \ CMD curl -f http://localhost:8000/api/health || exit 1
# The official Endurain image already includes the entrypoint# No additional configuration neededEnvironment Variables File
Create a .env.example file to document the required configuration:
# Database Configuration (Required)POSTGRES_HOST=your-postgres-app.klutch.shPOSTGRES_PORT=8000POSTGRES_DB=endurainPOSTGRES_USER=endurainPOSTGRES_PASSWORD=your_secure_password
# Application ConfigurationSECRET_KEY=your_very_long_random_secret_key_here_min_32_charsAPI_URL=https://example-app.klutch.shFRONTEND_URL=https://example-app.klutch.sh
# Admin Account (Initial Setup)ADMIN_EMAIL=admin@example.comADMIN_PASSWORD=changeme123ADMIN_USERNAME=admin
# Timezone ConfigurationTZ=America/New_York
# Optional: Strava IntegrationSTRAVA_CLIENT_ID=STRAVA_CLIENT_SECRET=
# Optional: Garmin Connect IntegrationGARMIN_EMAIL=GARMIN_PASSWORD=
# Optional: Email Notifications (via Apprise)SMTP_SERVER=smtp.gmail.comSMTP_PORT=587SMTP_FROM=noreply@example.comSMTP_USERNAME=SMTP_PASSWORD=
# Optional: Registration SettingsALLOW_REGISTRATION=trueREQUIRE_EMAIL_VERIFICATION=falseREQUIRE_ADMIN_APPROVAL=false
# Optional: Feature FlagsENABLE_MFA=trueENABLE_SSO=false
# Optional: Observability (Jaeger)JAEGER_ENABLED=falseJAEGER_HOST=localhostJAEGER_PORT=6831Docker Ignore File
Create a .dockerignore file:
.git.gitignore.env.env.example*.mdREADME.mddocker-compose.yml.vscode.idea*.lognode_modules__pycache__*.pyc.DS_StoreGit Ignore File
Create a .gitignore file to prevent sensitive data from being committed:
.env*.log.DS_Store__pycache__/*.pyc.vscode/.idea/node_modules/Deploying on Klutch.sh
-
Set Up PostgreSQL Database
Before deploying Endurain, you need a PostgreSQL database. Follow our PostgreSQL deployment guide to set up a database on Klutch.sh.
When creating your PostgreSQL instance, note the following:
- Database name:
endurain - Username:
endurain - Password: Choose a secure password
- Connection details:
your-postgres-app.klutch.sh:8000
- Database name:
-
Create a New Project
Log in to the Klutch.sh dashboard and create a new project for your Endurain deployment.
-
Connect Your GitHub Repository
In your Klutch.sh project:
- Navigate to the Apps section
- Click “Create New App”
- Select GitHub as your source
- Choose the repository containing your Endurain Dockerfile
- Select the branch you want to deploy (typically
mainormaster)
-
Configure Build Settings
Klutch.sh will automatically detect your Dockerfile. No additional configuration needed for the build process.
-
Set Traffic Type
In the deployment settings:
- Select HTTP as the traffic type
- Set the internal port to 8000
- Klutch.sh will automatically route traffic to your Endurain application
-
Configure Environment Variables
In your Klutch.sh app settings, add the following environment variables:
Required Variables:
Terminal window POSTGRES_HOST=your-postgres-app.klutch.shPOSTGRES_PORT=8000POSTGRES_DB=endurainPOSTGRES_USER=endurainPOSTGRES_PASSWORD=your_secure_passwordSECRET_KEY=generate_a_long_random_string_min_32_charactersAPI_URL=https://example-app.klutch.shFRONTEND_URL=https://example-app.klutch.shADMIN_EMAIL=admin@example.comADMIN_PASSWORD=changeme123ADMIN_USERNAME=adminTZ=America/New_YorkOptional Variables (Strava Integration):
Terminal window STRAVA_CLIENT_ID=your_strava_client_idSTRAVA_CLIENT_SECRET=your_strava_client_secretOptional Variables (Notifications):
Terminal window SMTP_SERVER=smtp.gmail.comSMTP_PORT=587SMTP_FROM=noreply@example.comSMTP_USERNAME=your_email@gmail.comSMTP_PASSWORD=your_app_passwordMark sensitive values like
POSTGRES_PASSWORD,SECRET_KEY,STRAVA_CLIENT_SECRET, andSMTP_PASSWORDas secret. -
Attach Persistent Volume
Endurain needs persistent storage for uploaded activity files, profile photos, and other assets:
- In your Klutch.sh app settings, navigate to Volumes
- Click “Add Volume”
- Set the mount path to
/app/data - Choose an appropriate size (start with 10GB, scale as needed)
- Save the volume configuration
-
Deploy the Application
Click “Deploy” to start the deployment process. Klutch.sh will:
- Pull your code from GitHub
- Build the Docker image
- Configure networking and volumes
- Start your Endurain application
The initial deployment may take 2-3 minutes as the database schema is initialized.
-
Verify Deployment
Once deployed, access your Endurain instance at
https://example-app.klutch.sh(replace with your actual app URL).You should see the Endurain login page. Use the admin credentials you configured to log in.
Initial Configuration
First Login
- Navigate to your Endurain URL:
https://example-app.klutch.sh - Log in with your admin credentials:
- Username:
admin(or what you set inADMIN_USERNAME) - Password: The value from
ADMIN_PASSWORD
- Username:
- Immediately change your admin password after first login
Configure User Settings
As an admin, you can configure registration and user management:
- Navigate to Settings → Admin Panel
- Configure registration settings:
- Allow new user registrations
- Require email verification
- Require admin approval for new accounts
- Set up user roles and permissions
- Configure default privacy settings for activities
Set Up Strava Integration
If you want to sync activities from Strava:
-
Create a Strava API Application:
- Go to Strava API Settings
- Click “Create an App”
- Set the authorization callback domain to your Endurain URL
- Note your Client ID and Client Secret
-
Configure Endurain:
- Add
STRAVA_CLIENT_IDandSTRAVA_CLIENT_SECRETto your environment variables - Redeploy the application
- Add
-
Connect Your Account:
- Go to Settings → Integrations
- Click “Connect Strava”
- Authorize the connection
- Configure sync settings (automatic or manual)
Set Up Garmin Connect Integration
For Garmin device sync:
-
Configure Credentials:
- Add
GARMIN_EMAILandGARMIN_PASSWORDenvironment variables - These should be your Garmin Connect account credentials
- Redeploy the application
- Add
-
Enable Sync:
- Go to Settings → Integrations
- Enable Garmin Connect sync
- Configure what data to sync (activities, gear, body composition)
-
Initial Sync:
- Trigger a manual sync to import existing activities
- Set up automatic sync schedule if desired
Using Endurain
Recording Activities
Manual Activity Entry:
- Click “Add Activity” from the dashboard
- Fill in activity details:
- Activity type (run, ride, swim, etc.)
- Date and time
- Duration
- Distance
- Notes and description
- Set privacy level (public, followers only, private)
- Assign gear used (if applicable)
- Save the activity
File Upload:
- Click “Upload Activity”
- Select your activity file (GPX, TCX, or FIT)
- Endurain will automatically parse:
- Route and GPS data
- Pace and speed metrics
- Elevation gain/loss
- Heart rate zones (if available)
- Power data (for cycling activities)
- Review and edit details if needed
- Save the activity
Bulk Upload:
For importing multiple activities at once:
- Go to Settings → Import
- Select multiple activity files
- Configure import settings (privacy, gear assignment)
- Start bulk import
- Monitor import progress
Managing Gear
Adding Gear:
- Navigate to Gear section
- Click “Add Gear”
- Select gear type:
- Bicycle (road, mountain, gravel, etc.)
- Running shoes
- Wetsuit
- Skis or snowboard
- Racquet
- Add details:
- Brand and model
- Purchase date
- Initial mileage
- Notes
- Set as default for specific activity types (optional)
Tracking Components:
Track wear on individual gear components:
- Select a gear item (e.g., your bike)
- Click “Add Component”
- Specify component:
- Chain
- Brake pads
- Tires
- Cassette
- Set replacement threshold (e.g., replace chain every 3,000 km)
- Endurain will alert you when replacement is due
Analyzing Performance
Dashboard Overview:
The dashboard provides at-a-glance metrics:
- Total distance this week/month
- Active time
- Elevation gain
- Activity count by type
- Recent activities feed
Activity Details:
Click any activity to view:
- Interactive route map
- Pace/speed graphs
- Heart rate zones
- Elevation profile
- Split times
- Gear used
- Photos and comments
Statistics Page:
View comprehensive stats:
- Distance and time by sport type
- Monthly and yearly trends
- Personal records
- Goal progress
- Gear usage statistics
Social Features
Following Users:
- Search for users by name or username
- Visit their profile
- Click “Follow”
- View their public and followers-only activities in your feed
Activity Sharing:
Control who sees your activities:
- Public: Visible to everyone
- Followers: Only users who follow you
- Private: Only you can see it
Kudos and Comments:
Engage with other users’ activities:
- Give kudos (likes) to activities
- Leave comments and encouragement
- View who has given you kudos
Sample Code and Getting Started
Using the Endurain API
Endurain exposes a comprehensive REST API for integrating with third-party applications and automation.
Authentication:
First, obtain an API token:
curl -X POST https://example-app.klutch.sh/api/auth/login \ -H "Content-Type: application/json" \ -d '{ "username": "your_username", "password": "your_password" }'Response:
{ "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "token_type": "bearer"}Creating an Activity via API (JavaScript):
const ENDURAIN_URL = "https://example-app.klutch.sh";const API_TOKEN = "your_access_token";
async function createActivity(activityData) { try { const response = await fetch(`${ENDURAIN_URL}/api/activities`, { method: "POST", headers: { "Authorization": `Bearer ${API_TOKEN}`, "Content-Type": "application/json" }, body: JSON.stringify({ name: activityData.name, sport_type: activityData.sportType, // "run", "ride", "swim", etc. start_time: new Date(activityData.startTime).toISOString(), duration: activityData.duration, // in seconds distance: activityData.distance, // in meters elevation_gain: activityData.elevationGain, // in meters description: activityData.description, privacy: activityData.privacy || "public", // "public", "followers", "private" gear_id: activityData.gearId || null }) });
if (response.ok) { const activity = await response.json(); console.log("Activity created successfully:", activity); return activity; } else { const error = await response.json(); console.error("Error creating activity:", error); } } catch (error) { console.error("Network error:", error); }}
// Usage examplecreateActivity({ name: "Morning Run", sportType: "run", startTime: "2025-12-18T07:00:00", duration: 3600, // 1 hour in seconds distance: 10000, // 10km in meters elevationGain: 150, description: "Great morning run through the park", privacy: "public"});Fetching Activities (Python):
import requestsfrom datetime import datetime, timedelta
ENDURAIN_URL = "https://example-app.klutch.sh"API_TOKEN = "your_access_token"
def get_recent_activities(days=7): """Fetch activities from the last N days""" headers = { "Authorization": f"Bearer {API_TOKEN}" }
since_date = (datetime.now() - timedelta(days=days)).isoformat()
response = requests.get( f"{ENDURAIN_URL}/api/activities", headers=headers, params={ "since": since_date, "limit": 50 } )
if response.status_code == 200: activities = response.json() print(f"Found {len(activities)} activities:") for activity in activities: print(f"- {activity['name']}: {activity['distance']/1000:.2f}km on {activity['start_time']}") return activities else: print(f"Error: {response.status_code}") return None
def get_activity_stats(activity_id): """Get detailed statistics for a specific activity""" headers = { "Authorization": f"Bearer {API_TOKEN}" }
response = requests.get( f"{ENDURAIN_URL}/api/activities/{activity_id}/stats", headers=headers )
if response.status_code == 200: stats = response.json() print(f"Activity Statistics:") print(f"Average Speed: {stats.get('average_speed')} m/s") print(f"Average Heart Rate: {stats.get('average_hr')} bpm") print(f"Calories: {stats.get('calories')}") return stats else: print(f"Error: {response.status_code}") return None
# Usageactivities = get_recent_activities(days=30)if activities and len(activities) > 0: get_activity_stats(activities[0]['id'])Upload Activity File (cURL):
# Upload a GPX filecurl -X POST https://example-app.klutch.sh/api/activities/upload \ -H "Authorization: Bearer your_access_token" \ -F "file=@/path/to/your/activity.gpx" \ -F "privacy=public" \ -F "gear_id=123"
# Upload a FIT file (preferred format)curl -X POST https://example-app.klutch.sh/api/activities/upload \ -H "Authorization: Bearer your_access_token" \ -F "file=@/path/to/your/activity.fit" \ -F "name=Evening Ride" \ -F "privacy=followers"Managing Gear (Go):
package main
import ( "bytes" "encoding/json" "fmt" "io/ioutil" "net/http")
const ( endurainURL = "https://example-app.klutch.sh" apiToken = "your_access_token")
type Gear struct { ID int `json:"id,omitempty"` Name string `json:"name"` GearType string `json:"gear_type"` // "bike", "shoes", "wetsuit", etc. Brand string `json:"brand"` Model string `json:"model"` TotalDistance float64 `json:"total_distance,omitempty"` Active bool `json:"active"`}
func createGear(gear Gear) (*Gear, error) { jsonData, err := json.Marshal(gear) if err != nil { return nil, err }
req, err := http.NewRequest("POST", endurainURL+"/api/gear", bytes.NewBuffer(jsonData)) if err != nil { return nil, err }
req.Header.Set("Authorization", "Bearer "+apiToken) req.Header.Set("Content-Type", "application/json")
client := &http.Client{} resp, err := client.Do(req) if err != nil { return nil, err } defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body) if err != nil { return nil, err }
if resp.StatusCode != http.StatusCreated { return nil, fmt.Errorf("error creating gear: %s", body) }
var createdGear Gear err = json.Unmarshal(body, &createdGear) if err != nil { return nil, err }
return &createdGear, nil}
func getGearList() ([]Gear, error) { req, err := http.NewRequest("GET", endurainURL+"/api/gear", nil) if err != nil { return nil, err }
req.Header.Set("Authorization", "Bearer "+apiToken)
client := &http.Client{} resp, err := client.Do(req) if err != nil { return nil, err } defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body) if err != nil { return nil, err }
var gearList []Gear err = json.Unmarshal(body, &gearList) if err != nil { return nil, err }
return gearList, nil}
func main() { // Create new gear newBike := Gear{ Name: "Road Bike", GearType: "bike", Brand: "Specialized", Model: "Tarmac SL7", Active: true, }
gear, err := createGear(newBike) if err != nil { fmt.Println("Error creating gear:", err) return } fmt.Printf("Created gear: %s (ID: %d)\n", gear.Name, gear.ID)
// List all gear gearList, err := getGearList() if err != nil { fmt.Println("Error fetching gear:", err) return }
fmt.Println("\nYour gear:") for _, g := range gearList { fmt.Printf("- %s (%s): %.2f km\n", g.Name, g.GearType, g.TotalDistance/1000) }}Advanced Configuration
Custom Domain Setup
Use your own domain for a professional fitness tracking platform:
-
Add Custom Domain in Klutch.sh:
- Navigate to your app in the Klutch.sh dashboard
- Go to Domains section
- Click “Add Custom Domain”
- Enter your domain (e.g.,
fitness.example.com)
-
Configure DNS:
-
Add a CNAME record in your DNS provider:
Type: CNAMEName: fitnessValue: example-app.klutch.shTTL: 3600
-
-
Update Environment Variables:
Terminal window API_URL=https://fitness.example.comFRONTEND_URL=https://fitness.example.com -
Redeploy:
- Redeploy your application for changes to take effect
- Wait for DNS propagation (typically 5-30 minutes)
-
Update OAuth Callbacks:
- If using Strava or other OAuth providers
- Update callback URLs in their developer consoles
Email Notifications Setup
Configure email notifications for password resets, activity kudos, and follower alerts:
-
Using Gmail:
Terminal window SMTP_SERVER=smtp.gmail.comSMTP_PORT=587SMTP_FROM=your-email@gmail.comSMTP_USERNAME=your-email@gmail.comSMTP_PASSWORD=your-app-specific-passwordNote: Use an App Password, not your regular Gmail password.
-
Using SendGrid:
Terminal window SMTP_SERVER=smtp.sendgrid.netSMTP_PORT=587SMTP_FROM=noreply@example.comSMTP_USERNAME=apikeySMTP_PASSWORD=your-sendgrid-api-key -
Using Mailgun:
Terminal window SMTP_SERVER=smtp.mailgun.orgSMTP_PORT=587SMTP_FROM=noreply@mg.example.comSMTP_USERNAME=postmaster@mg.example.comSMTP_PASSWORD=your-mailgun-password -
Test Email Configuration:
- Trigger a password reset to test
- Check spam folder if emails don’t arrive
- Verify SPF and DKIM records for better deliverability
Single Sign-On (SSO) Configuration
Endurain supports SSO via OIDC and SAML protocols:
-
Enable SSO:
Terminal window ENABLE_SSO=true -
Configure OIDC Provider (Example: Keycloak):
Terminal window SSO_TYPE=oidcSSO_CLIENT_ID=endurainSSO_CLIENT_SECRET=your-client-secretSSO_ISSUER=https://keycloak.example.com/realms/your-realmSSO_AUTHORIZATION_ENDPOINT=https://keycloak.example.com/realms/your-realm/protocol/openid-connect/authSSO_TOKEN_ENDPOINT=https://keycloak.example.com/realms/your-realm/protocol/openid-connect/tokenSSO_USERINFO_ENDPOINT=https://keycloak.example.com/realms/your-realm/protocol/openid-connect/userinfo -
Configure SAML Provider:
Terminal window SSO_TYPE=samlSSO_IDP_METADATA_URL=https://idp.example.com/metadataSSO_ENTITY_ID=https://fitness.example.com -
Redeploy the application
Multi-Factor Authentication
Enable MFA for enhanced security:
ENABLE_MFA=trueUsers can then:
- Go to Settings → Security
- Click “Enable Two-Factor Authentication”
- Scan QR code with authenticator app (Google Authenticator, Authy, etc.)
- Enter verification code to confirm
Production Best Practices
Database Optimization
Regular Backups:
- Set up automated PostgreSQL backups (see our PostgreSQL guide)
- Test restore procedures regularly
- Store backups off-site or in different cloud region
Connection Pooling:
For high-traffic deployments, configure connection pooling:
POSTGRES_MAX_CONNECTIONS=20POSTGRES_MIN_CONNECTIONS=5POSTGRES_POOL_TIMEOUT=30Database Maintenance:
Run regular maintenance on PostgreSQL:
-- Vacuum and analyze tablesVACUUM ANALYZE;
-- Reindex for performanceREINDEX DATABASE endurain;
-- Check for bloatSELECT schemaname, tablename, pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename)) AS sizeFROM pg_tablesORDER BY pg_total_relation_size(schemaname||'.'||tablename) DESCLIMIT 10;Security Hardening
Environment Variables:
- Never commit
.envfiles to version control - Use Klutch.sh’s secret management for sensitive values
- Rotate secrets regularly (every 90 days recommended)
HTTPS Only:
Enforce HTTPS for all traffic (Klutch.sh handles this automatically)
Rate Limiting:
Endurain includes built-in rate limiting. Configure if needed:
RATE_LIMIT_ENABLED=trueRATE_LIMIT_REQUESTS=100RATE_LIMIT_PERIOD=60 # secondsSecurity Headers:
Endurain automatically sets security headers:
- Content Security Policy (CSP)
- X-Frame-Options
- X-Content-Type-Options
- Strict-Transport-Security
User Permissions:
- Regularly audit user accounts
- Remove inactive users
- Monitor admin access logs
- Enable MFA for all admin accounts
Performance Optimization
Volume Size Planning:
Start with 10GB for volumes and monitor usage:
- Activity files: ~5-10MB per activity with full GPS
- Profile photos: ~500KB per user
- Gear photos: ~1MB per item
Scale up as needed based on:
# Users x Average activities per user x Average file size1000 users × 100 activities × 5MB = 500GBCaching:
Endurain includes in-memory caching for improved performance. No additional configuration needed for small deployments.
For larger deployments, consider adding Redis:
REDIS_URL=redis://your-redis-app.klutch.sh:8000REDIS_CACHE_TTL=3600CDN for Static Assets:
For global deployments, consider using a CDN:
- Configure Cloudflare or similar CDN
- Point CDN to your Endurain domain
- Enable caching for static assets
Monitoring and Observability
Health Check Endpoint:
Monitor your Endurain instance:
curl https://example-app.klutch.sh/api/healthExpected response:
{ "status": "healthy", "database": "connected", "version": "0.16.3"}Application Metrics:
View application metrics in the admin panel:
- Total users and activities
- Storage usage
- API request counts
- Database query performance
Enable Jaeger Tracing (Optional):
For advanced observability:
JAEGER_ENABLED=trueJAEGER_HOST=your-jaeger-hostJAEGER_PORT=6831This enables distributed tracing for debugging and performance analysis.
Log Monitoring:
Access logs through Klutch.sh dashboard:
- Navigate to your app
- Click “Logs”
- Monitor for errors or warnings
- Set up alerts for critical issues
Scaling Considerations
Vertical Scaling:
Increase resources as user base grows:
- Start with 1 CPU / 2GB RAM
- Scale to 2 CPU / 4GB RAM for 100+ active users
- Scale to 4 CPU / 8GB RAM for 1000+ active users
Database Scaling:
- Start with standard PostgreSQL instance
- Upgrade to larger instance as data grows
- Consider read replicas for analytics queries
File Storage Scaling:
- Monitor volume usage regularly
- Scale volume size as needed (Klutch.sh supports online scaling)
- Consider object storage (S3) for very large deployments
Troubleshooting
Application Won’t Start
Check Database Connection:
# Verify PostgreSQL is accessiblecurl -v telnet://your-postgres-app.klutch.sh:8000Verify Environment Variables:
- Ensure all required variables are set
- Check for typos in variable names
- Verify SECRET_KEY is at least 32 characters
Check Logs:
Review application logs in Klutch.sh dashboard for specific error messages.
Activities Not Importing
File Format Issues:
- FIT files are preferred over GPX and TCX
- Ensure files are not corrupted
- Check file size limits (default: 50MB per file)
Missing GPS Data:
- Some indoor activities may not have GPS tracks
- Manual distance entry is available for these activities
Integration Sync Failures:
For Strava or Garmin:
- Verify API credentials are correct
- Check OAuth token hasn’t expired
- Re-authenticate if necessary
- Check API rate limits
Performance Issues
Slow Activity Loading:
- Check database performance
- Run VACUUM ANALYZE on PostgreSQL
- Consider adding database indexes
- Enable caching if not already enabled
High Memory Usage:
- Monitor memory in Klutch.sh dashboard
- Consider scaling to larger instance
- Check for memory leaks in logs
- Restart application if needed
Upload Failures:
- Check volume storage space
- Verify file permissions on volume mount
- Check maximum file size limits
- Review logs for specific errors
Email Notifications Not Working
SMTP Issues:
- Verify SMTP credentials
- Check SMTP server allows connections from Klutch.sh IPs
- Test with different email provider
- Check spam folders
Apprise Configuration:
Endurain uses Apprise for flexible notification delivery. Configure alternative providers:
APPRISE_URL=mailto://smtp.gmail.com:587/?from=noreply@example.com&to=admin@example.com&user=your-email@gmail.com&pass=your-app-passwordOAuth Integration Issues
Strava Connection Fails:
- Verify callback URL matches in Strava app settings
- Check client ID and secret are correct
- Ensure API_URL environment variable is correct
- Try disconnecting and reconnecting
Garmin Sync Problems:
- Verify credentials are correct
- Check if Garmin account requires 2FA (may cause issues)
- Check Garmin API status
- Try manual activity upload as alternative
Database Migration Issues
Migration Fails on Deployment:
# Check migration statusdocker exec -it your-container-id alembic current
# Run migrations manuallydocker exec -it your-container-id alembic upgrade headSchema Conflicts:
- Ensure database is empty for fresh install
- Or ensure existing schema matches expected version
- Check Endurain version compatibility
Additional Resources
- Endurain Official Documentation
- Endurain GitHub Repository
- Endurain Discord Community
- Endurain Mastodon
- PostgreSQL Deployment Guide
- Persistent Volumes Documentation
- Deployment Configuration
Conclusion
Deploying Endurain on Klutch.sh gives you a complete, privacy-focused fitness tracking platform with full control over your data. Whether you’re an individual athlete, a training group, or building a fitness community, Endurain provides powerful tools for tracking workouts, managing gear, analyzing performance, and connecting with others. With persistent storage, database integration, and seamless third-party sync, your fitness journey is captured in detail while remaining completely under your control.
Start tracking your fitness journey today by deploying Endurain on Klutch.sh and take ownership of your training data.