Skip to content

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
└── .gitignore

Dockerfile

Create a production-ready Dockerfile for Endurain:

FROM ghcr.io/endurain-project/endurain:latest
# Set environment variables
ENV TZ=UTC \
PYTHONUNBUFFERED=1
# Expose the application port
EXPOSE 8000
# Health check
HEALTHCHECK --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 needed

Environment Variables File

Create a .env.example file to document the required configuration:

Terminal window
# Database Configuration (Required)
POSTGRES_HOST=your-postgres-app.klutch.sh
POSTGRES_PORT=8000
POSTGRES_DB=endurain
POSTGRES_USER=endurain
POSTGRES_PASSWORD=your_secure_password
# Application Configuration
SECRET_KEY=your_very_long_random_secret_key_here_min_32_chars
API_URL=https://example-app.klutch.sh
FRONTEND_URL=https://example-app.klutch.sh
# Admin Account (Initial Setup)
ADMIN_EMAIL=admin@example.com
ADMIN_PASSWORD=changeme123
ADMIN_USERNAME=admin
# Timezone Configuration
TZ=America/New_York
# Optional: Strava Integration
STRAVA_CLIENT_ID=
STRAVA_CLIENT_SECRET=
# Optional: Garmin Connect Integration
GARMIN_EMAIL=
GARMIN_PASSWORD=
# Optional: Email Notifications (via Apprise)
SMTP_SERVER=smtp.gmail.com
SMTP_PORT=587
SMTP_FROM=noreply@example.com
SMTP_USERNAME=
SMTP_PASSWORD=
# Optional: Registration Settings
ALLOW_REGISTRATION=true
REQUIRE_EMAIL_VERIFICATION=false
REQUIRE_ADMIN_APPROVAL=false
# Optional: Feature Flags
ENABLE_MFA=true
ENABLE_SSO=false
# Optional: Observability (Jaeger)
JAEGER_ENABLED=false
JAEGER_HOST=localhost
JAEGER_PORT=6831

Docker Ignore File

Create a .dockerignore file:

.git
.gitignore
.env
.env.example
*.md
README.md
docker-compose.yml
.vscode
.idea
*.log
node_modules
__pycache__
*.pyc
.DS_Store

Git 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

  1. 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
  2. Create a New Project

    Log in to the Klutch.sh dashboard and create a new project for your Endurain deployment.

  3. Connect Your GitHub Repository

    In your Klutch.sh project:

    1. Navigate to the Apps section
    2. Click “Create New App”
    3. Select GitHub as your source
    4. Choose the repository containing your Endurain Dockerfile
    5. Select the branch you want to deploy (typically main or master)
  4. Configure Build Settings

    Klutch.sh will automatically detect your Dockerfile. No additional configuration needed for the build process.

  5. Set Traffic Type

    In the deployment settings:

    1. Select HTTP as the traffic type
    2. Set the internal port to 8000
    3. Klutch.sh will automatically route traffic to your Endurain application
  6. Configure Environment Variables

    In your Klutch.sh app settings, add the following environment variables:

    Required Variables:

    Terminal window
    POSTGRES_HOST=your-postgres-app.klutch.sh
    POSTGRES_PORT=8000
    POSTGRES_DB=endurain
    POSTGRES_USER=endurain
    POSTGRES_PASSWORD=your_secure_password
    SECRET_KEY=generate_a_long_random_string_min_32_characters
    API_URL=https://example-app.klutch.sh
    FRONTEND_URL=https://example-app.klutch.sh
    ADMIN_EMAIL=admin@example.com
    ADMIN_PASSWORD=changeme123
    ADMIN_USERNAME=admin
    TZ=America/New_York

    Optional Variables (Strava Integration):

    Terminal window
    STRAVA_CLIENT_ID=your_strava_client_id
    STRAVA_CLIENT_SECRET=your_strava_client_secret

    Optional Variables (Notifications):

    Terminal window
    SMTP_SERVER=smtp.gmail.com
    SMTP_PORT=587
    SMTP_FROM=noreply@example.com
    SMTP_USERNAME=your_email@gmail.com
    SMTP_PASSWORD=your_app_password

    Mark sensitive values like POSTGRES_PASSWORD, SECRET_KEY, STRAVA_CLIENT_SECRET, and SMTP_PASSWORD as secret.

  7. Attach Persistent Volume

    Endurain needs persistent storage for uploaded activity files, profile photos, and other assets:

    1. In your Klutch.sh app settings, navigate to Volumes
    2. Click “Add Volume”
    3. Set the mount path to /app/data
    4. Choose an appropriate size (start with 10GB, scale as needed)
    5. Save the volume configuration
  8. Deploy the Application

    Click “Deploy” to start the deployment process. Klutch.sh will:

    1. Pull your code from GitHub
    2. Build the Docker image
    3. Configure networking and volumes
    4. Start your Endurain application

    The initial deployment may take 2-3 minutes as the database schema is initialized.

  9. 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

  1. Navigate to your Endurain URL: https://example-app.klutch.sh
  2. Log in with your admin credentials:
    • Username: admin (or what you set in ADMIN_USERNAME)
    • Password: The value from ADMIN_PASSWORD
  3. Immediately change your admin password after first login

Configure User Settings

As an admin, you can configure registration and user management:

    1. Navigate to SettingsAdmin Panel
    2. Configure registration settings:
      • Allow new user registrations
      • Require email verification
      • Require admin approval for new accounts
    3. Set up user roles and permissions
    4. Configure default privacy settings for activities

Set Up Strava Integration

If you want to sync activities from Strava:

    1. 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
    2. Configure Endurain:

      • Add STRAVA_CLIENT_ID and STRAVA_CLIENT_SECRET to your environment variables
      • Redeploy the application
    3. Connect Your Account:

      • Go to SettingsIntegrations
      • Click “Connect Strava”
      • Authorize the connection
      • Configure sync settings (automatic or manual)

Set Up Garmin Connect Integration

For Garmin device sync:

    1. Configure Credentials:

      • Add GARMIN_EMAIL and GARMIN_PASSWORD environment variables
      • These should be your Garmin Connect account credentials
      • Redeploy the application
    2. Enable Sync:

      • Go to SettingsIntegrations
      • Enable Garmin Connect sync
      • Configure what data to sync (activities, gear, body composition)
    3. Initial Sync:

      • Trigger a manual sync to import existing activities
      • Set up automatic sync schedule if desired

Using Endurain

Recording Activities

Manual Activity Entry:

    1. Click “Add Activity” from the dashboard
    2. Fill in activity details:
      • Activity type (run, ride, swim, etc.)
      • Date and time
      • Duration
      • Distance
      • Notes and description
    3. Set privacy level (public, followers only, private)
    4. Assign gear used (if applicable)
    5. Save the activity

File Upload:

    1. Click “Upload Activity”
    2. Select your activity file (GPX, TCX, or FIT)
    3. 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)
    4. Review and edit details if needed
    5. Save the activity

Bulk Upload:

For importing multiple activities at once:

    1. Go to SettingsImport
    2. Select multiple activity files
    3. Configure import settings (privacy, gear assignment)
    4. Start bulk import
    5. Monitor import progress

Managing Gear

Adding Gear:

    1. Navigate to Gear section
    2. Click “Add Gear”
    3. Select gear type:
      • Bicycle (road, mountain, gravel, etc.)
      • Running shoes
      • Wetsuit
      • Skis or snowboard
      • Racquet
    4. Add details:
      • Brand and model
      • Purchase date
      • Initial mileage
      • Notes
    5. Set as default for specific activity types (optional)

Tracking Components:

Track wear on individual gear components:

    1. Select a gear item (e.g., your bike)
    2. Click “Add Component”
    3. Specify component:
      • Chain
      • Brake pads
      • Tires
      • Cassette
    4. Set replacement threshold (e.g., replace chain every 3,000 km)
    5. 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:

    1. Search for users by name or username
    2. Visit their profile
    3. Click “Follow”
    4. 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:

Terminal window
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 example
createActivity({
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 requests
from 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
# Usage
activities = get_recent_activities(days=30)
if activities and len(activities) > 0:
get_activity_stats(activities[0]['id'])

Upload Activity File (cURL):

Terminal window
# Upload a GPX file
curl -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:

    1. 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)
    2. Configure DNS:

      • Add a CNAME record in your DNS provider:

        Type: CNAME
        Name: fitness
        Value: example-app.klutch.sh
        TTL: 3600
    3. Update Environment Variables:

      Terminal window
      API_URL=https://fitness.example.com
      FRONTEND_URL=https://fitness.example.com
    4. Redeploy:

      • Redeploy your application for changes to take effect
      • Wait for DNS propagation (typically 5-30 minutes)
    5. 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:

    1. Using Gmail:

      Terminal window
      SMTP_SERVER=smtp.gmail.com
      SMTP_PORT=587
      SMTP_FROM=your-email@gmail.com
      SMTP_USERNAME=your-email@gmail.com
      SMTP_PASSWORD=your-app-specific-password

      Note: Use an App Password, not your regular Gmail password.

    2. Using SendGrid:

      Terminal window
      SMTP_SERVER=smtp.sendgrid.net
      SMTP_PORT=587
      SMTP_FROM=noreply@example.com
      SMTP_USERNAME=apikey
      SMTP_PASSWORD=your-sendgrid-api-key
    3. Using Mailgun:

      Terminal window
      SMTP_SERVER=smtp.mailgun.org
      SMTP_PORT=587
      SMTP_FROM=noreply@mg.example.com
      SMTP_USERNAME=postmaster@mg.example.com
      SMTP_PASSWORD=your-mailgun-password
    4. 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:

    1. Enable SSO:

      Terminal window
      ENABLE_SSO=true
    2. Configure OIDC Provider (Example: Keycloak):

      Terminal window
      SSO_TYPE=oidc
      SSO_CLIENT_ID=endurain
      SSO_CLIENT_SECRET=your-client-secret
      SSO_ISSUER=https://keycloak.example.com/realms/your-realm
      SSO_AUTHORIZATION_ENDPOINT=https://keycloak.example.com/realms/your-realm/protocol/openid-connect/auth
      SSO_TOKEN_ENDPOINT=https://keycloak.example.com/realms/your-realm/protocol/openid-connect/token
      SSO_USERINFO_ENDPOINT=https://keycloak.example.com/realms/your-realm/protocol/openid-connect/userinfo
    3. Configure SAML Provider:

      Terminal window
      SSO_TYPE=saml
      SSO_IDP_METADATA_URL=https://idp.example.com/metadata
      SSO_ENTITY_ID=https://fitness.example.com
    4. Redeploy the application

Multi-Factor Authentication

Enable MFA for enhanced security:

Terminal window
ENABLE_MFA=true

Users can then:

  1. Go to SettingsSecurity
  2. Click “Enable Two-Factor Authentication”
  3. Scan QR code with authenticator app (Google Authenticator, Authy, etc.)
  4. Enter verification code to confirm

Production Best Practices

Database Optimization

Regular Backups:

    1. Set up automated PostgreSQL backups (see our PostgreSQL guide)
    2. Test restore procedures regularly
    3. Store backups off-site or in different cloud region

Connection Pooling:

For high-traffic deployments, configure connection pooling:

Terminal window
POSTGRES_MAX_CONNECTIONS=20
POSTGRES_MIN_CONNECTIONS=5
POSTGRES_POOL_TIMEOUT=30

Database Maintenance:

Run regular maintenance on PostgreSQL:

-- Vacuum and analyze tables
VACUUM ANALYZE;
-- Reindex for performance
REINDEX DATABASE endurain;
-- Check for bloat
SELECT schemaname, tablename,
pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename)) AS size
FROM pg_tables
ORDER BY pg_total_relation_size(schemaname||'.'||tablename) DESC
LIMIT 10;

Security Hardening

Environment Variables:

  • Never commit .env files 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:

Terminal window
RATE_LIMIT_ENABLED=true
RATE_LIMIT_REQUESTS=100
RATE_LIMIT_PERIOD=60 # seconds

Security 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:

Terminal window
# Users x Average activities per user x Average file size
1000 users × 100 activities × 5MB = 500GB

Caching:

Endurain includes in-memory caching for improved performance. No additional configuration needed for small deployments.

For larger deployments, consider adding Redis:

Terminal window
REDIS_URL=redis://your-redis-app.klutch.sh:8000
REDIS_CACHE_TTL=3600

CDN for Static Assets:

For global deployments, consider using a CDN:

  1. Configure Cloudflare or similar CDN
  2. Point CDN to your Endurain domain
  3. Enable caching for static assets

Monitoring and Observability

Health Check Endpoint:

Monitor your Endurain instance:

Terminal window
curl https://example-app.klutch.sh/api/health

Expected 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:

Terminal window
JAEGER_ENABLED=true
JAEGER_HOST=your-jaeger-host
JAEGER_PORT=6831

This enables distributed tracing for debugging and performance analysis.

Log Monitoring:

Access logs through Klutch.sh dashboard:

  1. Navigate to your app
  2. Click “Logs”
  3. Monitor for errors or warnings
  4. 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:

Terminal window
# Verify PostgreSQL is accessible
curl -v telnet://your-postgres-app.klutch.sh:8000

Verify 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:

Terminal window
APPRISE_URL=mailto://smtp.gmail.com:587/?from=noreply@example.com&to=admin@example.com&user=your-email@gmail.com&pass=your-app-password

OAuth 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:

Terminal window
# Check migration status
docker exec -it your-container-id alembic current
# Run migrations manually
docker exec -it your-container-id alembic upgrade head

Schema Conflicts:

  • Ensure database is empty for fresh install
  • Or ensure existing schema matches expected version
  • Check Endurain version compatibility

Additional Resources

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.