How to Set Up Self-Hosted OpenWork on Kubernetes (GKE, EKS, AKS): A Complete Helm Guide

Deploy OpenWork on any major cloud Kubernetes platform using the official Helm chart in packaging/helm/openwork-ee, which bundles the web application, PostgreSQL, Redis, and optional inference services.

OpenWork's enterprise-ready deployment is packaged as a Helm chart located at packaging/helm/openwork-ee in the different-ai/openwork repository. This chart enables self-hosted OpenWork on Google Kubernetes Engine (GKE), Amazon Elastic Kubernetes Service (EKS), and Azure Kubernetes Service (AKS) without vendor-specific modifications. The deployment architecture uses standard Kubernetes primitives—Deployments, Services, Ingress resources, and ConfigMaps—making it portable across CNCF-compatible clusters.

This guide walks through complete setup, from cluster preparation to production hardening, with working configurations for each major cloud provider.


Prerequisites for Kubernetes Deployment

Before installing OpenWork, ensure your environment meets these baseline requirements across all three platforms.

Component Purpose Verification Command
kubectl v1.25+ Cluster communication kubectl version --client
Helm 3.12+ Chart installation helm version
Cluster access Authentication to target cluster Provider-specific (see below)
Domain name (optional) TLS and external access DNS A record pointing to ingress IP
Storage class PostgreSQL and Redis persistence kubectl get storageclass

Provider-Specific Configuration

GKE

gcloud container clusters get-credentials CLUSTER_NAME --region=REGION --project=PROJECT_ID

Default storage class: standard (GCE Persistent Disk)

EKS

aws eks update-kubeconfig --region REGION --name CLUSTER_NAME

Default storage class: gp2 or gp3 (EBS CSI driver)

AKS

az aks get-credentials --resource-group RG_NAME --name CLUSTER_NAME

Default storage class: managed-csi or managed-premium


Understanding the OpenWork Helm Chart Structure

The chart in packaging/helm/openwork-ee defines a multi-service architecture. Familiarity with these files enables effective customization.

Core Chart Files

File Path Purpose
Chart.yaml Chart metadata, dependencies (PostgreSQL, Redis subcharts)
values.yaml Default configuration values
templates/deployment.yaml Main application Deployment with web and inference containers
templates/ingress.yaml Ingress resource for external HTTP/HTTPS access
templates/service.yaml ClusterIP Services for internal communication
templates/configmap.yaml Non-sensitive environment configuration

Services Deployed

Service Container Image Default Port Description
web-app ghcr.io/different-ai/openwork-ee 3000 Next.js UI and API server
inference (optional) ghcr.io/different-ai/openwork-inference 8000 Model proxy for local LLMs
postgres postgres:15 5432 Relational data store
redis redis:7 6379 Session cache and job queue

Step 1: Clone and Inspect the Helm Chart

Retrieve the latest chart directly from the repository:

git clone https://github.com/different-ai/openwork.git
cd openwork/packaging/helm/openwork-ee

Review the default configuration:

cat values.yaml

Key defaults to note:

  • replicaCount: 1 (single instance, not HA)
  • ingress.enabled: false (no external access)
  • persistence.size: 10Gi (PVC for PostgreSQL)

Step 2: Create Cloud-Specific values.yaml

GKE Configuration

Create gke-values.yaml:

replicaCount: 2

image:
  repository: ghcr.io/different-ai/openwork-ee
  tag: "latest"
  pullPolicy: IfNotPresent

service:
  type: ClusterIP

ingress:
  enabled: true
  className: "gce"  # GKE Ingress controller

  annotations:
    kubernetes.io/ingress.global-static-ip-name: "openwork-ip"
    networking.gke.io/managed-certificates: "openwork-cert"
    kubernetes.io/ingress.allow-http: "false"
  hosts:
    - host: openwork.example.com
      paths:
        - path: /*
          pathType: ImplementationSpecific

postgres:
  enabled: true
  auth:
    postgresPassword: "CHANGE_ME_IN_SECRET"
    database: "openwork"
  persistence:
    enabled: true
    storageClass: "premium-rwo"  # SSD-backed GKE storage

    size: 20Gi

redis:
  enabled: true
  auth:
    password: "CHANGE_ME_IN_SECRET"

env:
  OPENWORK_JWT_SECRET: ""  # Set via external secret

  OPENWORK_APP_URL: "https://openwork.example.com"

GKE-Specific Notes:

  • The gce ingress class creates a Google Load Balancer with automatic health checks
  • managed-certificates reference a pre-created ManagedCertificate resource
  • Use premium-rwo for production PostgreSQL workloads

EKS Configuration

Create eks-values.yaml:

replicaCount: 2

image:
  repository: ghcr.io/different-ai/openwork-ee
  tag: "latest"

service:
  type: ClusterIP

ingress:
  enabled: true
  className: "alb"  # AWS Load Balancer Controller

  annotations:
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/target-type: ip
    alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":443}]'
    alb.ingress.kubernetes.io/certificate-arn: "arn:aws:acm:REGION:ACCOUNT:certificate/CERT_ID"
  hosts:
    - host: openwork.example.com
      paths:
        - path: /
          pathType: Prefix

postgres:
  enabled: true
  persistence:
    storageClass: "gp3"  # EBS GP3 volumes

    size: 20Gi

redis:
  enabled: true

env:
  OPENWORK_JWT_SECRET: ""
  OPENWORK_APP_URL: "https://openwork.example.com"

EKS-Specific Notes:

  • Requires AWS Load Balancer Controller installed in cluster
  • target-type: ip enables direct pod routing without NodePort
  • Certificate ARN references ACM certificate in same region

AKS Configuration

Create aks-values.yaml:

replicaCount: 2

image:
  repository: ghcr.io/different-ai/openwork-ee
  tag: "latest"

service:
  type: ClusterIP

ingress:
  enabled: true
  className: "azure-application-gateway"  # AGIC

  annotations:
    appgw.ingress.kubernetes.io/use-private-ip: "false"
    kubernetes.io/ingress.class: "azure/application-gateway"
  hosts:
    - host: openwork.example.com
      paths:
        - path: /
          pathType: Prefix
  tls:
    - secretName: openwork-tls
      hosts:
        - openwork.example.com

postgres:
  enabled: true
  persistence:
    storageClass: "managed-csi-premium"
    size: 20Gi

redis:
  enabled: true

env:
  OPENWORK_JWT_SECRET: ""
  OPENWORK_APP_URL: "https://openwork.example.com"

AKS-Specific Notes:

  • Application Gateway Ingress Controller (AGIC) or NGINX ingress supported
  • TLS secrets must be manually created or use cert-manager with Let's Encrypt

Step 3: Secure Secrets Management

Never store credentials in values.yaml. Instead, create Kubernetes Secrets:


# Generate cryptographically secure values

JWT_SECRET=$(openssl rand -base64 32)
POSTGRES_PASSWORD=$(openssl rand -base64 24)
REDIS_PASSWORD=$(openssl rand -base64 24)

# Create namespace

kubectl create namespace openwork

# Create secret

kubectl -n openwork create secret generic openwork-secrets \
  --from-literal=OPENWORK_JWT_SECRET="$JWT_SECRET" \
  --from-literal=POSTGRES_PASSWORD="$POSTGRES_PASSWORD" \
  --from-literal=REDIS_PASSWORD="$REDIS_PASSWORD"

Reference this secret in your values file:

envExistingSecret: "openwork-secrets"

postgres:
  auth:
    existingSecret: "openwork-secrets"
    secretKeys:
      postgresPassword: "POSTGRES_PASSWORD"

redis:
  auth:
    existingSecret: "openwork-secrets"
    secretKeys:
      password: "REDIS_PASSWORD"

Step 4: Install the Helm Chart

GKE Installation

helm dependency update  # Fetch PostgreSQL and Redis subcharts

helm install openwork . \
  -f gke-values.yaml \
  --namespace openwork \
  --set envExistingSecret=openwork-secrets

EKS Installation

helm dependency update

helm install openwork . \
  -f eks-values.yaml \
  --namespace openwork \
  --set envExistingSecret=openwork-secrets

AKS Installation

helm dependency update

helm install openwork . \
  -f aks-values.yaml \
  --namespace openwork \
  --set envExistingSecret=openwork-secrets

Step 5: Verify and Access

Check deployment status:

kubectl -n openwork get pods
kubectl -n openwork logs -l app.kubernetes.io/name=openwork-ee --tail=100

# Expected output: "ready started server on 0.0.0.0:3000, url: http://localhost:3000"

Retrieve external access information:

GKE

kubectl -n openwork get ingress openwork-ee

# Look for IP under "ADDRESS" (takes 5-10 minutes to provision)

EKS

kubectl -n openwork get ingress openwork-ee

# ALB DNS name appears under "ADDRESS"

AKS

kubectl -n openwork get ingress openwork-ee

# Application Gateway IP or custom domain

Step 6: External Database Configuration (Production)

For production workloads, replace the bundled PostgreSQL with a managed service:

postgres:
  enabled: false

env:
  DATABASE_URL: "postgresql://openwork:${PASSWORD}@HOST.REGION.rds.amazonaws.com:5432/openwork"
  # GKE: Cloud SQL via proxy or private IP

  # AKS: Azure Database for PostgreSQL flexible server

Update with helm upgrade:

helm upgrade openwork . -f eks-values.yaml --namespace openwork

Step 7: Enable Optional Inference Service

The inference container enables local LLM integration. Expose it via LoadBalancer for external model access:

inference:
  enabled: true
  service:
    type: LoadBalancer
    port: 8000
    annotations:
      # GKE: Reserve static IP

      networking.gke.io/load-balancer-type: "External"

After deployment, retrieve the endpoint:

kubectl -n openwork get svc openwork-ee-inference

Upgrading and Scaling

Horizontal scaling:

helm upgrade openwork . \
  -f gke-values.yaml \
  --namespace openwork \
  --set replicaCount=4

Version upgrade:

helm upgrade openwork . \
  -f gke-values.yaml \
  --namespace openwork \
  --set image.tag="v0.15.0"

Uninstall:

helm uninstall openwork --namespace openwork
kubectl delete namespace openwork

Summary

  • Self-hosted OpenWork on Kubernetes deploys via the official Helm chart at packaging/helm/openwork-ee
  • GKE, EKS, and AKS require only ingress class and annotation changes—core manifests remain identical
  • Secrets management uses Kubernetes Secrets referenced through envExistingSecret and existingSecret fields
  • Production hardening replaces bundled PostgreSQL with managed databases and enables multiple replicas
  • Inference service deploys as optional sidecar container exposed via LoadBalancer or ClusterIP

Frequently Asked Questions

What Kubernetes versions does OpenWork support?

OpenWork's Helm chart targets Kubernetes 1.25+ and uses the networking.k8s.io/v1 Ingress API. The chart is tested against GKE 1.28+, EKS 1.27+, and AKS 1.28+. Older versions may require API version overrides in templates/ingress.yaml.

Can I use an external Redis instance instead of the bundled one?

Yes. Set redis.enabled: false and provide the connection string via environment variables: REDIS_HOST, REDIS_PORT, and REDIS_PASSWORD in your custom values or secret.

How do I configure TLS certificates for the ingress?

Three approaches are supported: GKE ManagedCertificates (referenced by annotation), AWS ACM (certificate ARN in ALB annotation), and Kubernetes TLS secrets (standard spec.tls blocks). cert-manager with Let's Encrypt works on all platforms when the NGINX ingress class is used instead of cloud-native controllers.

Where are the container images hosted?

Official images publish to ghcr.io/different-ai/openwork-ee and ghcr.io/different-ai/openwork-inference. The scripts/docker-build.sh file in the repository root contains the build logic used to create these images.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →