Skip to content

Dreeve (Statistics for Strava)

Dreeve, formerly Statistics for Strava, is a self-hosted fitness dashboard for FIT, TCX, and GPX files or activities imported through the Strava API. The chart deploys the official image with a web container and the mandatory background daemon sharing one SQLite-backed persistent volume.

Upgrade security requirement

Dreeve v5.6.2 includes the upstream fix for the October 2026 critical PHP remote-code-execution vulnerability. Earlier versions should be upgraded. The former robiningelbrecht/strava-statistics:v5.0.0 tag now serves only a rename notice and is no longer the application image.

Key Features

  • Official docker.io/robiningelbrecht/dreeve:v5.6.2 image
  • Web and daemon containers co-located for safe RWO SQLite access
  • Shared persistent database, file, build-cache, gear-maintenance, and watch paths
  • File-import and Strava API modes
  • Chart-managed, existing, or External Secrets for runtime and OAuth credentials
  • Non-root UID/GID 82, read-only root filesystems, RuntimeDefault seccomp, and disabled token automounting
  • Optional Ingress, Gateway API, NetworkPolicy, PodDisruptionBudget, and dual-stack Service controls
  • Probes based on the upstream FrankenPHP metrics endpoint and migrated database

Architecture

User -> Ingress or HTTPRoute -> Service -> Dreeve web container
                                             |
                                   shared RWO PVC (SQLite/data)
                                             |
                                      Dreeve daemon

The Deployment is intentionally one replica with Recreate. SQLite is single-writer, and the mandatory daemon must share the same filesystem and node attachment as the web process.

Installation

HTTPS repository:

helm repo add helmforge https://repo.helmforge.dev
helm repo update
helm install dreeve helmforge/strava-statistics -f values.yaml

OCI registry:

helm install dreeve oci://ghcr.io/helmforgedev/helm/strava-statistics -f values.yaml

Prepare the Admin Password

ADMIN_PASSWORD_HASH must contain a bcrypt hash, not a plaintext password. Generate it with the same image version:

docker run --rm -it --entrypoint bin/console robiningelbrecht/dreeve:v5.6.2 security:hash-password

Helm values preserve $ characters as written; do not apply Docker Compose’s double-dollar escaping.

When dreeve.appSecret is empty, the chart generates a 64-character value and reuses the value stored in the live Secret during upgrades. Set dreeve.appSecret or dreeve.existingSecret explicitly for reproducible GitOps and offline rendering.

Deployment Examples

dreeve:
  appUrl: https://dreeve.example.com
  appSecret: replace-with-a-long-random-value
  adminUsername: admin
  adminPasswordHash: '$2y$13$replace-with-the-generated-bcrypt-hash'
  importMode: stravaApi

strava:
  clientId: '12345'
  clientSecret: replace-me
  refreshToken: replace-me
  timezone: America/Sao_Paulo

persistence:
  size: 10Gi

ingress:
  enabled: true
  ingressClassName: traefik
  hosts:
    - host: dreeve.example.com
      paths:
        - path: /
          pathType: Prefix
  tls:
    - secretName: dreeve-tls
      hosts:
        - dreeve.example.com
dreeve:
  appUrl: https://dreeve.example.com
  appSecret: replace-with-a-long-random-value
  adminPasswordHash: '$2y$13$replace-with-the-generated-bcrypt-hash'
  importMode: files

strava:
  clientId: ''
  clientSecret: ''
  refreshToken: ''

persistence:
  size: 10Gi

Both containers mount the file watch directory at /var/www/watch.

dreeve:
  appUrl: https://dreeve.example.com
  existingSecret: dreeve-runtime
  existingSecretAppSecretKey: app-secret
  existingSecretAdminPasswordHashKey: admin-password-hash
  existingSecretApiKeyKey: api-key

strava:
  existingSecret: strava-credentials
  existingSecretClientIdKey: client-id
  existingSecretClientSecretKey: client-secret
  existingSecretRefreshTokenKey: refresh-token

The api-key key is optional; without it the upload API remains closed.

externalSecrets:
  enabled: true
  items:
    - name: runtime
      storeRef:
        name: platform-secrets
        kind: ClusterSecretStore
      targetName: dreeve-runtime
      data:
        - secretKey: app-secret
          remoteRef:
            key: dreeve/runtime
            property: app-secret
        - secretKey: admin-password-hash
          remoteRef:
            key: dreeve/runtime
            property: admin-password-hash
    - name: strava
      storeRef:
        name: platform-secrets
        kind: ClusterSecretStore
      targetName: strava-credentials
      data:
        - secretKey: client-id
          remoteRef:
            key: dreeve/strava
            property: client-id
        - secretKey: client-secret
          remoteRef:
            key: dreeve/strava
            property: client-secret
        - secretKey: refresh-token
          remoteRef:
            key: dreeve/strava
            property: refresh-token

dreeve:
  existingSecret: dreeve-runtime

strava:
  existingSecret: strava-credentials
dreeve:
  appUrl: https://dreeve.example.com
  existingSecret: dreeve-runtime

strava:
  existingSecret: strava-credentials

gatewayAPI:
  enabled: true
  httpRoutes:
    - name: web
      parentRefs:
        - name: public-gateway
          namespace: gateway-system
      hostnames:
        - dreeve.example.com

Configuration Reference

Image and Runtime

Parameter Type Default Description
image.repository string docker.io/robiningelbrecht/dreeve Official Dreeve image.
image.tag string v5.6.2 Pinned release with the critical PHP fix.
image.pullPolicy string IfNotPresent Kubernetes image pull policy.
dreeve.appUrl string http://localhost:8080 Public root URL.
dreeve.importMode string stravaApi stravaApi or files.
dreeve.appSecret string "" Session-signing secret; generated and preserved when empty.
dreeve.adminUsername string admin Admin panel username.
dreeve.adminPasswordHash string "" Bcrypt admin password hash.
dreeve.apiKey string "" Optional activity-upload API key.
dreeve.existingSecret string "" Existing Secret for Dreeve runtime secrets.
daemon.enabled boolean true Run the mandatory Dreeve background daemon.
Replace the validation credentials

The mock-* defaults allow deterministic chart rendering and runtime tests. They are not safe production credentials. Set unique values or reference existing Secrets before exposing Dreeve.

Strava API

Parameter Type Default Description
strava.clientId string mock-client-id Validation-only OAuth client ID.
strava.clientSecret string mock-client-secret Validation-only OAuth client secret.
strava.refreshToken string mock-refresh-token Validation-only refresh token.
strava.existingSecret string "" Existing Secret containing OAuth credentials.
strava.timezone string UTC Runtime timezone.
strava.config string legacy YAML One-time v4 migration input.
strava.extraEnv array [] Additional environment entries for both containers.

Strava credentials are required only when dreeve.importMode=stravaApi.

Persistence

Parameter Type Default Description
persistence.enabled boolean true Create or mount persistent shared storage.
persistence.size string 2Gi PVC request.
persistence.storageClass string "" StorageClass; empty uses the cluster default.
persistence.accessModes array [ReadWriteOnce] PVC access modes.
persistence.existingClaim string "" Existing PVC name.
persistence.bootstrap.enabled boolean true Prepare Dreeve directories and legacy database links.

The shared volume stores the SQLite database, application files, build cache, gear-maintenance assets, and watched imports. Snapshot or back up the PVC before every upgrade.

Resources and Security

Parameter Type Default Description
resources.requests.cpu string 100m Web CPU request.
resources.requests.memory string 256Mi Web memory request.
resources.limits.cpu string 2 Web CPU limit.
resources.limits.memory string 5Gi Web memory limit.
daemon.resources.requests.cpu string 100m Daemon CPU request.
daemon.resources.requests.memory string 256Mi Daemon memory request.
daemon.resources.limits.cpu string 2 Daemon CPU limit.
daemon.resources.limits.memory string 5Gi Daemon memory limit.
serviceAccount.automountServiceAccountToken boolean false Mount a Kubernetes API token.
networkPolicy.enabled boolean false Render supplied ingress and egress rules.
podDisruptionBudget.enabled boolean false Protect the single pod from voluntary disruption.

The web container adds only NET_BIND_SERVICE, required by the upstream FrankenPHP binary capability. The daemon drops all capabilities. Both run as UID/GID 82 with read-only root filesystems and bounded writable volumes.

Exposure

Parameter Type Default Description
service.port integer 80 Service port targeting named port http.
service.ipFamilyPolicy string omitted Optional single/dual-stack policy.
service.ipFamilies array omitted Ordered IP families.
ingress.enabled boolean false Render Ingress.
gatewayAPI.enabled boolean false Render HTTPRoutes.
externalSecrets.enabled boolean false Render ExternalSecrets.

NetworkPolicy

When enabled, empty rule lists deny that traffic direction. Declare DNS plus every Strava, map, notification, or AI destination used by the installation:

networkPolicy:
  enabled: true
  ingress:
    - ports:
        - protocol: TCP
          port: 8080
  egress:
    - {}

Replace unrestricted egress with cluster-specific destinations where the CNI supports them.

Upgrading from v4

Dreeve v5 moves YAML settings into SQLite and adds the mandatory daemon. Keep strava.config during the first successful v5 startup so supported settings can migrate, then verify them in the admin panel. Images and gear purchase prices are not migrated upstream. Back up the PVC and read the official v4-to-v5 guide.

Troubleshooting

Daemon waits for schema

The web container owns database migrations; the daemon waits for them. Inspect both logs:

kubectl logs deploy/<release>-strava-statistics -c strava-statistics
kubectl logs deploy/<release>-strava-statistics -c daemon

Pod reports permission errors

The PVC must be writable by UID/GID 82. Confirm that the CSI driver honors fsGroup and repair ownership out of band for existing volumes rather than disabling non-root execution.

Import is OOMKilled

Large histories can consume substantial memory. Increase the web and daemon memory limits together. The chart configures PHP for 4G and defaults each container limit to 5Gi.

Admin login fails

Verify the stored value is a bcrypt hash and that dreeve.appUrl exactly matches the browser’s public root URL. Helm values use literal $ characters; Compose escaping is not required.

More Information