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:
betterAuthSecretanddenDbEncryptionKeymust be 32+ random characters - Configure origins carefully:
webOrigin,apiOrigin, anddesktopDenBaseUrlmust be mutually reachable by desktop clients - Reference source documentation:
packages/docs/start-here/self-host.mdxfor architecture,packaging/helm/openwork-ee/README.mdfor Helm specifics,packages/docs/start-here/private-network-deployment.mdxfor 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →