Installation
This guide shows how to install SRExpert on your Kubernetes cluster using Helm.
Prerequisites
Before starting, make sure you have:
- Kubernetes cluster running (v1.25+)
kubectlconfigured and connected to the clusterhelminstalled (v3.x)- Namespace created for SRExpert
- StorageClass configured for Persistent Volumes
1. Add Helm Repository
helm repo add srexpert-helm https://nexus.srexpert.io/repository/srexpert-helm/
helm repo update2. Create Namespace
kubectl create namespace srexpert3. Configure Values
Backend (values-backend.yaml)
Create a values-backend.yaml file with backend settings:
# Default values for srexpert-backend (Production)
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
replicaCount: 1
image:
repository: registry.srexpert.io/srexpert/backend
pullPolicy: Always
# Overrides the image tag whose default is the chart appVersion.
tag: "latest"
# Nexus Registry credentials (optional - creates secret automatically)
# Set create: true and provide credentials to auto-create the imagePullSecret
imageCredentials:
create: false
name: nexus-registry
registry: registry.srexpert.io
# imagePullSecrets - uncomment if using private registry
# imagePullSecrets:
# - name: nexus-registry
imagePullSecrets: []
nameOverride: ""
fullnameOverride: ""
serviceAccount:
# Specifies whether a service account should be created
create: true
# Annotations to add to the service account
annotations: {}
# The name of the service account to use.
name: "srexpert-backend"
podSecurityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 2000
seccompProfile:
type: RuntimeDefault
securityContext:
allowPrivilegeEscalation: false
runAsNonRoot: true
runAsUser: 1000
capabilities:
drop:
- ALL
seccompProfile:
type: RuntimeDefault
service:
type: ClusterIP
port: 8000
targetPort: 8000
ingress:
enabled: false
className: "nginx"
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
cert-manager.io/cluster-issuer: "letsencrypt-prod"
nginx.ingress.kubernetes.io/proxy-body-size: "100m"
nginx.ingress.kubernetes.io/proxy-connect-timeout: "600"
nginx.ingress.kubernetes.io/proxy-send-timeout: "600"
nginx.ingress.kubernetes.io/proxy-read-timeout: "600"
nginx.ingress.kubernetes.io/enable-cors: "true"
nginx.ingress.kubernetes.io/cors-allow-methods: "GET, POST, PUT, DELETE, OPTIONS"
nginx.ingress.kubernetes.io/cors-allow-headers: "DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization"
nginx.ingress.kubernetes.io/proxy-http-version: "1.1"
nginx.ingress.kubernetes.io/websocket-services: "srexpert-backend"
# Real client IP forwarding (SD-145)
nginx.ingress.kubernetes.io/use-forwarded-headers: "true"
nginx.ingress.kubernetes.io/compute-full-forwarded-for: "true"
nginx.ingress.kubernetes.io/forwarded-for-header: "X-Forwarded-For"
hosts:
- host: srexpert-api.yourdomain.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: srexpert-backend-tls
hosts:
- srexpert-api.yourdomain.com
resources:
limits:
cpu: 1000m
memory: 3Gi
requests:
cpu: 500m
memory: 2Gi
livenessProbe:
httpGet:
path: /api/v1/health
port: 8000
initialDelaySeconds: 60
periodSeconds: 30
failureThreshold: 5
readinessProbe:
httpGet:
path: /api/v1/health
port: 8000
initialDelaySeconds: 10
periodSeconds: 10
failureThreshold: 3
volumes:
helmCache:
enabled: true
# Environment variables
env:
environment: "production"
logLevel: "error"
database:
# When postgresql.enabled=true, use: srexpert-backend-postgresql (subchart service)
# When using external DB, use: postgresql.dbs.svc.cluster.local
host: "srexpert-backend-postgresql"
port: "5432"
name: "srexpert"
redis:
# When customRedis.enabled=true, use: srexpert-backend-redis (custom deployment)
# When redis.enabled=true (Bitnami), use: srexpert-backend-redis-master
url: "redis://srexpert-backend-redis:6379"
encryptionKey: "SOME_KEY"
nextPublicApiBaseUrl: "https://srexpert-frontend.srexpert-dev.svc.cluster.local/api/v1"
accessTokenExpireMinutes: "480"
backofficeApiUrl: "https://backoffice-api.srexpert.io" #Not change this endpoint
frontendUrl: "https://srexpert.yourdomain.com" # IMPORTANT: Must match your actual frontend domain
# Cookie Security Settings
# Set cookieSecure to "false" for HTTP-only environments (NodePort, local dev without TLS)
# Set cookieSecure to "true" for HTTPS environments (Ingress with TLS)
cookieSecure: "false" # Default false for local/NodePort - change to "true" for production with HTTPS
cookieSamesite: "lax" # Options: "strict", "lax", "none"
helm:
cacheHome: "/tmp/.helm/cache"
configHome: "/tmp/.helm/config"
dataHome: "/tmp/.helm/data"
# Pod Management Configuration
podManagement:
maxIdleTimeHours: "8"
cleanupIntervalMinutes: "30"
terminationGracePeriod: "120"
# IA Terminal Configuration
iaTerminal:
namespace: "srexpert"
image: "registry.srexpert.io/srexpert/ia-terminal:latest"
serviceAccount: "srexpert-backend"
backendServiceUrl: "http://srexpert-backend.srexpert.svc.cluster.local:8000"
allowTlsInsecure: "false"
allowedOrigins: '["https://srexpert.yourdomain.com","https://app.yourdomain.com"]'
wsRateLimit: "20/min"
wsIdleTimeout: "300"
sseHeartbeatInterval: "20"
# Pod Resource Configuration
resources:
requests:
memory: "1Gi"
cpu: "500m"
ephemeralStorage: "2Gi"
limits:
memory: "4Gi"
cpu: "2"
ephemeralStorage: "10Gi"
# Redis Advanced Configuration
redisConfig:
db: "0"
sessionEnabled: "true"
cacheEnabled: "true"
pubsubEnabled: "true"
# Cache TTL Settings (seconds)
cacheTtl:
short: "300"
medium: "1800"
long: "3600"
metrics: "60"
workloads: "120"
# Security - API Key Encryption (should be different from encryptionKey)
apiKeyEncryptionSecret: "CHANGE-IN-PRODUCTION-32-CHARS-MIN-KEY-HERE"
# Secrets configuration
secrets:
database:
name: srexpert-database
postgresUser: postgres
postgresPassword: postgres123
redisPassword: srexpert-redis
rabbitmqPassword: srexpert-rabbitmq
jwt:
name: srexpert-jwt
secretKey: "srexpert-jwt-secret-key-change-in-production-please-32-chars-min"
integrations:
name: srexpert-integrations
slackWebhookUrl: ""
teamsWebhookUrl: ""
emailSmtpPassword: ""
# RBAC configuration
rbac:
create: true
clusterAdmin: true
# Init DB Job configuration
initDb:
enabled: true
image:
repository: registry.srexpert.io/srexpert/backend
tag: "latest"
resources:
requests:
memory: "512Mi"
cpu: "500m"
limits:
memory: "1Gi"
cpu: "1"
autoscaling:
enabled: true
minReplicas: 2
maxReplicas: 5
targetCPUUtilizationPercentage: 80
# targetMemoryUtilizationPercentage: 80
# PostgreSQL subchart configuration (Bitnami)
postgresql:
enabled: true # Set to true to deploy PostgreSQL with this chart
image:
registry: registry.srexpert.io
repository: dependencies/postgresql
tag: "17.0.0"
auth:
username: postgres
password: postgres123
database: srexpert
enablePostgresUser: true
postgresPassword: postgres123
primary:
persistence:
enabled: true
size: 8Gi
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
# Custom Redis configuration (standalone deployment)
# Use this instead of Bitnami subchart for simpler setups
customRedis:
enabled: true # Set to true to deploy a simple Redis alongside the backend
image: registry.srexpert.io/dependencies/redis:7.2-alpine
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "200m"
# Redis subchart configuration (Bitnami) - disabled, using customRedis
redis:
enabled: false
auth:
password: srexpert-redis
master:
persistence:
enabled: true
size: 2Gi
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "200m"
nodeSelector: {}
tolerations: []
affinity: {}Critical Configuration: frontendUrl
The frontendUrl field in values-backend.yaml must match the exact domain where your SRExpert frontend is accessible (e.g., https://srexpert.yourdomain.com).
This URL is used by the backend to generate links in emails (password recovery, invitations, notifications) and in the payment flow (Stripe redirect URLs). If misconfigured:
- Password recovery emails will contain broken links
- Payment checkout will fail to redirect back to your application
- Team invitation links will not work
Make sure it uses https:// and does not include a trailing slash.
Frontend (values-frontend.yaml)
Create a values-frontend.yaml file:
# Default values for srexpert-frontend
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
replicaCount: 1
image:
repository: registry.srexpert.io/srexpert/frontend
pullPolicy: Always
tag: "latest"
# Docker registry credentials for pulling images
imageCredentials:
create: false
nameOverride: ""
fullnameOverride: ""
serviceAccount:
create: true
annotations: {}
name: "srexpert-frontend"
podAnnotations:
kubectl.kubernetes.io/restartedAt: "2025-09-22T19:30:00Z"
podSecurityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 2000
seccompProfile:
type: RuntimeDefault
securityContext:
allowPrivilegeEscalation: false
runAsNonRoot: true
runAsUser: 1001
capabilities:
drop:
- ALL
seccompProfile:
type: RuntimeDefault
service:
type: ClusterIP
port: 3000
targetPort: 3000
ingress:
enabled: true
className: "nginx"
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
cert-manager.io/cluster-issuer: "letsencrypt-prod"
nginx.ingress.kubernetes.io/proxy-body-size: "100m"
nginx.ingress.kubernetes.io/proxy-connect-timeout: "600"
nginx.ingress.kubernetes.io/proxy-send-timeout: "600"
nginx.ingress.kubernetes.io/proxy-read-timeout: "600"
hosts:
- host: srexpert.yourdomain.com
paths:
- path: /api/v1/
pathType: Prefix
backend: srexpert-backend
backendPort: 8000
- path: /
pathType: Prefix
tls:
- secretName: srexpert-frontend-tls
hosts:
- srexpert.yourdomain.com
resources:
limits:
cpu: 1000m
memory: 3Gi
requests:
cpu: 500m
memory: 2Gi
livenessProbe:
httpGet:
path: /api/health
port: 3000
initialDelaySeconds: 60
periodSeconds: 30
timeoutSeconds: 10
failureThreshold: 5
readinessProbe:
httpGet:
path: /api/health
port: 3000
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 5
startupProbe:
httpGet:
path: /api/health
port: 3000
initialDelaySeconds: 0
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 30
# Environment variables
env:
nodeEnv: "production"
backendUrl: "http://srexpert-backend:8000"
nextPublicApiBaseUrl: "/api/v1"
nextPublicBackendUrl: ""
nextTelemetryDisabled: "1"
# Backoffice API URL for registration/payment flow (runtime configurable)
backofficeApiUrl: "https://backoffice-api.srexpert.io"
# Cookie security (secure flag) is auto-detected based on request protocol (HTTP vs HTTPS)
# No manual configuration needed - works automatically in any environment
autoscaling:
enabled: false
minReplicas: 3
maxReplicas: 10
targetCPUUtilizationPercentage: 80
nodeSelector: {}
tolerations: []
affinity: {}4. Install Backend
helm upgrade --install srexpert-backend srexpert-helm/srexpert-backend \
--namespace srexpert \
-f values-backend.yamlWait for PostgreSQL and Backend to be ready:
kubectl get pods -n srexpert -w5. Install Frontend
helm upgrade --install srexpert-frontend srexpert-helm/srexpert-frontend \
--namespace srexpert \
-f values-frontend.yaml6. Verify Installation
# Check pods
kubectl get pods -n srexpert
# Check services
kubectl get svc -n srexpert
# Check ingress
kubectl get ingress -n srexpert
# View backend logs
kubectl logs -n srexpert -l app.kubernetes.io/name=srexpert-backend --tail=100Persistent Volumes
SRExpert uses Persistent Volumes for:
| Component | Usage | Recommended Size |
|---|---|---|
| PostgreSQL | Database data | 10Gi+ |
| Redis | Cache (optional) | 2Gi |
| Helm Cache | Chart cache | 5Gi |
Check PVCs
kubectl get pvc -n srexpertManual PVC Example (if needed)
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: data-srexpert-backend-postgresql-0
namespace: srexpert
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
storageClassName: defaultTroubleshooting
Pod not starting
# View events
kubectl describe pod -n srexpert <pod-name>
# View logs
kubectl logs -n srexpert <pod-name> --previousImagePull Error
Check if registry secret was created:
kubectl get secret nexus-registry -n srexpertDatabase not connecting
Check if PostgreSQL is running:
kubectl get pods -n srexpert -l app.kubernetes.io/name=postgresqlNext Steps
After installation, continue to: