Skip to content

Statistics for Strava

Self-hosted fitness dashboard for Statistics for Strava. Pulls your complete activity history from the Strava API using OAuth and presents it as a rich, personal dashboard with heatmaps, pace charts, gear tracking, and year-over-year comparisons — all stored locally in SQLite, with no external database required.

Personal dashboard — single athlete

Statistics for Strava is designed for a single Strava athlete account. All three Strava credentials (clientId, clientSecret, refreshToken) are required before the application will start syncing.

Key Features

  • Complete activity history — imports all past and future Strava activities automatically
  • Rich visualizations — heatmaps, pace/HR charts, segment leaderboards, gear tracking
  • SQLite storage — embedded database, no external database dependency
  • OAuth integration — syncs with a single Strava athlete account via OAuth2
  • Persistent storage — PVC-backed data directory for SQLite database and cached files
  • Ingress support — TLS via cert-manager with configurable ingress class

Strava OAuth Setup

Before deploying, you need a Strava API application and a refresh token. Follow these steps:

1. Create a Strava API Application

  1. Go to strava.com/settings/api
  2. Fill in the application name, category, and website
  3. Set Authorization Callback Domain to your deployment hostname (e.g. strava.example.com)
  4. Note your Client ID and Client Secret

2. Obtain a Refresh Token

Replace <YOUR_CLIENT_ID> and authorize in your browser:

https://www.strava.com/oauth/authorize?client_id=<YOUR_CLIENT_ID>&response_type=code&redirect_uri=http://localhost/callback&scope=read,activity:read_all

After authorization, exchange the code for tokens:

curl -X POST https://www.strava.com/api/v3/oauth/token \
  -d client_id=<YOUR_CLIENT_ID> \
  -d client_secret=<YOUR_CLIENT_SECRET> \
  -d code=<AUTHORIZATION_CODE> \
  -d grant_type=authorization_code

The response contains refresh_token — use this value for strava.refreshToken.

Installation

HTTPS repository:

helm repo add helmforge https://repo.helmforge.dev
helm repo update
helm install strava-statistics helmforge/strava-statistics

OCI registry:

helm install strava-statistics oci://ghcr.io/helmforgedev/helm/strava-statistics

Deployment Examples

# values.yaml — Statistics for Strava with inline credentials
strava:
  clientId: '12345'
  clientSecret: 'your-strava-client-secret'
  refreshToken: 'your-strava-refresh-token'
  timezone: America/Sao_Paulo

persistence:
  enabled: true
  size: 5Gi

ingress:
  enabled: true
  ingressClassName: traefik
  hosts:
    - host: strava.example.com
      paths:
        - path: /
          pathType: Prefix
# values.yaml — Strava credentials stored in a Kubernetes Secret
# kubectl create secret generic strava-credentials \
#   --from-literal=client-id=12345 \
#   --from-literal=client-secret=your-secret \
#   --from-literal=refresh-token=your-token
strava:
  timezone: America/Sao_Paulo
  existingSecret: strava-credentials
  existingSecretClientIdKey: client-id
  existingSecretClientSecretKey: client-secret
  existingSecretRefreshTokenKey: refresh-token

persistence:
  enabled: true
  size: 5Gi

ingress:
  enabled: true
  ingressClassName: traefik
  hosts:
    - host: strava.example.com
      paths:
        - path: /
          pathType: Prefix
# values.yaml — Statistics for Strava with TLS via cert-manager
strava:
  clientId: '12345'
  clientSecret: 'your-strava-client-secret'
  refreshToken: 'your-strava-refresh-token'
  timezone: America/Sao_Paulo

persistence:
  enabled: true
  size: 5Gi

ingress:
  enabled: true
  ingressClassName: traefik
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
  hosts:
    - host: strava.example.com
      paths:
        - path: /
          pathType: Prefix
  tls:
    - secretName: strava-statistics-tls
      hosts:
        - strava.example.com

Configuration Reference

Core

ParameterTypeDefaultDescription
nameOverridestring""Override the chart name.
fullnameOverridestring""Override the full release name.
commonLabelsobject{}Extra labels added to all resources.

Image

ParameterTypeDefaultDescription
image.repositorystringdocker.io/robiningelbrecht/strava-statisticsContainer image.
image.tagstring"v4.7.10"Image tag.
image.pullPolicystringIfNotPresentImage pull policy.
imagePullSecretsarray[]Pull secrets for private registries.

Strava Configuration

ParameterTypeDefaultDescription
strava.portinteger8080Internal HTTP port for the container.
strava.clientIdstring""Strava OAuth application Client ID.
strava.clientSecretstring""Strava OAuth application Client Secret.
strava.refreshTokenstring""Strava OAuth Refresh Token for the athlete account.
strava.existingSecretstring""Existing Kubernetes Secret containing Strava credentials.
strava.existingSecretClientIdKeystringclient-idKey in the existing secret for the Client ID.
strava.existingSecretClientSecretKeystringclient-secretKey in the existing secret for the Client Secret.
strava.existingSecretRefreshTokenKeystringrefresh-tokenKey in the existing secret for the Refresh Token.
strava.timezonestringUTCTimezone for activity time display (e.g. America/Sao_Paulo).
strava.extraEnvarray[]Extra environment variables injected into the container.
All three Strava credentials are required

The application will not start without strava.clientId, strava.clientSecret, and strava.refreshToken. Do not deploy without completing the Strava OAuth Setup steps above.

Store credentials in a Kubernetes Secret

Avoid setting clientId, clientSecret, and refreshToken as plain-text values in your values file if it is committed to a repository. Use strava.existingSecret to reference a pre-created Kubernetes Secret instead.

Set your local timezone

Activity start times are displayed in the configured timezone. Set strava.timezone to your local timezone (e.g. America/Sao_Paulo, Europe/London, Asia/Tokyo) so workout times match your local clock.

Persistence

ParameterTypeDefaultDescription
persistence.enabledbooleantrueEnable a PVC for /data (SQLite database and cached files).
persistence.sizestring2GiPVC size.
persistence.storageClassstring""StorageClass for the PVC.
persistence.accessModesarray["ReadWriteOnce"]PVC access modes.
persistence.existingClaimstring""Use an existing PVC instead of creating one.
SQLite data lives on the PVC — back it up separately

Unlike other charts, there is no built-in S3 backup CronJob. The SQLite database and all downloaded activity data are stored in the PVC. Use your storage provider’s snapshot functionality or a Velero-based backup policy to protect this data.

Service

ParameterTypeDefaultDescription
service.typestringClusterIPKubernetes service type.
service.portinteger80Service port exposed to the cluster.
service.annotationsobject{}Annotations for the Service.
service.ipFamilyPolicystringomittedService IP family policy.
service.ipFamiliesarrayomittedOrdered Service IP families.

Ingress

ParameterTypeDefaultDescription
ingress.enabledbooleanfalseEnable an Ingress resource.
ingress.ingressClassNamestringtraefikIngress class name.
ingress.annotationsobject{}Annotations for the Ingress (e.g. cert-manager).
ingress.hostsarray[]Ingress host and path rules.
ingress.tlsarray[]TLS configuration (secret name and hosts).

Probes

ParameterTypeDefaultDescription
probes.startup.enabledbooleantrueEnable startup probe.
probes.startup.initialDelaySecondsinteger10Startup probe initial delay.
probes.startup.periodSecondsinteger5Startup probe period.
probes.startup.timeoutSecondsinteger3Startup probe timeout.
probes.startup.failureThresholdinteger30Startup probe failure threshold.
probes.liveness.enabledbooleantrueEnable liveness probe.
probes.liveness.initialDelaySecondsinteger0Liveness probe initial delay.
probes.liveness.periodSecondsinteger15Liveness probe period.
probes.liveness.timeoutSecondsinteger5Liveness probe timeout.
probes.liveness.failureThresholdinteger3Liveness probe failure threshold.
probes.readiness.enabledbooleantrueEnable readiness probe.
probes.readiness.initialDelaySecondsinteger0Readiness probe initial delay.
probes.readiness.periodSecondsinteger10Readiness probe period.
probes.readiness.timeoutSecondsinteger5Readiness probe timeout.
probes.readiness.failureThresholdinteger3Readiness probe failure threshold.

Resources and Security

ParameterTypeDefaultDescription
resourcesobject{}CPU and memory requests and limits.
podSecurityContextobject{}Pod-level security context.
securityContextobject{}Container-level security context.

Service Account

ParameterTypeDefaultDescription
serviceAccount.createbooleanfalseCreate a dedicated ServiceAccount.
serviceAccount.namestring""Override the ServiceAccount name.
serviceAccount.annotationsobject{}Annotations for the ServiceAccount.

Scheduling

ParameterTypeDefaultDescription
nodeSelectorobject{}Node selector for scheduling.
tolerationsarray[]Tolerations for scheduling.
affinityobject{}Affinity rules.
topologySpreadConstraintsarray[]Topology spread constraints.
priorityClassNamestring""PriorityClass for the pod.
terminationGracePeriodSecondsinteger30Termination grace period.
podLabelsobject{}Extra labels for the pod.
podAnnotationsobject{}Extra annotations for the pod.

Extra

ParameterTypeDefaultDescription
extraVolumesarray[]Extra volumes to attach to the pod.
extraVolumeMountsarray[]Extra volume mounts for the container.
extraManifestsarray[]Extra Kubernetes manifests deployed alongside the chart.

Gateway API

ParameterTypeDefaultDescription
gatewayApi.enabledbooleanfalseEnable HTTPRoute resources.
gatewayApi.httpRoutesarray[]HTTPRoute definitions to render.

External Secrets

ParameterTypeDefaultDescription
externalSecrets.enabledbooleanfalseEnable ExternalSecret resources.
externalSecrets.itemsarray[]ExternalSecret definitions to render.

Common Issues

Dashboard empty — invalid or expired refresh token

If the dashboard shows no activities after deployment, verify your Strava refresh token is valid. Refresh tokens from the authorization flow are long-lived but can be revoked from the Strava application settings. Re-run the OAuth flow to obtain a new token and update your values.

Initial sync takes time for large histories

On first deployment, the application imports your entire Strava activity history. For athletes with hundreds or thousands of activities, this can take several minutes. The dashboard will be empty or partial until the sync completes.

More Information