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
gceingress class creates a Google Load Balancer with automatic health checks managed-certificatesreference a pre-createdManagedCertificateresource- Use
premium-rwofor 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: ipenables 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
envExistingSecretandexistingSecretfields - 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →