How to Deploy different-ai/openwork in a Production Environment: Complete Self-Hosting Guide

Deploy different-ai/openwork in production using the official Helm chart on Kubernetes, with support for private-network, air-gapped, and single-org topologies.

OpenWork is an open-source AI workflow platform that combines a desktop client with a self-hosted backend. A production deployment requires orchestrating multiple services—Den Web (UI), Den Controller (API), MySQL-compatible database, and optional inference service—behind a unified origin. According to the source code, the recommended path for production workloads is the official Helm chart documented in packaging/helm/openwork-ee/README.md, with supplementary guides for specialized network configurations.

Production Deployment Architectures

Before installing, select the topology that matches your security and network constraints. The repository defines three supported patterns in packages/docs/start-here/self-host.mdx:

Architecture Use Case Key Documentation
Standard self-hosted Single Kubernetes cluster with external database access packages/docs/start-here/self-host.mdx
Private-network deployment Den runs inside a VPC/VPN; desktop connects through VPN-reachable origin packages/docs/start-here/private-network-deployment.mdx
Air-gapped deployment Zero outbound internet; all artifacts mirrored internally packages/docs/start-here/air-gapped-deployment.mdx

Most enterprise deployments follow the private-network pattern—allowing laptops internet access while keeping the control plane inside corporate infrastructure.

Prerequisites for Production

A production-ready openwork deployment requires:

  • Kubernetes cluster (1.24+) with ingress controller
  • MySQL 8.0+ or PlanetScale for persistent state
  • SMTP relay for email-based authentication flows
  • TLS certificates (via cert-manager or external)
  • Container registry access to ghcr.io/different-ai/charts/openwork-ee

The Helm chart (packaging/helm/openwork-ee/README.md) packages the Den Web frontend, Den Controller API, optional inference service, and database migration jobs into coordinated deployments.

Step-by-Step Helm Deployment

1. Create Namespace and Prepare Secrets

kubectl create namespace openwork

Generate cryptographically secure keys for authentication and database encryption:

export BETTER_AUTH_SECRET=$(openssl rand -base64 32)
export DB_ENCRYPTION_KEY=$(openssl rand -base64 32)

2. Configure values.prod.yaml

The values.prod.yaml file drives all runtime configuration. Per the chart README, these fields are required for single-organization mode:

image:
  tag: "v0.18.0"  # Pin to specific chart version

config:
  tenancy:
    mode: "single_org"
    singleOrgName: "YourOrg"
    singleOrgSlug: "yourorg"
    ownerEmails: "admin@yourorg.com"
    allowPublicSignup: "false"
  public:
    webOrigin: "https://openwork.yourorg.internal"
    apiOrigin: "https://api.openwork.yourorg.internal"
    mcpResourceUrl: "https://api.yourorg.internal/mcp"
    desktopDenBaseUrl: "https://openwork.yourorg.internal"
    corsOrigins: "https://openwork.yourorg.internal,https://api.yourorg.internal"
    betterAuthTrustedOrigins: "https://openwork.yourorg.internal"
    bootstrapAdminEmails: "admin@yourorg.com"

secret:
  values:
    databaseUrl: "mysql://openwork:${DB_PASSWORD}@mysql.internal:3306/openwork_den?sslmode=verify-full"
    betterAuthSecret: "${BETTER_AUTH_SECRET}"
    denDbEncryptionKey: "${DB_ENCRYPTION_KEY}"
    emailFrom: "OpenWork <no-reply@yourorg.internal>"
    smtpHost: "smtp.yourorg.internal"
    smtpPort: "587"
    smtpUser: "openwork@yourorg.internal"
    smtpPass: "${SMTP_PASSWORD}"

Critical security note: Never commit values.prod.yaml to version control. Inject secrets via CI/CD environment variables or external secret operators.

3. Handle Custom Certificate Authorities

For corporate environments with internal PKI, mount your root CA into the pods. Create the secret:

kubectl create secret generic openwork-custom-ca \
  --namespace openwork \
  --from-file=ca.crt=./corp-root-ca.pem

Then reference it in your values file (customCa.enabled: true, customCa.existingSecret: openwork-custom-ca).

4. Install the Helm Chart

helm upgrade --install openwork-ee \
  oci://ghcr.io/different-ai/charts/openwork-ee \
  --namespace openwork \
  --create-namespace \
  --version v0.18.0 \
  -f values.prod.yaml

For private registries, authenticate first and configure imagePullSecrets as documented in packaging/helm/openwork-ee/README.md.

5. Verify Deployment Health

kubectl get pods -n openwork
kubectl rollout status deployment/openwork-ee-den-api -n openwork
kubectl rollout status deployment/openwork-ee-den-web -n openwork

The chart exposes /health and /ready endpoints automatically. Test locally via port-forward:

kubectl port-forward svc/openwork-ee-den-web 3005:3005 -n openwork
curl -s http://127.0.0.1:3005/api/health

6. Configure Desktop Client Connectivity

OpenWork Desktop connects via install-link flow. Set the Connect URL to your configured webOrigin (https://openwork.yourorg.internal). The client auto-derives API endpoints at /api/den/....

For split-origin deployments (separate web and API hosts), additionally configure DEN_API_PUBLIC_URL as described in packages/docs/start-here/private-network-deployment.mdx.

7. Run Database Migrations

The Helm chart includes a pre-install/upgrade hook Job that executes migrations automatically. To run manually (e.g., after failed upgrade):

migrations:
  enabled: true
  hook: false

Apply the chart, then create the Job manually with kubectl.

Production Hardening Checklist

Layer Implementation Source Reference
Database External managed MySQL with SSL verification; encryption key in denDbEncryptionKey packaging/helm/openwork-ee/README.md
Authentication betterAuthSecret 32+ characters; allowPublicSignup: "false" for single-org values.prod.yaml example
Network Private VPC with VPN ingress; or air-gapped with mirrored registries packages/docs/start-here/private-network-deployment.mdx
Observability OpenTelemetry or Sentry via observability block Chart README
Custom CA openwork-custom-ca secret mounted to all pods customCa values structure

CI/CD Deployment Script

Automate your openwork production deployment with this bash template:

#!/usr/bin/env bash
set -euo pipefail

NS=openwork
CHART=oci://ghcr.io/different-ai/charts/openwork-ee
VERSION="${OPENWORK_VERSION:-v0.18.0}"

# Generate secrets if not provided

BETTER_AUTH_SECRET="${BETTER_AUTH_SECRET:-$(openssl rand -base64 32)}"
DB_ENCRYPTION_KEY="${DB_ENCRYPTION_KEY:-$(openssl rand -base64 32)}"

cat >values.prod.yaml <<EOF
image:
  tag: "${VERSION}"
config:
  tenancy:
    mode: "single_org"
    singleOrgName: "${ORG_NAME}"
    singleOrgSlug: "${ORG_SLUG}"
    ownerEmails: "${ADMIN_EMAIL}"
    allowPublicSignup: "false"
  public:
    webOrigin: "https://openwork.${DOMAIN}"
    apiOrigin: "https://api.openwork.${DOMAIN}"
    mcpResourceUrl: "https://api.openwork.${DOMAIN}/mcp"
    desktopDenBaseUrl: "https://openwork.${DOMAIN}"
    corsOrigins: "https://openwork.${DOMAIN},https://api.openwork.${DOMAIN}"
    betterAuthTrustedOrigins: "https://openwork.${DOMAIN}"
    bootstrapAdminEmails: "${ADMIN_EMAIL}"
secret:
  values:
    databaseUrl: "mysql://openwork:${DB_PASSWORD}@${DB_HOST}:3306/openwork_den?sslmode=verify-full"
    betterAuthSecret: "${BETTER_AUTH_SECRET}"
    denDbEncryptionKey: "${DB_ENCRYPTION_KEY}"
    emailFrom: "OpenWork <no-reply@${DOMAIN}>"
    smtpHost: "${SMTP_HOST}"
    smtpPort: "${SMTP_PORT:-587}"
    smtpUser: "${SMTP_USER}"
    smtpPass: "${SMTP_PASSWORD}"
EOF

kubectl create namespace "$NS" 2>/dev/null || true

helm upgrade --install openwork-ee "$CHART" \
  --namespace "$NS" \
  --version "$VERSION" \
  -f values.prod.yaml

kubectl rollout status deployment/openwork-ee-den-api -n "$NS" --timeout=300s
kubectl rollout status deployment/openwork-ee-den-web -n "$NS" --timeout=300s

echo "OpenWork v${VERSION} deployed. Verify at https://openwork.${DOMAIN}"

Troubleshooting Production Issues

Symptom Diagnostic Resolution
Desktop cannot connect Check webOrigin/apiOrigin DNS resolution and TLS packages/docs/start-here/network-diagnostics.mdx
Database connection failures Verify sslmode and custom CA mounting Review customCa configuration
Migration Job stuck Check kubectl logs job/openwork-ee-migrations Re-run with hook: false
Install-link validation fails Regenerate keypair: scripts/generate-connect-link-keypair.mjs Update CONNECT_LINK_PRIVATE_KEY secret

The desktop client's Settings → Debug panel surfaces connection diagnostics for end-user troubleshooting.

Summary

  • Deploy different-ai/openwork in production using the official Helm chart (oci://ghcr.io/different-ai/charts/openwork-ee) with pinned versions and external database
  • Choose your topology: standard self-hosted, private-network (VPC/VPN), or air-gapped (fully isolated)
  • Secure with generated secrets: betterAuthSecret and denDbEncryptionKey must be 32+ random characters
  • Configure origins carefully: webOrigin, apiOrigin, and desktopDenBaseUrl must be mutually reachable by desktop clients
  • Reference source documentation: packages/docs/start-here/self-host.mdx for architecture, packaging/helm/openwork-ee/README.md for Helm specifics, packages/docs/start-here/private-network-deployment.mdx for enterprise networking

Frequently Asked Questions

What Kubernetes versions are supported for openwork production deployment?

The Helm chart targets Kubernetes 1.24+. Earlier versions may function but are not validated in CI. The chart uses standard Deployments, Services, and Job resources without CRDs, ensuring broad compatibility with managed offerings (EKS, GKE, AKS) and on-premise distributions.

Can I deploy openwork without Kubernetes?

Yes, but not via official automation. The repository contains Dockerfiles (packaging/docker/Dockerfile.den-web) for manual container orchestration. However, the Helm chart in packaging/helm/openwork-ee/ is the only production-hardened, maintained deployment path. Docker Compose and bare-metal deployments require custom integration work.

How do I handle certificate rotation in a private-network deployment?

Mount custom CAs via customCa.enabled: true and customCa.existingSecret, then trigger rolling restarts:

kubectl rollout restart deployment/openwork-ee-den-api -n openwork
kubectl rollout restart deployment/openwork-ee-den-web -n openwork

The network-diagnostics.mdx guide validates TLS handshake behavior from both server and desktop perspectives.

What database permissions does openwork require?

The databaseUrl user needs FULL privileges on the target database: CREATE, ALTER, DROP for migrations; plus standard DML for runtime operations. The migration Job executes schema changes automatically on chart upgrade. Use a dedicated database user rather than root, and enable SSL verification (sslmode=verify-full) in production.

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 →