# wger - Workout Manager # https://github.com/wger-project/wger # Version: 2.3 # Domain: workout.dooplex.hu # Auth: Authentik Forward Auth (Proxy) with Remote-User header # # wger supports authentication via headers (X-Remote-User) # when WGER_USE_PROXY_AUTH is enabled # # Authentik Setup: # 1. Create Proxy Provider: # - Name: wger # - External Host: https://workout.dooplex.hu # - Mode: Forward auth (single application) # 2. Configure to send X-Remote-User header # 3. Create Application linked to this provider # 4. Create Outpost (or add to existing) with this provider --- apiVersion: v1 kind: Namespace metadata: name: workout-system labels: app.kubernetes.io/name: wger --- apiVersion: apps/v1 kind: Deployment metadata: name: wger-redis namespace: workout-system labels: app.kubernetes.io/instance: wger app.kubernetes.io/name: wger-redis spec: replicas: 1 selector: matchLabels: app.kubernetes.io/instance: wger app.kubernetes.io/name: wger-redis template: metadata: labels: app.kubernetes.io/instance: wger app.kubernetes.io/name: wger-redis spec: containers: - name: redis image: redis:7.2-alpine ports: - containerPort: 6379 name: redis resources: requests: cpu: 50m memory: 64Mi limits: cpu: 200m memory: 128Mi --- apiVersion: apps/v1 kind: Deployment metadata: name: wger namespace: workout-system labels: app.kubernetes.io/instance: wger app.kubernetes.io/name: wger spec: replicas: 1 selector: matchLabels: app.kubernetes.io/instance: wger app.kubernetes.io/name: wger strategy: type: Recreate template: metadata: labels: app.kubernetes.io/instance: wger app.kubernetes.io/name: wger spec: securityContext: fsGroup: 1000 containers: - name: nginx image: nginx:alpine ports: - containerPort: 80 name: http volumeMounts: - name: static mountPath: /home/wger/static readOnly: true - name: media mountPath: /home/wger/media readOnly: true - name: nginx-config mountPath: /etc/nginx/conf.d/default.conf subPath: nginx.conf - name: wger image: ghcr.io/kisfenyo/wger-oidc:latest imagePullPolicy: Always env: # Django settings - name: SECRET_KEY valueFrom: secretKeyRef: name: wger-app key: secret-key - name: SIGNING_KEY valueFrom: secretKeyRef: name: wger-app key: signing-key - name: DJANGO_DEBUG value: "False" - name: WGER_INSTANCE value: "https://workout.dooplex.hu" - name: TIME_ZONE value: "Europe/Budapest" - name: DJANGO_SETTINGS_MODULE value: "config.settings.production" - name: DJANGO_CACHE_TIMEOUT value: "120" # Database - name: DJANGO_DB_ENGINE value: "django.db.backends.postgresql" - name: DJANGO_DB_HOST value: "postgresql-rw.database-system.svc.cluster.local" - name: DJANGO_DB_PORT value: "5432" - name: DJANGO_DB_DATABASE value: "wger" - name: DJANGO_DB_USER valueFrom: secretKeyRef: name: wger-db key: username - name: DJANGO_DB_PASSWORD valueFrom: secretKeyRef: name: wger-db key: password # Cache - name: DJANGO_CACHE_BACKEND value: "django_redis.cache.RedisCache" - name: DJANGO_CACHE_LOCATION value: "redis://wger-redis:6379/1" - name: DJANGO_CACHE_CLIENT_CLASS value: "django_redis.client.DefaultClient" # Celery - name: CELERY_BROKER value: "redis://wger-redis:6379/2" - name: CELERY_BACKEND value: "redis://wger-redis:6379/2" - name: ENABLE_OIDC value: "True" - name: OIDC_RP_CLIENT_ID value: "AXr6k4P1JcgKKMcvGeXOLwd69MJ1UVjz3fW80mEg" - name: OIDC_RP_CLIENT_SECRET value: "oaj4yWum0skWoAJVf4VvXSSnc4pdaWQbKtyPaMaG6prBN0av1b1w7bna6nUALoIXwSQWu9seFZl66XsYxaFWXVXcWyI6B63rl5saIFCifVg9hqkl6RlhxHL4X4u42pqd" - name: OIDC_RP_SIGN_ALGO value: "RS256" - name: CSRF_TRUSTED_ORIGINS value: "https://workout.dooplex.hu" # Authentik Endpoints (Replace 'authentik.dooplex.hu' with your actual Authentik domain) - name: OIDC_OP_LOGOUT_ENDPOINT value: "https://authentik.dooplex.hu/application/o/workout/end-session/" - name: OIDC_LOGIN_BUTTON_TEXT value: "Login with Authentik" - name: OIDC_ALLOW_CREATE_USER value: "true" - name: OIDC_OP_AUTHORIZATION_ENDPOINT value: "https://authentik.dooplex.hu/application/o/authorize/" - name: OIDC_OP_TOKEN_ENDPOINT value: "https://authentik.dooplex.hu/application/o/token/" - name: OIDC_OP_USER_ENDPOINT value: "https://authentik.dooplex.hu/application/o/userinfo/" - name: OIDC_OP_JWKS_ENDPOINT value: "https://authentik.dooplex.hu/application/o/workout/jwks/" # Email (disabled - no email sending) - name: ENABLE_EMAIL value: "False" # Media settings - name: DJANGO_MEDIA_ROOT value: "/home/wger/media" - name: DJANGO_STATIC_ROOT value: "/home/wger/static" # Features - name: ALLOW_REGISTRATION value: "False" - name: ALLOW_GUEST_USERS value: "False" - name: ALLOW_UPLOAD_VIDEOS value: "True" - name: USE_RECAPTCHA value: "False" - name: DOWNLOAD_EXERCISE_IMAGES_ON_STARTUP value: "True" ports: - containerPort: 8000 name: http resources: requests: cpu: 100m memory: 256Mi limits: cpu: 1000m memory: 1Gi volumeMounts: - name: media mountPath: /home/wger/media - name: static mountPath: /home/wger/static livenessProbe: httpGet: path: / port: http initialDelaySeconds: 120 periodSeconds: 30 readinessProbe: httpGet: path: / port: http initialDelaySeconds: 60 periodSeconds: 10 volumes: - name: nginx-config configMap: name: wger-nginx-config - name: media persistentVolumeClaim: claimName: wger-media - name: static persistentVolumeClaim: claimName: wger-static --- # Celery worker for background tasks apiVersion: apps/v1 kind: Deployment metadata: name: wger-celery-worker namespace: workout-system labels: app.kubernetes.io/instance: wger app.kubernetes.io/name: wger-celery-worker spec: replicas: 1 selector: matchLabels: app.kubernetes.io/instance: wger app.kubernetes.io/name: wger-celery-worker template: metadata: labels: app.kubernetes.io/instance: wger app.kubernetes.io/name: wger-celery-worker spec: securityContext: fsGroup: 1000 containers: - name: celery-worker image: ghcr.io/kisfenyo/wger-oidc:latest imagePullPolicy: Always command: ["/start-worker"] env: - name: SECRET_KEY valueFrom: secretKeyRef: name: wger-app key: secret-key - name: SIGNING_KEY valueFrom: secretKeyRef: name: wger-app key: signing-key - name: DJANGO_DB_ENGINE value: "django.db.backends.postgresql" - name: DJANGO_DB_HOST value: "postgresql-rw.database-system.svc.cluster.local" - name: DJANGO_DB_PORT value: "5432" - name: DJANGO_DB_DATABASE value: "wger" - name: DJANGO_DB_USER valueFrom: secretKeyRef: name: wger-db key: username - name: DJANGO_DB_PASSWORD valueFrom: secretKeyRef: name: wger-db key: password - name: DJANGO_CACHE_TIMEOUT value: "120" - name: DJANGO_CACHE_CLIENT_CLASS value: "django_redis.client.DefaultClient" - name: CELERY_BROKER value: "redis://wger-redis:6379/2" - name: CELERY_BACKEND value: "redis://wger-redis:6379/2" - name: DJANGO_CACHE_BACKEND value: "django_redis.cache.RedisCache" - name: DJANGO_CACHE_LOCATION value: "redis://wger-redis:6379/1" resources: requests: cpu: 50m memory: 128Mi limits: cpu: 500m memory: 512Mi --- # Celery beat for scheduled tasks apiVersion: apps/v1 kind: Deployment metadata: name: wger-celery-beat namespace: workout-system labels: app.kubernetes.io/instance: wger app.kubernetes.io/name: wger-celery-beat spec: replicas: 1 selector: matchLabels: app.kubernetes.io/instance: wger app.kubernetes.io/name: wger-celery-beat template: metadata: labels: app.kubernetes.io/instance: wger app.kubernetes.io/name: wger-celery-beat spec: securityContext: fsGroup: 1000 containers: - name: celery-beat image: ghcr.io/kisfenyo/wger-oidc:latest imagePullPolicy: Always command: ["/start-beat"] env: - name: SECRET_KEY valueFrom: secretKeyRef: name: wger-app key: secret-key - name: SIGNING_KEY valueFrom: secretKeyRef: name: wger-app key: signing-key - name: DJANGO_CACHE_TIMEOUT value: "120" - name: DJANGO_CACHE_CLIENT_CLASS value: "django_redis.client.DefaultClient" - name: DJANGO_DB_ENGINE value: "django.db.backends.postgresql" - name: DJANGO_DB_HOST value: "postgresql-rw.database-system.svc.cluster.local" - name: DJANGO_DB_PORT value: "5432" - name: DJANGO_DB_DATABASE value: "wger" - name: DJANGO_DB_USER valueFrom: secretKeyRef: name: wger-db key: username - name: DJANGO_DB_PASSWORD valueFrom: secretKeyRef: name: wger-db key: password - name: CELERY_BROKER value: "redis://wger-redis:6379/2" - name: CELERY_BACKEND value: "redis://wger-redis:6379/2" resources: requests: cpu: 50m memory: 64Mi limits: cpu: 200m memory: 256Mi --- apiVersion: v1 kind: Service metadata: name: wger-redis namespace: workout-system labels: app.kubernetes.io/instance: wger app.kubernetes.io/name: wger-redis spec: type: ClusterIP ports: - name: redis port: 6379 targetPort: redis selector: app.kubernetes.io/instance: wger app.kubernetes.io/name: wger-redis --- apiVersion: v1 kind: Service metadata: name: wger namespace: workout-system labels: app.kubernetes.io/instance: wger app.kubernetes.io/name: wger spec: type: ClusterIP ports: - name: http port: 80 targetPort: 80 selector: app.kubernetes.io/instance: wger app.kubernetes.io/name: wger --- # Ingress with Authentik forward auth apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: wger namespace: workout-system labels: app.kubernetes.io/instance: wger app.kubernetes.io/name: wger annotations: cert-manager.io/cluster-issuer: letsencrypt-prod external-dns.alpha.kubernetes.io/hostname: workout.dooplex.hu,workout.home nginx.ingress.kubernetes.io/ssl-redirect: "true" nginx.ingress.kubernetes.io/proxy-body-size: "100m" nginx.ingress.kubernetes.io/configuration-snippet: | set $geo_allowed 0; if ($remote_addr ~ "^192\.168\.") { set $geo_allowed 1; } if ($remote_addr ~ "^10\.") { set $geo_allowed 1; } if ($geoip2_country_code = "HU") { set $geo_allowed 1; } if ($geo_allowed = 0) { return 403 "Access restricted to Hungary"; } spec: ingressClassName: nginx-internal rules: - host: workout.dooplex.hu http: paths: - path: / pathType: Prefix backend: service: name: wger port: number: 80 - host: workout.home http: paths: - path: / pathType: Prefix backend: service: name: wger port: number: 80 tls: - hosts: - workout.dooplex.hu secretName: wger-tls --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: wger-media namespace: workout-system labels: app.kubernetes.io/instance: wger app.kubernetes.io/name: wger-media recurring-job-group.longhorn.io/needbackup: enabled recurring-job.longhorn.io/source: enabled spec: accessModes: - ReadWriteOnce storageClassName: longhorn resources: requests: storage: 5Gi --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: wger-static namespace: workout-system labels: app.kubernetes.io/instance: wger app.kubernetes.io/name: wger-static spec: accessModes: - ReadWriteOnce storageClassName: longhorn resources: requests: storage: 2Gi --- apiVersion: v1 kind: ConfigMap metadata: name: wger-nginx-config namespace: workout-system data: nginx.conf: | server { listen 80; server_name _; client_max_body_size 4G; # Official Wger Logic root /var/www/html/; # This is just a dummy root, aliases do the work location /static/ { alias /home/wger/static/; expires 30d; access_log off; } location /media/ { alias /home/wger/media/; expires 30d; access_log off; } location / { proxy_pass http://localhost:8000; proxy_set_header Host $http_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 $http_x_forwarded_proto; } } ---