{"id":48431448,"url":"https://github.com/openops-cloud/helm-chart","last_synced_at":"2026-04-06T11:01:38.277Z","repository":{"id":338390237,"uuid":"1130997779","full_name":"openops-cloud/helm-chart","owner":"openops-cloud","description":null,"archived":false,"fork":false,"pushed_at":"2026-03-30T09:13:31.000Z","size":153,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-30T11:24:10.501Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Go Template","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/openops-cloud.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-01-09T10:16:41.000Z","updated_at":"2026-03-30T09:13:35.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/openops-cloud/helm-chart","commit_stats":null,"previous_names":["openops-cloud/helm-chart"],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/openops-cloud/helm-chart","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/openops-cloud%2Fhelm-chart","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/openops-cloud%2Fhelm-chart/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/openops-cloud%2Fhelm-chart/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/openops-cloud%2Fhelm-chart/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/openops-cloud","download_url":"https://codeload.github.com/openops-cloud/helm-chart/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/openops-cloud%2Fhelm-chart/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31469743,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-06T08:36:52.050Z","status":"ssl_error","status_checked_at":"2026-04-06T08:36:51.267Z","response_time":112,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-04-06T11:01:37.604Z","updated_at":"2026-04-06T11:01:38.263Z","avatar_url":"https://github.com/openops-cloud.png","language":"Go Template","funding_links":[],"categories":[],"sub_categories":[],"readme":"# OpenOps Helm Chart\n\nThis repository contains the Helm chart that deploys the OpenOps application stack (nginx, app server, engine, tables, analytics, Postgres, Redis) onto a Kubernetes cluster.\n\n## Repository layout\n- `chart/Chart.yaml`: Chart metadata for the `openops` release.\n- `chart/values.yaml`: Default configuration values.\n- `chart/values.overrides-example.yaml`: Sample overrides file to copy and customize.\n- `chart/values.ci.yaml`: Resource-constrained overlay for CI environments.\n- `chart/values.dev.yaml`: Development overlay for local development environments.\n- `chart/values.production.yaml`: Production overlay with externalized dependencies and cloud settings.\n- `chart/templates/`: Kubernetes manifests templated by Helm (43 files including deployments, statefulsets, services, configmaps, secrets, external secrets, PVCs, ingress, service accounts, PodDisruptionBudgets, HorizontalPodAutoscalers, NetworkPolicy, LimitRange, ServiceMonitor, Helm tests, and helpers).\n- `chart/.helmignore`: Excludes development and repository files from packaged charts.\n- `LICENSE`: Apache 2.0 license for this Helm chart.\n- `docs/`: Deployment guides for AWS EKS, EKS Fargate, and other platforms.\n\n## Components\n- **nginx**: Reverse proxy and load balancer exposed via `LoadBalancer`.\n- **openops-app**: Main application server.\n- **openops-engine**: Task execution engine.\n- **openops-tables**: Data tables service (Baserow).\n- **openops-analytics**: Analytics dashboard (Superset).\n- **postgres**: PostgreSQL database.\n- **redis**: Redis cache.\n\n## Quick start\n\n### Install from OCI registry (recommended)\n\n```bash\nhelm install openops oci://public.ecr.aws/openops/helm/openops \\\n  --version \u003cVERSION\u003e \\\n  -n openops --create-namespace \\\n  -f values.overrides.yaml\n```\n\n### Install from source\n\n1. Copy the sample overrides file and adjust it to match your environment:\n   ```bash\n   cp chart/values.overrides-example.yaml chart/values.overrides.yaml\n   ```\n2. Install (or upgrade) the chart into your target namespace:\n   ```bash\n   helm upgrade --install openops ./chart -n openops --create-namespace -f chart/values.overrides.yaml\n   ```\n3. Retrieve the external endpoint exposed by the nginx service to access the application:\n   ```bash\n   kubectl get svc nginx -n openops\n   ```\n\n## Secret hardening\n- All sensitive environment keys are rendered through a shared Kubernetes `Secret` so containers never embed credentials in-line.\n- Control how that secret is managed via the `secretEnv` block (disable creation, mark it `immutable`, or attach compliance labels/annotations).\n- When `secretEnv.existingSecret` is set (optionally with `create: false`), the chart references the externally managed secret, which is recommended for SOPS, ExternalSecrets, or Vault-driven workflows.\n- Values added under `secretEnv.stringData` stay in plain text for readability, while entries under `secretEnv.data` are templated and base64-encoded by the chart before being stored.\n- Workloads automatically receive a `checksum/secret-env` pod annotation so any change to the secret triggers a rolling restart.\n\nExample override:\n```yaml\nsecretEnv:\n  create: false\n  existingSecret: openops-env\n  immutable: true\n  annotations:\n    secrets.kubernetes.io/managed-by: external\n```\n\n## Multi-environment deployments\nUse overlays to configure different environments:\n\n**Development (default):**\n```bash\nhelm upgrade --install openops ./chart -n openops-dev \\\n  -f chart/values.yaml \\\n  -f values.overrides.yaml\n```\n\n**CI/Testing:**\n```bash\nhelm upgrade --install openops ./chart -n openops-ci \\\n  -f chart/values.yaml \\\n  -f chart/values.ci.yaml \\\n  -f values.overrides.yaml\n```\n\n**Production (externalized dependencies):**\n```bash\nhelm upgrade --install openops ./chart -n openops-prod \\\n  -f chart/values.yaml \\\n  -f chart/values.production.yaml \\\n  -f values.overrides.yaml\n```\n\n### Sample values overlays\n\nThe chart includes several example overlay files to help you get started:\n\n**`values.overrides-example.yaml`** - Basic configuration template\n- Copy this file to create your own `values.overrides.yaml`\n- Contains examples for secrets, URLs, and resource adjustments\n- Safe defaults for single-node development environments\n- Shows how to enable Ingress with TLS\n\n**`values.ci.yaml`** - CI/testing environment overlay\n- Resource-constrained settings for fast startup\n- Reduced replica counts (all components set to 1)\n- Lower memory/CPU requests and limits\n- Suitable for automated testing in resource-limited CI runners\n- Example usage in GitHub Actions, GitLab CI, Jenkins\n\n**`values.production.yaml`** - Production-ready overlay\n- Demonstrates externalized PostgreSQL and Redis (AWS RDS, ElastiCache, etc.)\n- Increased replica counts for high availability (app: 3, engine: 3, nginx: 2)\n- Production-grade resource allocations (2-4Gi memory per service)\n- Cloud-specific storage classes (gp3, premium-rwo, managed-csi)\n- LoadBalancer annotations for AWS/GCP/Azure\n- Security hardening examples\n\n### Creating custom overlays\n\n**Staging environment example:**\n```yaml\n# values.staging.yaml\nglobal:\n  version: \"1.0.0-rc.1\"\n\nopenopsEnv:\n  OPS_PUBLIC_URL: \"https://staging.openops.example.com\"\n  OPS_ENVIRONMENT_NAME: \"staging\"\n  OPS_LOG_LEVEL: debug\n  OPS_POSTGRES_HOST: \"staging-db.example.com\"\n  OPS_POSTGRES_DATABASE: openops_staging\n\n# Moderate resource allocation\napp:\n  replicas: 2\n  resources:\n    requests:\n      memory: \"1Gi\"\n      cpu: \"500m\"\n    limits:\n      memory: \"2Gi\"\n      cpu: \"1000m\"\n\n# Use external staging database\npostgres:\n  replicas: 0\n```\n\n**Multi-region deployment example:**\n```yaml\n# values.us-east-1.yaml\nopenopsEnv:\n  OPS_PUBLIC_URL: \"https://us-east.openops.example.com\"\n  OPS_POSTGRES_HOST: \"rds-us-east-1.example.com\"\n  OPS_REDIS_URL: \"redis://elasticache-us-east-1.example.com:6379/0\"\n\nnginx:\n  service:\n    annotations:\n      service.beta.kubernetes.io/aws-load-balancer-type: \"nlb\"\n      service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled: \"true\"\n\ntables:\n  storage:\n    storageClass: \"gp3\"\n```\n\n### Overlay precedence and merging\n\nHelm merges values files from left to right, with later files overriding earlier ones:\n\n```bash\nhelm upgrade --install openops ./chart \\\n  -f chart/values.yaml \\           # 1. Base defaults\n  -f chart/values.production.yaml \\ # 2. Production overlay\n  -f values.overrides.yaml \\        # 3. Your custom secrets/config\n  -f values.region.yaml             # 4. Region-specific overrides\n```\n\n**Best practices:**\n- Keep `values.yaml` as the base with sensible defaults\n- Use environment overlays (`values.ci.yaml`, `values.production.yaml`) for environment-specific settings\n- Store secrets in `values.overrides.yaml` or external secret managers\n- Use separate overlay files for region, tenant, or customer-specific configurations\n- Version control overlay files (except secrets) for reproducible deployments\n- Document customizations in comments within overlay files\n\nThe `values.production.yaml` overlay demonstrates:\n- Externalized PostgreSQL (AWS RDS, GCP Cloud SQL, Azure Database)\n- Externalized Redis (AWS ElastiCache, GCP Memorystore, Azure Cache)\n- Cloud-specific storage classes and annotations\n- Production-grade resource allocations and replica counts\n- Security and logging best practices\n\n## Storage\nThe chart uses StatefulSets with volumeClaimTemplates for stateful dependencies:\n- **PostgreSQL**: 20Gi persistent storage (StatefulSet)\n- **Redis**: 5Gi persistent storage (StatefulSet)\n- **Tables**: 10Gi persistent storage (PVC)\n\n### Volume ownership fix for tables\nThe tables component includes an init container that automatically fixes volume ownership to ensure compatibility with non-root security contexts. The init container runs once on pod startup and sets the ownership of `/baserow/data` to `1000:1000` (matching the application's security context). This prevents permission errors when the main container tries to write to the mounted volume.\n\nTo customize or disable this init container, override the `tables.initContainers` value in your configuration.\n\n### StatefulSet benefits\n- **Stable network identities**: Each pod gets a predictable DNS name\n- **Ordered rollouts**: Pods are updated sequentially for safe state transitions\n- **Per-pod storage**: Each replica has its own dedicated PersistentVolumeClaim\n- **Safe scaling**: Controlled pod creation and deletion order\n\n### Storage customization\nCustomize storage classes, sizes, and backup annotations:\n```yaml\npostgres:\n  storage:\n    size: 50Gi\n    storageClass: \"gp3\"\n    annotations:\n      snapshot.storage.kubernetes.io/enabled: \"true\"\n  backup:\n    annotations:\n      backup.velero.io/backup-volumes: data\n```\n\n### Authentication and TLS\nBoth Postgres and Redis support optional authentication and TLS:\n```yaml\npostgres:\n  auth:\n    enabled: true\n    existingSecret: \"postgres-auth\"\n  tls:\n    enabled: true\n    existingSecret: \"postgres-tls\"\n    caFile: true\n\nredis:\n  auth:\n    enabled: true\n    existingSecret: \"redis-auth\"\n  tls:\n    enabled: true\n    existingSecret: \"redis-tls\"\n```\n\n### Update strategies\nStatefulSets support partitioned rollouts for extra safety:\n```yaml\npostgres:\n  updateStrategy:\n    type: RollingUpdate\n    rollingUpdate:\n      partition: 1  # Update pods with ordinal \u003e= partition\n```\n\n## Networking\n- The `nginx` service is exposed as a `LoadBalancer` on port 80 by default.\n- All other services use `ClusterIP` for internal communication.\n- The nginx configuration routes traffic to the appropriate backend services.\n- An optional `Ingress` resource can be enabled for environments using an ingress controller instead of a LoadBalancer.\n\n### TLS/HTTPS configuration\nEnable TLS termination using Kubernetes Ingress with cert-manager or cloud-managed certificates:\n\n**Using Ingress with TLS:**\n```yaml\ningress:\n  enabled: true\n  ingressClassName: nginx\n  annotations:\n    cert-manager.io/cluster-issuer: letsencrypt-prod\n  hosts:\n    - host: openops.example.com\n      paths:\n        - path: /\n          pathType: Prefix\n          serviceName: nginx\n          servicePort: 80\n  tls:\n    - hosts:\n        - openops.example.com\n      secretName: openops-tls  # cert-manager will populate this\n  tlsConfig:\n    enabled: true  # enables HSTS, SSL redirect, and cipher configuration\n    sslProtocols: \"TLSv1.2 TLSv1.3\"\n    hstsMaxAge: \"31536000\"\n    hstsIncludeSubdomains: \"true\"\n    hstsPreload: \"true\"\n```\n\n**Cloud-specific LoadBalancer with SSL:**\nFor AWS NLB with ACM certificate:\n```yaml\nnginx:\n  service:\n    type: LoadBalancer\n    annotations:\n      service.beta.kubernetes.io/aws-load-balancer-type: \"nlb\"\n      service.beta.kubernetes.io/aws-load-balancer-ssl-cert: \"arn:aws:acm:region:account:certificate/cert-id\"\n      service.beta.kubernetes.io/aws-load-balancer-ssl-ports: \"443\"\n      service.beta.kubernetes.io/aws-load-balancer-backend-protocol: \"http\"\n```\n\n**Pre-created TLS secret:**\n```bash\nkubectl create secret tls openops-tls \\\n  --cert=/path/to/tls.crt \\\n  --key=/path/to/tls.key \\\n  -n openops\n```\n\n## Dependencies\nThe deployments include health checks and readiness probes so dependent services wait until their prerequisites are available.\n\n## Topology and rollout safeguards\nThe chart provides built-in safeguards to avoid single-node concentration and ensure safe rolling updates:\n\n### Deployment strategy\nAll deployments use a `RollingUpdate` strategy with configurable parameters (default: `maxSurge: 1`, `maxUnavailable: 0`) to ensure zero-downtime deployments.\n\n### Topology spread constraints\nWhen enabled (default), pods are distributed across nodes to avoid concentration on a single node:\n- **maxSkew**: Maximum difference in pod count between nodes (default: 1)\n- **topologyKey**: Topology domain key (default: `kubernetes.io/hostname`)\n- **whenUnsatisfiable**: Scheduling behavior when constraint cannot be met (default: `ScheduleAnyway`)\n\nDisable topology spread constraints:\n```yaml\nglobal:\n  topologySpreadConstraints:\n    enabled: false\n```\n\n### Pod anti-affinity\nOptional pod anti-affinity rules can be enabled to prefer scheduling pods on different nodes:\n```yaml\nglobal:\n  affinity:\n    enabled: true\n```\n\n### Priority classes\nAssign priority classes to pods for better scheduling control:\n```yaml\nglobal:\n  priorityClassName: \"high-priority\"\n```\n\n### Customizing safeguards\nOverride the defaults in your values file:\n```yaml\nglobal:\n  strategy:\n    type: RollingUpdate\n    rollingUpdate:\n      maxSurge: 2\n      maxUnavailable: 1\n  \n  topologySpreadConstraints:\n    enabled: true\n    maxSkew: 2\n    topologyKey: topology.kubernetes.io/zone\n    whenUnsatisfiable: DoNotSchedule\n  \n  affinity:\n    enabled: true\n  \n  priorityClassName: \"system-cluster-critical\"\n```\n\n## Scaling and resource management\n\n### Horizontal scaling\nScale replicas for individual components based on load:\n\n```yaml\napp:\n  replicas: 3\n  resources:\n    requests:\n      memory: \"2Gi\"\n      cpu: \"1000m\"\n    limits:\n      memory: \"4Gi\"\n      cpu: \"2000m\"\n\nengine:\n  replicas: 3\n\ntables:\n  replicas: 2\n\nanalytics:\n  replicas: 2\n\nnginx:\n  replicas: 2\n```\n\n**Important scaling considerations:**\n- **app** and **engine** are stateless and can be scaled horizontally without restrictions.\n- **tables** uses file-based storage (SQLite for media) and requires `ReadWriteOnce` PVC; limit to 2-3 replicas or migrate to object storage.\n- **analytics** can be scaled but shares session state; consider sticky sessions or external session storage for \u003e2 replicas.\n- **postgres** and **redis** bundled deployments are single-replica; use external managed services for HA.\n\n### Vertical scaling\nAdjust resource requests and limits per workload:\n\n```yaml\napp:\n  resources:\n    requests:\n      memory: \"2Gi\"  # guaranteed resources\n      cpu: \"1000m\"\n    limits:\n      memory: \"4Gi\"  # maximum allowed\n      cpu: \"2000m\"\n```\n\n**Resource tuning guidelines:**\n- **app**: Memory-intensive for large workflows; start with 1-2Gi, scale to 4Gi+ under load.\n- **engine**: CPU-intensive for code execution; allocate 500m-1000m CPU per replica.\n- **tables**: Initial migrations require 1-2Gi memory; steady-state can run on 512Mi-1Gi.\n- **analytics**: Dashboard rendering is memory-heavy; allocate 2Gi+ for production.\n- **postgres**: Size based on dataset; 512Mi-1Gi for dev, 2Gi+ for production.\n- **redis**: Typically light; 256Mi-512Mi sufficient for most workloads.\n\n### Autoscaling\nThe chart includes optional HorizontalPodAutoscaler (HPA) resources for app, engine, analytics, and nginx. Enable them in your values:\n\n```yaml\nhpa:\n  enabled: true\n  app:\n    enabled: true\n    minReplicas: 2\n    maxReplicas: 10\n    targetCPUUtilizationPercentage: 70\n    targetMemoryUtilizationPercentage: 80\n  engine:\n    enabled: true\n    minReplicas: 2\n    maxReplicas: 8\n  analytics:\n    enabled: true\n    minReplicas: 1\n    maxReplicas: 5\n  nginx:\n    enabled: true\n    minReplicas: 2\n    maxReplicas: 6\n```\n\n**Prerequisites:**\n- Kubernetes Metrics Server must be installed in your cluster\n- Resource requests must be defined for accurate scaling decisions\n\n**Note:** When HPA is enabled, the `replicas` field in the deployment is ignored. The HPA manages replica count dynamically based on observed metrics.\n\n## Production hardening\n\n### Security best practices\n\n**1. Secrets management**\nUse external secret managers instead of storing secrets in values files:\n\n```yaml\nsecretEnv:\n  create: false\n  existingSecret: openops-env  # managed by ExternalSecrets, SOPS, or Vault\n  immutable: true\n  annotations:\n    secrets.kubernetes.io/managed-by: external-secrets\n```\n\nCreate secrets using one of these methods:\n\n**External Secrets Operator:**\n```yaml\napiVersion: external-secrets.io/v1beta1\nkind: ExternalSecret\nmetadata:\n  name: openops-env\n  namespace: openops\nspec:\n  secretStoreRef:\n    name: aws-secrets-manager  # or vault, gcpsm, etc.\n    kind: SecretStore\n  target:\n    name: openops-env\n  data:\n    - secretKey: OPS_ENCRYPTION_KEY\n      remoteRef:\n        key: openops/encryption-key\n    - secretKey: OPS_JWT_SECRET\n      remoteRef:\n        key: openops/jwt-secret\n```\n\n**SOPS encryption:**\n```bash\n# Encrypt values file\nsops --encrypt --kms arn:aws:kms:region:account:key/id values.overrides.yaml \u003e values.overrides.enc.yaml\n\n# Deploy with decryption\nhelm secrets upgrade --install openops ./chart -f values.overrides.enc.yaml\n```\n\n**Manual secret creation:**\n```bash\nkubectl create secret generic openops-env -n openops \\\n  --from-literal=OPS_ENCRYPTION_KEY=\"$(openssl rand -hex 16)\" \\\n  --from-literal=OPS_JWT_SECRET=\"$(openssl rand -hex 32)\" \\\n  --from-literal=OPS_POSTGRES_PASSWORD=\"$(openssl rand -base64 32)\" \\\n  --from-literal=OPS_OPENOPS_ADMIN_PASSWORD=\"$(openssl rand -base64 24)\" \\\n  --from-literal=OPS_ANALYTICS_ADMIN_PASSWORD=\"$(openssl rand -base64 24)\" \\\n  --from-literal=ANALYTICS_POWERUSER_PASSWORD=\"$(openssl rand -base64 24)\"\n```\n\n**2. Network policies**\nThe chart includes a comprehensive NetworkPolicy that restricts pod-to-pod communication. Enable it in your values:\n\n```yaml\nnetworkPolicy:\n  enabled: true\n  # Allow ingress from specific namespaces (e.g., monitoring)\n  allowedNamespaces:\n    - monitoring\n    - ingress-nginx\n  # Allow egress to external services\n  allowExternal: true\n  # Custom egress rules\n  extraEgress:\n    - to:\n      - namespaceSelector:\n          matchLabels:\n            name: external-api\n      ports:\n      - protocol: TCP\n        port: 443\n```\n\nThe default policy enforces:\n- Nginx accepts traffic from LoadBalancer/Ingress and routes to app/analytics/tables\n- App, engine, and analytics can access Postgres and Redis\n- All components can query DNS and access external HTTPS endpoints\n- Postgres and Redis only accept connections from authorized components\n- All other traffic is denied by default (zero-trust networking)\n\n**3. Pod Security Standards**\nApply restricted security context:\n\n```yaml\napp:\n  securityContext:\n    runAsNonRoot: true\n    runAsUser: 1000\n    fsGroup: 1000\n    seccompProfile:\n      type: RuntimeDefault\n    capabilities:\n      drop:\n      - ALL\n```\n\n**4. Resource limits and quotas**\nThe chart includes an optional LimitRange to set default resource constraints:\n\n```yaml\nlimitRange:\n  enabled: true\n  limits:\n    - type: Container\n      default:\n        cpu: \"1000m\"\n        memory: \"2Gi\"\n      defaultRequest:\n        cpu: \"100m\"\n        memory: \"128Mi\"\n      max:\n        cpu: \"4000m\"\n        memory: \"8Gi\"\n      min:\n        cpu: \"50m\"\n        memory: \"64Mi\"\n    - type: PersistentVolumeClaim\n      max:\n        storage: \"100Gi\"\n      min:\n        storage: \"1Gi\"\n```\n\nAdditionally, create a ResourceQuota at the namespace level to prevent resource exhaustion:\n\n```yaml\napiVersion: v1\nkind: ResourceQuota\nmetadata:\n  name: openops-quota\n  namespace: openops\nspec:\n  hard:\n    requests.cpu: \"20\"\n    requests.memory: \"40Gi\"\n    limits.cpu: \"40\"\n    limits.memory: \"80Gi\"\n    persistentvolumeclaims: \"5\"\n```\n\n**5. Service accounts and IRSA/Workload Identity**\nEach component has a dedicated service account with configurable annotations:\n\n```yaml\nserviceAccount:\n  app:\n    create: true\n    annotations:\n      eks.amazonaws.com/role-arn: \"arn:aws:iam::ACCOUNT:role/openops-app\"\n  engine:\n    create: true\n    annotations:\n      eks.amazonaws.com/role-arn: \"arn:aws:iam::ACCOUNT:role/openops-engine\"\n  # For GCP Workload Identity\n  analytics:\n    create: true\n    annotations:\n      iam.gke.io/gcp-service-account: \"openops-analytics@PROJECT.iam.gserviceaccount.com\"\n```\n\n**6. Image security**\n- Use specific image tags (not `latest`)\n- Enable image pull secrets for private registries\n- Scan images for vulnerabilities with Trivy or Snyk\n- Use distroless or minimal base images\n\n```yaml\nimage:\n  repository: your-registry.example.com/openops\n  pullPolicy: IfNotPresent\n  pullSecrets:\n    - name: registry-credentials\n\nglobal:\n  version: \"1.0.0\"  # explicit version, not 'latest'\n```\n\n**7. Audit logging**\nEnable Kubernetes audit logs and application logging:\n\n```yaml\nopenopsEnv:\n  OPS_LOG_LEVEL: warn  # reduce noise in production\n  OPS_LOG_PRETTY: \"false\"  # JSON for log aggregation\n  OPS_TELEMETRY_MODE: COLLECTOR\n```\n\n**8. Database security**\n- Use SSL/TLS for database connections\n- Enable encryption at rest for managed databases\n- Rotate database credentials regularly\n- Limit database user permissions to minimum required\n\n**9. Regular updates**\n- Monitor security advisories for dependencies\n- Update Helm chart and application versions regularly\n- Test updates in staging before production deployment\n\n### High availability setup\n\nFor production deployments with zero-downtime requirements:\n\n```yaml\n# Use external managed services for stateful components\npostgres:\n  replicas: 0  # disabled; use AWS RDS/GCP Cloud SQL/Azure Database\n\nredis:\n  replicas: 0  # disabled; use AWS ElastiCache/GCP Memorystore/Azure Cache\n\n# Scale stateless components\napp:\n  replicas: 3\n  resources:\n    requests:\n      memory: \"2Gi\"\n      cpu: \"1000m\"\n\nengine:\n  replicas: 3\n\ntables:\n  replicas: 2\n\nanalytics:\n  replicas: 2\n\nnginx:\n  replicas: 2\n\n# Enable anti-affinity and topology spread\nglobal:\n  affinity:\n    enabled: true\n  topologySpreadConstraints:\n    enabled: true\n    maxSkew: 1\n    topologyKey: topology.kubernetes.io/zone\n    whenUnsatisfiable: DoNotSchedule\n\n# Enable PodDisruptionBudgets\npdb:\n  enabled: true\n  app:\n    enabled: true\n    minAvailable: 2\n  engine:\n    enabled: true\n    minAvailable: 2\n  nginx:\n    enabled: true\n    minAvailable: 1\n  analytics:\n    enabled: true\n    minAvailable: 1\n  tables:\n    enabled: true\n    minAvailable: 1\n```\n\n### Monitoring and observability\n\n**Prometheus metrics:**\nThe chart includes a ServiceMonitor resource for Prometheus Operator integration. Enable it in your values:\n\n```yaml\nserviceMonitor:\n  enabled: true\n  interval: 30s\n  scrapeTimeout: 10s\n  additionalLabels:\n    release: prometheus  # match your Prometheus Operator's serviceMonitorSelector\n  honorLabels: true\n```\n\nThe ServiceMonitor automatically discovers and scrapes metrics from:\n- `openops-app` on `/metrics`\n- `openops-engine` on `/metrics`\n- `openops-analytics` on `/metrics`\n- `postgres` on `:9187/metrics` (if postgres-exporter sidecar is enabled)\n- `redis` on `:9121/metrics` (if redis-exporter sidecar is enabled)\n\n**Liveness and readiness probes:**\nAll components include default health checks. Customize if needed:\n\n```yaml\napp:\n  livenessProbe:\n    httpGet:\n      path: /health\n      port: 8080\n    initialDelaySeconds: 30\n    periodSeconds: 10\n  readinessProbe:\n    httpGet:\n      path: /ready\n      port: 8080\n    initialDelaySeconds: 10\n    periodSeconds: 5\n```\n\n**Helm tests:**\nValidate your deployment with built-in Helm tests:\n\n```bash\nhelm test openops -n openops\n```\n\nThe test suite verifies:\n- Nginx service is reachable\n- App service responds to health checks\n- Database connectivity (if in-cluster Postgres is enabled)\n- Redis connectivity (if in-cluster Redis is enabled)\n\n### Backup and disaster recovery\n\n**Database backups:**\n```bash\n# PostgreSQL backup using pg_dump\nkubectl exec -n openops postgres-0 -- pg_dumpall -U postgres | gzip \u003e backup-$(date +%Y%m%d).sql.gz\n\n# Restore from backup\ngunzip \u003c backup-20260116.sql.gz | kubectl exec -i -n openops postgres-0 -- psql -U postgres\n```\n\nFor managed databases, use cloud-native backup solutions:\n- AWS RDS: Automated backups and snapshots\n- GCP Cloud SQL: Automated backups and point-in-time recovery\n- Azure Database: Automated backups with geo-redundancy\n\n**PVC snapshots:**\n```yaml\napiVersion: snapshot.storage.k8s.io/v1\nkind: VolumeSnapshot\nmetadata:\n  name: tables-snapshot\n  namespace: openops\nspec:\n  volumeSnapshotClassName: csi-snapclass\n  source:\n    persistentVolumeClaimName: tables-pvc\n```\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fopenops-cloud%2Fhelm-chart","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fopenops-cloud%2Fhelm-chart","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fopenops-cloud%2Fhelm-chart/lists"}