--- # JARR Dev Environment # Collaborative digital jar web application # # Prerequisites: # 1. Replace placeholders in secrets below # 2. Build and push the image: gitea.dooplex.hu/admin/jarr:dev # # Generate JWT secret: # node -e "console.log(require('crypto').randomBytes(32).toString('hex'))" --- apiVersion: v1 kind: Namespace metadata: name: jarrs-system labels: app.kubernetes.io/name: jarr --- # Database credentials apiVersion: v1 kind: Secret metadata: name: dev-jarr-db namespace: jarrs-system labels: app.kubernetes.io/name: jarr app.kubernetes.io/instance: dev-jarr app.kubernetes.io/component: postgres type: Opaque stringData: username: jarr_dev password: vz0RNgW5uhSXkByGlStp438U --- # Application secrets (JWT, Resend) apiVersion: v1 kind: Secret metadata: name: dev-jarr-app namespace: jarrs-system labels: app.kubernetes.io/name: jarr app.kubernetes.io/instance: dev-jarr app.kubernetes.io/component: app type: Opaque stringData: jwt-access-secret: ddaf79386b6614dbd2ea3f4a56b74dd785a54a30e7c0f739f86e935c12a5ab7c resend-api-key: re_jpGHKsKL_HurdXBv57wjNnhPvcfdzH7NX --- # PostgreSQL PVC apiVersion: v1 kind: PersistentVolumeClaim metadata: name: dev-jarr-postgres namespace: jarrs-system labels: app.kubernetes.io/name: jarr app.kubernetes.io/instance: dev-jarr app.kubernetes.io/component: postgres spec: accessModes: - ReadWriteOnce storageClassName: longhorn resources: requests: storage: 5Gi --- # Redis PVC apiVersion: v1 kind: PersistentVolumeClaim metadata: name: dev-jarr-redis namespace: jarrs-system labels: app.kubernetes.io/name: jarr app.kubernetes.io/instance: dev-jarr app.kubernetes.io/component: redis spec: accessModes: - ReadWriteOnce storageClassName: longhorn resources: requests: storage: 1Gi --- # PostgreSQL 16 apiVersion: apps/v1 kind: Deployment metadata: name: dev-jarr-postgres namespace: jarrs-system labels: app.kubernetes.io/name: jarr app.kubernetes.io/instance: dev-jarr app.kubernetes.io/component: postgres spec: replicas: 1 strategy: type: Recreate selector: matchLabels: app.kubernetes.io/instance: dev-jarr app.kubernetes.io/component: postgres template: metadata: labels: app.kubernetes.io/name: jarr app.kubernetes.io/instance: dev-jarr app.kubernetes.io/component: postgres spec: securityContext: fsGroup: 999 containers: - name: postgres image: postgres:16-alpine imagePullPolicy: IfNotPresent env: - name: POSTGRES_USER valueFrom: secretKeyRef: name: dev-jarr-db key: username - name: POSTGRES_PASSWORD valueFrom: secretKeyRef: name: dev-jarr-db key: password - name: POSTGRES_DB value: jarr_dev - name: PGDATA value: /var/lib/postgresql/data/pgdata ports: - containerPort: 5432 name: postgres protocol: TCP volumeMounts: - name: data mountPath: /var/lib/postgresql/data subPath: data resources: requests: cpu: 100m memory: 256Mi limits: cpu: "1" memory: 1Gi livenessProbe: exec: command: - pg_isready - -U - jarr_dev initialDelaySeconds: 30 periodSeconds: 10 failureThreshold: 5 timeoutSeconds: 5 readinessProbe: exec: command: - pg_isready - -U - jarr_dev initialDelaySeconds: 5 periodSeconds: 5 failureThreshold: 3 timeoutSeconds: 5 volumes: - name: data persistentVolumeClaim: claimName: dev-jarr-postgres --- apiVersion: v1 kind: Service metadata: name: dev-jarr-postgres namespace: jarrs-system labels: app.kubernetes.io/name: jarr app.kubernetes.io/instance: dev-jarr app.kubernetes.io/component: postgres spec: type: ClusterIP ports: - port: 5432 targetPort: postgres protocol: TCP name: postgres selector: app.kubernetes.io/instance: dev-jarr app.kubernetes.io/component: postgres --- # Redis 7 apiVersion: apps/v1 kind: Deployment metadata: name: dev-jarr-redis namespace: jarrs-system labels: app.kubernetes.io/name: jarr app.kubernetes.io/instance: dev-jarr app.kubernetes.io/component: redis spec: replicas: 1 strategy: type: Recreate selector: matchLabels: app.kubernetes.io/instance: dev-jarr app.kubernetes.io/component: redis template: metadata: labels: app.kubernetes.io/name: jarr app.kubernetes.io/instance: dev-jarr app.kubernetes.io/component: redis spec: containers: - name: redis image: redis:7-alpine imagePullPolicy: IfNotPresent args: - redis-server - --appendonly - "yes" - --maxmemory - "64mb" - --maxmemory-policy - "allkeys-lru" ports: - containerPort: 6379 name: redis protocol: TCP volumeMounts: - name: data mountPath: /data resources: requests: cpu: 50m memory: 64Mi limits: cpu: 200m memory: 256Mi livenessProbe: exec: command: - redis-cli - ping initialDelaySeconds: 10 periodSeconds: 10 failureThreshold: 3 timeoutSeconds: 5 readinessProbe: exec: command: - redis-cli - ping initialDelaySeconds: 5 periodSeconds: 5 failureThreshold: 3 timeoutSeconds: 5 volumes: - name: data persistentVolumeClaim: claimName: dev-jarr-redis --- apiVersion: v1 kind: Service metadata: name: dev-jarr-redis namespace: jarrs-system labels: app.kubernetes.io/name: jarr app.kubernetes.io/instance: dev-jarr app.kubernetes.io/component: redis spec: type: ClusterIP ports: - port: 6379 targetPort: redis protocol: TCP name: redis selector: app.kubernetes.io/instance: dev-jarr app.kubernetes.io/component: redis --- # JARR Application apiVersion: apps/v1 kind: Deployment metadata: name: dev-jarr namespace: jarrs-system labels: app.kubernetes.io/name: jarr app.kubernetes.io/instance: dev-jarr app.kubernetes.io/component: app spec: replicas: 1 strategy: type: Recreate selector: matchLabels: app.kubernetes.io/instance: dev-jarr app.kubernetes.io/component: app template: metadata: labels: app.kubernetes.io/name: jarr app.kubernetes.io/instance: dev-jarr app.kubernetes.io/component: app spec: initContainers: - name: wait-for-db image: busybox:1.36 command: - sh - -c - | echo "Waiting for PostgreSQL..." until nc -z dev-jarr-postgres 5432; do echo "PostgreSQL not ready, waiting..." sleep 2 done echo "PostgreSQL is ready!" - name: wait-for-redis image: busybox:1.36 command: - sh - -c - | echo "Waiting for Redis..." until nc -z dev-jarr-redis 6379; do echo "Redis not ready, waiting..." sleep 2 done echo "Redis is ready!" containers: - name: jarr image: gitea.dooplex.hu/admin/jarr:latest imagePullPolicy: Always env: - name: NODE_ENV value: development - name: PORT value: "3000" - name: BASE_URL value: "https://dev.jarrs.eu" - name: WEB_URL value: "https://dev.jarrs.eu" # Database - name: DB_USER valueFrom: secretKeyRef: name: dev-jarr-db key: username - name: DB_PASS valueFrom: secretKeyRef: name: dev-jarr-db key: password - name: DATABASE_URL value: "postgresql://$(DB_USER):$(DB_PASS)@dev-jarr-postgres:5432/jarr_dev" # Redis - name: REDIS_URL value: "redis://dev-jarr-redis:6379" # JWT - name: JWT_ACCESS_SECRET valueFrom: secretKeyRef: name: dev-jarr-app key: jwt-access-secret - name: JWT_ACCESS_EXPIRES_IN value: "15m" - name: JWT_REFRESH_EXPIRES_IN value: "30d" # Email (Resend) - name: RESEND_API_KEY valueFrom: secretKeyRef: name: dev-jarr-app key: resend-api-key - name: EMAIL_FROM value: "noreply@jarrs.eu" # Rate limiting - name: RATE_LIMIT_ENABLED value: "false" ports: - containerPort: 3000 name: http protocol: TCP resources: requests: cpu: 100m memory: 256Mi limits: cpu: "1" memory: 1Gi startupProbe: httpGet: path: /v1/health port: http periodSeconds: 10 failureThreshold: 30 timeoutSeconds: 5 readinessProbe: httpGet: path: /v1/health port: http initialDelaySeconds: 10 periodSeconds: 10 failureThreshold: 3 timeoutSeconds: 5 livenessProbe: httpGet: path: /v1/health port: http initialDelaySeconds: 30 periodSeconds: 30 failureThreshold: 5 timeoutSeconds: 5 --- apiVersion: v1 kind: Service metadata: name: dev-jarr namespace: jarrs-system labels: app.kubernetes.io/name: jarr app.kubernetes.io/instance: dev-jarr app.kubernetes.io/component: app spec: type: ClusterIP ports: - port: 3000 targetPort: http protocol: TCP name: http selector: app.kubernetes.io/instance: dev-jarr app.kubernetes.io/component: app --- # Ingress apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: dev-jarr namespace: jarrs-system labels: app.kubernetes.io/name: jarr app.kubernetes.io/instance: dev-jarr app.kubernetes.io/component: app annotations: cert-manager.io/cluster-issuer: letsencrypt-prod external-dns.alpha.kubernetes.io/hostname: dev.jarrs.eu nginx.ingress.kubernetes.io/ssl-redirect: "true" spec: ingressClassName: nginx-internal rules: - host: dev.jarrs.eu http: paths: - path: / pathType: Prefix backend: service: name: dev-jarr port: number: 3000 tls: - hosts: - dev.jarrs.eu secretName: dev-jarr-tls