add openclaw
parent
d4f9eb3ab7
commit
26190c21db
@ -0,0 +1,23 @@
|
|||||||
|
# Patterns to ignore when building packages.
|
||||||
|
# This supports shell glob matching, relative path matching, and
|
||||||
|
# negation (prefixed with !). Only one pattern per line.
|
||||||
|
.DS_Store
|
||||||
|
# Common VCS dirs
|
||||||
|
.git/
|
||||||
|
.gitignore
|
||||||
|
.bzr/
|
||||||
|
.bzrignore
|
||||||
|
.hg/
|
||||||
|
.hgignore
|
||||||
|
.svn/
|
||||||
|
# Common backup files
|
||||||
|
*.swp
|
||||||
|
*.bak
|
||||||
|
*.tmp
|
||||||
|
*.orig
|
||||||
|
*~
|
||||||
|
# Various IDEs
|
||||||
|
.project
|
||||||
|
.idea/
|
||||||
|
*.tmproj
|
||||||
|
.vscode/
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
apiVersion: v2
|
||||||
|
name: openclaw
|
||||||
|
description: Personal AI assistant with multi-channel support
|
||||||
|
type: application
|
||||||
|
version: 1.0.0
|
||||||
|
appVersion: "2026.2.1"
|
||||||
|
keywords:
|
||||||
|
- ai
|
||||||
|
- assistant
|
||||||
|
- whatsapp
|
||||||
|
- telegram
|
||||||
|
- discord
|
||||||
|
- claude
|
||||||
|
- chatgpt
|
||||||
|
home: https://github.com/openclaw/openclaw
|
||||||
|
sources:
|
||||||
|
- https://github.com/openclaw/openclaw
|
||||||
|
maintainers:
|
||||||
|
- name: Openclaw Team
|
||||||
|
url: https://docs.clawd.bot
|
||||||
|
icon: https://github.com/openclaw/openclaw/raw/main/README-header.png
|
||||||
@ -0,0 +1,253 @@
|
|||||||
|
# Publishing the Openclaw Helm Chart
|
||||||
|
|
||||||
|
This guide covers how to publish the Helm chart to make it available for users.
|
||||||
|
|
||||||
|
## Quick Start (Automated)
|
||||||
|
|
||||||
|
The chart is automatically published to GitHub Pages when you:
|
||||||
|
|
||||||
|
1. Update `charts/openclaw/Chart.yaml` version
|
||||||
|
2. Commit and push to `main` branch
|
||||||
|
3. GitHub Actions automatically packages and publishes
|
||||||
|
|
||||||
|
## Publishing Methods
|
||||||
|
|
||||||
|
### Option 1: GitHub Pages (Recommended)
|
||||||
|
|
||||||
|
#### Initial Setup
|
||||||
|
|
||||||
|
1. **Enable GitHub Pages:**
|
||||||
|
- Go to repository Settings → Pages
|
||||||
|
- Source: Deploy from a branch
|
||||||
|
- Branch: `gh-pages` → `/ (root)`
|
||||||
|
- Click Save
|
||||||
|
|
||||||
|
2. **Create gh-pages branch (first time only):**
|
||||||
|
```bash
|
||||||
|
# Create empty gh-pages branch
|
||||||
|
git checkout --orphan gh-pages
|
||||||
|
git rm -rf .
|
||||||
|
echo "# Openclaw Helm Charts" > README.md
|
||||||
|
git add README.md
|
||||||
|
git commit -m "Initial gh-pages"
|
||||||
|
git push origin gh-pages
|
||||||
|
git checkout main
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **The workflow will automatically:**
|
||||||
|
- Package the chart
|
||||||
|
- Create a GitHub release
|
||||||
|
- Update the chart repository index
|
||||||
|
- Publish to GitHub Pages
|
||||||
|
|
||||||
|
#### Manual Publishing (if needed)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Package the chart
|
||||||
|
helm package charts/openclaw -d .cr-release-packages
|
||||||
|
|
||||||
|
# 2. Create index
|
||||||
|
helm repo index .cr-release-packages --url https://openclaw.github.io/openclaw
|
||||||
|
|
||||||
|
# 3. Commit to gh-pages branch
|
||||||
|
git checkout gh-pages
|
||||||
|
cp .cr-release-packages/* .
|
||||||
|
git add .
|
||||||
|
git commit -m "Release chart version X.Y.Z"
|
||||||
|
git push origin gh-pages
|
||||||
|
git checkout main
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Usage for End Users
|
||||||
|
|
||||||
|
Once published, users can install via:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Add repo
|
||||||
|
helm repo add openclaw https://openclaw.github.io/openclaw
|
||||||
|
helm repo update
|
||||||
|
|
||||||
|
# Install
|
||||||
|
helm install my-openclaw openclaw/openclaw
|
||||||
|
```
|
||||||
|
|
||||||
|
### Option 2: OCI Registry (GitHub Container Registry)
|
||||||
|
|
||||||
|
Modern approach using OCI registries:
|
||||||
|
|
||||||
|
#### Setup
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Login to GHCR
|
||||||
|
echo $GITHUB_TOKEN | helm registry login ghcr.io -u USERNAME --password-stdin
|
||||||
|
|
||||||
|
# Package chart
|
||||||
|
helm package charts/openclaw
|
||||||
|
|
||||||
|
# Push to GHCR
|
||||||
|
helm push openclaw-1.0.0.tgz oci://ghcr.io/openclaw
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Automate in GitHub Actions
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- name: Login to GHCR
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Push chart to GHCR
|
||||||
|
run: |
|
||||||
|
helm package charts/openclaw
|
||||||
|
helm push openclaw-*.tgz oci://ghcr.io/${{ github.repository_owner }}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Usage for End Users
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Install directly from OCI
|
||||||
|
helm install my-openclaw oci://ghcr.io/openclaw/openclaw --version 1.0.0
|
||||||
|
```
|
||||||
|
|
||||||
|
### Option 3: Artifact Hub
|
||||||
|
|
||||||
|
Make your chart discoverable on [Artifact Hub](https://artifacthub.io).
|
||||||
|
|
||||||
|
#### Prerequisites
|
||||||
|
|
||||||
|
- Chart published to GitHub Pages or OCI registry
|
||||||
|
- Artifact Hub metadata file
|
||||||
|
|
||||||
|
#### Add Artifact Hub metadata
|
||||||
|
|
||||||
|
Create `charts/openclaw/artifacthub-repo.yml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
repositoryID: <your-repo-id>
|
||||||
|
owners:
|
||||||
|
- name: Openclaw Team
|
||||||
|
email: team@openclaw.com
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Submit to Artifact Hub
|
||||||
|
|
||||||
|
1. Go to https://artifacthub.io
|
||||||
|
2. Sign in with GitHub
|
||||||
|
3. Add repository
|
||||||
|
4. Provide repository URL: `https://openclaw.github.io/openclaw`
|
||||||
|
5. Wait for verification
|
||||||
|
|
||||||
|
### Option 4: ChartMuseum (Self-Hosted)
|
||||||
|
|
||||||
|
For private/internal charts:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run ChartMuseum
|
||||||
|
docker run -d \
|
||||||
|
-p 8080:8080 \
|
||||||
|
-v $(pwd)/charts:/charts \
|
||||||
|
ghcr.io/helm/chartmuseum:latest \
|
||||||
|
--storage local \
|
||||||
|
--storage-local-rootdir /charts
|
||||||
|
|
||||||
|
# Upload chart
|
||||||
|
curl --data-binary "@openclaw-1.0.0.tgz" http://localhost:8080/api/charts
|
||||||
|
```
|
||||||
|
|
||||||
|
## Versioning
|
||||||
|
|
||||||
|
Follow Semantic Versioning:
|
||||||
|
|
||||||
|
- **Chart version** (`version` in Chart.yaml): Chart changes
|
||||||
|
- **App version** (`appVersion` in Chart.yaml): Openclaw version
|
||||||
|
|
||||||
|
### Bumping Versions
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Update chart version
|
||||||
|
vim charts/openclaw/Chart.yaml
|
||||||
|
# Change version: 1.0.0 → 1.1.0
|
||||||
|
|
||||||
|
# Update app version (when openclaw version changes)
|
||||||
|
vim charts/openclaw/Chart.yaml
|
||||||
|
# Change appVersion: "2026.1.25" → "2026.1.26"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Version Guidelines
|
||||||
|
|
||||||
|
- **Major** (1.0.0 → 2.0.0): Breaking changes to values.yaml or behavior
|
||||||
|
- **Minor** (1.0.0 → 1.1.0): New features, non-breaking changes
|
||||||
|
- **Patch** (1.0.0 → 1.0.1): Bug fixes, documentation
|
||||||
|
|
||||||
|
## Release Checklist
|
||||||
|
|
||||||
|
- [ ] Update `version` in `Chart.yaml`
|
||||||
|
- [ ] Update `appVersion` if openclaw version changed
|
||||||
|
- [ ] Update `CHANGELOG.md` (if you have one)
|
||||||
|
- [ ] Test chart locally: `./scripts/test-helm-local.sh`
|
||||||
|
- [ ] Lint chart: `helm lint charts/openclaw`
|
||||||
|
- [ ] Commit changes
|
||||||
|
- [ ] Push to main (triggers automated release)
|
||||||
|
- [ ] Verify GitHub Pages deployment
|
||||||
|
- [ ] Test installation from published repo
|
||||||
|
|
||||||
|
## Testing Published Chart
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Add your published repo
|
||||||
|
helm repo add openclaw https://openclaw.github.io/openclaw
|
||||||
|
helm repo update
|
||||||
|
|
||||||
|
# Search for chart
|
||||||
|
helm search repo openclaw
|
||||||
|
|
||||||
|
# Install from published repo
|
||||||
|
helm install test openclaw/openclaw --dry-run --debug
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Chart not appearing after publish
|
||||||
|
|
||||||
|
1. Check GitHub Actions logs
|
||||||
|
2. Verify gh-pages branch exists
|
||||||
|
3. Check GitHub Pages settings are enabled
|
||||||
|
4. Wait 5-10 minutes for GitHub Pages to deploy
|
||||||
|
|
||||||
|
### Users getting "not found" error
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check index.yaml exists
|
||||||
|
curl https://openclaw.github.io/openclaw/index.yaml
|
||||||
|
|
||||||
|
# Verify chart package exists
|
||||||
|
curl https://openclaw.github.io/openclaw/openclaw-1.0.0.tgz
|
||||||
|
```
|
||||||
|
|
||||||
|
### Permission denied during publishing
|
||||||
|
|
||||||
|
Ensure GitHub Actions has write permissions:
|
||||||
|
- Settings → Actions → General → Workflow permissions
|
||||||
|
- Select "Read and write permissions"
|
||||||
|
|
||||||
|
## Advanced: Multi-Chart Repository
|
||||||
|
|
||||||
|
If you add more charts:
|
||||||
|
|
||||||
|
```
|
||||||
|
charts/
|
||||||
|
openclaw/
|
||||||
|
openclaw-operator/
|
||||||
|
openclaw-monitoring/
|
||||||
|
```
|
||||||
|
|
||||||
|
The `helm/chart-releaser-action` automatically handles multiple charts.
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- [Helm Chart Repository Guide](https://helm.sh/docs/topics/chart_repository/)
|
||||||
|
- [Chart Releaser Action](https://github.com/helm/chart-releaser-action)
|
||||||
|
- [Artifact Hub](https://artifacthub.io/docs/topics/repositories/)
|
||||||
|
- [OCI Registry Support](https://helm.sh/docs/topics/registries/)
|
||||||
@ -0,0 +1,302 @@
|
|||||||
|
# Openclaw Helm Chart
|
||||||
|
|
||||||
|
Personal AI assistant with multi-channel support (WhatsApp, Telegram, Discord, and more) running on Kubernetes.
|
||||||
|
|
||||||
|
## TL;DR
|
||||||
|
|
||||||
|
```bash
|
||||||
|
helm install my-openclaw ./openclaw \
|
||||||
|
--set secrets.data.anthropicApiKey=sk-ant-xxx \
|
||||||
|
--set secrets.data.gatewayToken=$(openssl rand -hex 32)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
|
||||||
|
This chart deploys Openclaw Gateway on a Kubernetes cluster using the Helm package manager.
|
||||||
|
|
||||||
|
**Important:** Openclaw is designed as a single-user personal assistant. The chart enforces `replicas: 1` and does not support horizontal scaling.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- Kubernetes 1.19+
|
||||||
|
- Helm 3.x
|
||||||
|
- PV provisioner support in the underlying infrastructure (for persistent storage)
|
||||||
|
- **Optional:** Ingress controller (NGINX, Traefik, etc.) for external access
|
||||||
|
- **Optional:** cert-manager for automatic TLS certificates
|
||||||
|
|
||||||
|
## Installing the Chart
|
||||||
|
|
||||||
|
### Basic Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
helm install my-openclaw ./openclaw
|
||||||
|
```
|
||||||
|
|
||||||
|
### With Custom Values
|
||||||
|
|
||||||
|
```bash
|
||||||
|
helm install my-openclaw ./openclaw \
|
||||||
|
--values examples/values-production.yaml \
|
||||||
|
--set secrets.data.anthropicApiKey=sk-ant-xxx \
|
||||||
|
--set ingress.hosts[0].host=assistant.example.com
|
||||||
|
```
|
||||||
|
|
||||||
|
### From Examples
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Basic (local testing)
|
||||||
|
helm install my-openclaw ./openclaw -f examples/values-basic.yaml
|
||||||
|
|
||||||
|
# Production
|
||||||
|
helm install my-openclaw ./openclaw -f examples/values-production.yaml
|
||||||
|
|
||||||
|
# Fly.io-like setup
|
||||||
|
helm install my-openclaw ./openclaw -f examples/values-fly-like.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
## Uninstalling the Chart
|
||||||
|
|
||||||
|
```bash
|
||||||
|
helm uninstall my-openclaw
|
||||||
|
|
||||||
|
# Also delete PVCs (data will be lost)
|
||||||
|
kubectl delete pvc -l app.kubernetes.io/instance=my-openclaw
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
### Key Parameters
|
||||||
|
|
||||||
|
| Parameter | Description | Default |
|
||||||
|
|-----------|-------------|---------|
|
||||||
|
| `replicaCount` | Number of replicas (must be 1) | `1` |
|
||||||
|
| `image.repository` | Openclaw image repository | `openclaw/openclaw` |
|
||||||
|
| `image.tag` | Image tag | Chart appVersion |
|
||||||
|
| `gateway.bind` | Gateway binding mode (`loopback`, `lan`, `auto`) | `lan` |
|
||||||
|
| `gateway.port` | Gateway port | `18789` |
|
||||||
|
| `secrets.data.anthropicApiKey` | Anthropic API key | `""` |
|
||||||
|
| `secrets.data.openaiApiKey` | OpenAI API key | `""` |
|
||||||
|
| `secrets.data.gatewayToken` | Gateway authentication token (auto-generated if empty) | `""` |
|
||||||
|
| `persistence.enabled` | Enable persistent storage | `true` |
|
||||||
|
| `persistence.size` | PVC size | `10Gi` |
|
||||||
|
| `ingress.enabled` | Enable Ingress | `false` |
|
||||||
|
| `ingress.className` | Ingress class | `nginx` |
|
||||||
|
| `resources.limits.memory` | Memory limit | `2Gi` |
|
||||||
|
| `resources.requests.memory` | Memory request | `512Mi` |
|
||||||
|
|
||||||
|
### Full Values Reference
|
||||||
|
|
||||||
|
See [values.yaml](values.yaml) for all available parameters.
|
||||||
|
|
||||||
|
## Storage
|
||||||
|
|
||||||
|
The chart creates a StatefulSet with a persistent volume claim template that mounts storage at two locations:
|
||||||
|
|
||||||
|
- `/home/node/.openclaw` (subPath: `openclaw-state`) - Configuration, sessions, device identity, SQLite databases
|
||||||
|
- `/home/node/clawd` (subPath: `openclaw-workspace`) - Agent workspace files
|
||||||
|
|
||||||
|
**Storage Class:** By default, uses the cluster's default storage class. Override with `persistence.storageClass`.
|
||||||
|
|
||||||
|
**Size Recommendations:**
|
||||||
|
- Development/Testing: 5-10Gi
|
||||||
|
- Production (single user): 20Gi+
|
||||||
|
- Depends on session history and workspace usage
|
||||||
|
|
||||||
|
## Secrets Management
|
||||||
|
|
||||||
|
### Option 1: Inline Secrets (Development Only)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
helm install my-openclaw ./openclaw \
|
||||||
|
--set secrets.data.anthropicApiKey=sk-ant-xxx \
|
||||||
|
--set secrets.data.gatewayToken=$(openssl rand -hex 32)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Option 2: External Secret (Production)
|
||||||
|
|
||||||
|
Create a Kubernetes Secret:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kubectl create secret generic openclaw-secrets \
|
||||||
|
--from-literal=gatewayToken=$(openssl rand -hex 32) \
|
||||||
|
--from-literal=anthropicApiKey=sk-ant-xxx \
|
||||||
|
--from-literal=discordBotToken=MTQ...
|
||||||
|
```
|
||||||
|
|
||||||
|
Install with existing secret:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
helm install my-openclaw ./openclaw \
|
||||||
|
--set secrets.create=false \
|
||||||
|
--set secrets.existingSecret=openclaw-secrets
|
||||||
|
```
|
||||||
|
|
||||||
|
### Option 3: External Secrets Operator
|
||||||
|
|
||||||
|
Use External Secrets Operator to sync from Vault, AWS Secrets Manager, etc.
|
||||||
|
|
||||||
|
## Ingress
|
||||||
|
|
||||||
|
### NGINX Ingress Controller
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
ingress:
|
||||||
|
enabled: true
|
||||||
|
className: nginx
|
||||||
|
annotations:
|
||||||
|
cert-manager.io/cluster-issuer: letsencrypt-prod
|
||||||
|
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
|
||||||
|
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
|
||||||
|
nginx.ingress.kubernetes.io/websocket-services: "{{ include \"openclaw.fullname\" . }}"
|
||||||
|
hosts:
|
||||||
|
- host: assistant.example.com
|
||||||
|
paths:
|
||||||
|
- path: /
|
||||||
|
pathType: Prefix
|
||||||
|
tls:
|
||||||
|
- secretName: openclaw-tls
|
||||||
|
hosts:
|
||||||
|
- assistant.example.com
|
||||||
|
```
|
||||||
|
|
||||||
|
**Important:** WebSocket support requires extended timeouts (3600s recommended).
|
||||||
|
|
||||||
|
### Traefik
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
ingress:
|
||||||
|
className: traefik
|
||||||
|
annotations:
|
||||||
|
traefik.ingress.kubernetes.io/router.entrypoints: websecure
|
||||||
|
traefik.ingress.kubernetes.io/router.tls: "true"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Health Checks
|
||||||
|
|
||||||
|
The chart uses exec-based probes that run the CLI health command:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
livenessProbe:
|
||||||
|
exec:
|
||||||
|
command:
|
||||||
|
- sh
|
||||||
|
- -c
|
||||||
|
- node dist/index.js health --token "${CLAWDBOT_GATEWAY_TOKEN}" || exit 1
|
||||||
|
```
|
||||||
|
|
||||||
|
## Upgrading
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Pull latest chart changes
|
||||||
|
git pull
|
||||||
|
|
||||||
|
# Upgrade with current values
|
||||||
|
helm upgrade my-openclaw ./openclaw --reuse-values
|
||||||
|
|
||||||
|
# Upgrade with new values
|
||||||
|
helm upgrade my-openclaw ./openclaw -f values-production.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Pod Not Starting
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check pod events
|
||||||
|
kubectl describe pod my-openclaw-0
|
||||||
|
|
||||||
|
# Check logs
|
||||||
|
kubectl logs my-openclaw-0
|
||||||
|
```
|
||||||
|
|
||||||
|
### OOM (Out of Memory)
|
||||||
|
|
||||||
|
Increase memory limits:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
memory: 4Gi
|
||||||
|
requests:
|
||||||
|
memory: 1Gi
|
||||||
|
```
|
||||||
|
|
||||||
|
**Note:** 512MB is too small for production. 2GB recommended minimum.
|
||||||
|
|
||||||
|
### PVC Not Binding
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check PVC status
|
||||||
|
kubectl get pvc
|
||||||
|
|
||||||
|
# Check storage class
|
||||||
|
kubectl get storageclass
|
||||||
|
|
||||||
|
# Describe for events
|
||||||
|
kubectl describe pvc data-my-openclaw-0
|
||||||
|
```
|
||||||
|
|
||||||
|
### WebSocket Connections Timing Out
|
||||||
|
|
||||||
|
Ensure Ingress has WebSocket annotations:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
annotations:
|
||||||
|
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
|
||||||
|
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Gateway Lock File Issues
|
||||||
|
|
||||||
|
If the gateway won't start due to stale lock files:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kubectl exec my-openclaw-0 -- rm -f /home/node/.openclaw/gateway.*.lock
|
||||||
|
kubectl delete pod my-openclaw-0 # Restart pod
|
||||||
|
```
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
### Local Testing (Minikube/Kind/Docker Desktop)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Build local image
|
||||||
|
docker build -t openclaw:local .
|
||||||
|
|
||||||
|
# Load into cluster (example for Minikube)
|
||||||
|
minikube image load openclaw:local
|
||||||
|
|
||||||
|
# Install chart
|
||||||
|
helm install test ./openclaw \
|
||||||
|
-f examples/values-basic.yaml \
|
||||||
|
--set image.repository=openclaw \
|
||||||
|
--set image.tag=local \
|
||||||
|
--set image.pullPolicy=Never
|
||||||
|
```
|
||||||
|
|
||||||
|
### Production Deployment
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Create external secret first
|
||||||
|
kubectl create secret generic openclaw-secrets \
|
||||||
|
--from-literal=gatewayToken=$(openssl rand -hex 32) \
|
||||||
|
--from-literal=anthropicApiKey=$ANTHROPIC_API_KEY
|
||||||
|
|
||||||
|
# Install with production values
|
||||||
|
helm install my-openclaw ./openclaw -f examples/values-production.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
- [Openclaw Documentation](https://docs.clawd.bot)
|
||||||
|
- [Kubernetes Installation Guide](https://docs.clawd.bot/install/kubernetes)
|
||||||
|
- [GitHub Repository](https://github.com/openclaw/openclaw)
|
||||||
|
|
||||||
|
## Support
|
||||||
|
|
||||||
|
- [GitHub Issues](https://github.com/openclaw/openclaw/issues)
|
||||||
|
- [Documentation](https://docs.clawd.bot)
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
See [LICENSE](https://github.com/openclaw/openclaw/blob/main/LICENSE)
|
||||||
@ -0,0 +1,33 @@
|
|||||||
|
# Basic configuration for local testing or development
|
||||||
|
|
||||||
|
image:
|
||||||
|
repository: openclaw
|
||||||
|
tag: local
|
||||||
|
pullPolicy: Never
|
||||||
|
|
||||||
|
gateway:
|
||||||
|
bind: lan
|
||||||
|
port: 18789
|
||||||
|
allowUnconfigured: false
|
||||||
|
|
||||||
|
secrets:
|
||||||
|
create: true
|
||||||
|
data:
|
||||||
|
# Set via --set or environment variables
|
||||||
|
anthropicApiKey: ""
|
||||||
|
gatewayToken: ""
|
||||||
|
|
||||||
|
persistence:
|
||||||
|
enabled: true
|
||||||
|
size: 5Gi
|
||||||
|
|
||||||
|
ingress:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
memory: 2Gi
|
||||||
|
cpu: 1000m
|
||||||
|
requests:
|
||||||
|
memory: 512Mi
|
||||||
|
cpu: 250m
|
||||||
@ -0,0 +1,55 @@
|
|||||||
|
# Configuration similar to Fly.io deployment
|
||||||
|
# Mimics fly.toml settings
|
||||||
|
|
||||||
|
image:
|
||||||
|
repository: ghcr.io/openclaw/openclaw
|
||||||
|
tag: "2026.1.25"
|
||||||
|
pullPolicy: IfNotPresent
|
||||||
|
|
||||||
|
gateway:
|
||||||
|
bind: lan
|
||||||
|
port: 3000 # Fly.io uses port 3000
|
||||||
|
allowUnconfigured: true
|
||||||
|
extraArgs: []
|
||||||
|
|
||||||
|
env:
|
||||||
|
NODE_ENV: production
|
||||||
|
CLAWDBOT_STATE_DIR: /home/node/.openclaw
|
||||||
|
CLAWDBOT_WORKSPACE_DIR: /home/node/clawd
|
||||||
|
NODE_OPTIONS: "--max-old-space-size=1536" # Fly.io recommendation
|
||||||
|
|
||||||
|
secrets:
|
||||||
|
create: false
|
||||||
|
existingSecret: openclaw-secrets
|
||||||
|
|
||||||
|
persistence:
|
||||||
|
enabled: true
|
||||||
|
size: 1Gi # Similar to Fly.io volume size
|
||||||
|
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
memory: 2Gi # shared-cpu-2x on Fly.io
|
||||||
|
cpu: 1000m
|
||||||
|
requests:
|
||||||
|
memory: 512Mi
|
||||||
|
cpu: 250m
|
||||||
|
|
||||||
|
service:
|
||||||
|
type: ClusterIP
|
||||||
|
port: 3000
|
||||||
|
|
||||||
|
ingress:
|
||||||
|
enabled: true
|
||||||
|
className: nginx
|
||||||
|
domain: "my-openclaw.example.com"
|
||||||
|
annotations:
|
||||||
|
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
|
||||||
|
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
|
||||||
|
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
|
||||||
|
nginx.ingress.kubernetes.io/websocket-services: "openclaw"
|
||||||
|
tls:
|
||||||
|
enabled: true
|
||||||
|
secretName: openclaw-tls
|
||||||
|
certManager:
|
||||||
|
enabled: true
|
||||||
|
issuer: letsencrypt-prod
|
||||||
@ -0,0 +1,89 @@
|
|||||||
|
# Production configuration with Ingress and external secrets
|
||||||
|
|
||||||
|
image:
|
||||||
|
repository: ghcr.io/openclaw/openclaw
|
||||||
|
tag: "2026.1.25"
|
||||||
|
pullPolicy: IfNotPresent
|
||||||
|
|
||||||
|
gateway:
|
||||||
|
bind: lan
|
||||||
|
port: 18789
|
||||||
|
allowUnconfigured: false
|
||||||
|
|
||||||
|
# Use external secret in production
|
||||||
|
secrets:
|
||||||
|
create: false
|
||||||
|
existingSecret: openclaw-secrets
|
||||||
|
|
||||||
|
config:
|
||||||
|
create: true
|
||||||
|
data:
|
||||||
|
agents:
|
||||||
|
defaults:
|
||||||
|
model:
|
||||||
|
primary: "anthropic/claude-opus-4-5"
|
||||||
|
fallbacks:
|
||||||
|
- "anthropic/claude-sonnet-4-5"
|
||||||
|
maxConcurrent: 4
|
||||||
|
sandbox:
|
||||||
|
mode: "off"
|
||||||
|
list:
|
||||||
|
- id: main
|
||||||
|
default: true
|
||||||
|
auth:
|
||||||
|
profiles:
|
||||||
|
"anthropic:default":
|
||||||
|
mode: token
|
||||||
|
provider: anthropic
|
||||||
|
gateway:
|
||||||
|
mode: local
|
||||||
|
bind: auto
|
||||||
|
auth:
|
||||||
|
mode: token
|
||||||
|
controlUi:
|
||||||
|
enabled: true
|
||||||
|
channels:
|
||||||
|
discord:
|
||||||
|
enabled: true
|
||||||
|
meta:
|
||||||
|
lastTouchedVersion: "2026.1.25"
|
||||||
|
|
||||||
|
persistence:
|
||||||
|
enabled: true
|
||||||
|
storageClass: fast-ssd
|
||||||
|
size: 20Gi
|
||||||
|
accessMode: ReadWriteOnce
|
||||||
|
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
memory: 4Gi
|
||||||
|
cpu: 2000m
|
||||||
|
requests:
|
||||||
|
memory: 1Gi
|
||||||
|
cpu: 500m
|
||||||
|
|
||||||
|
ingress:
|
||||||
|
enabled: true
|
||||||
|
className: nginx
|
||||||
|
domain: "assistant.example.com"
|
||||||
|
annotations:
|
||||||
|
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
|
||||||
|
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
|
||||||
|
nginx.ingress.kubernetes.io/websocket-services: "openclaw"
|
||||||
|
tls:
|
||||||
|
enabled: true
|
||||||
|
secretName: openclaw-tls
|
||||||
|
certManager:
|
||||||
|
enabled: true
|
||||||
|
issuer: letsencrypt-prod
|
||||||
|
|
||||||
|
affinity:
|
||||||
|
nodeAffinity:
|
||||||
|
preferredDuringSchedulingIgnoredDuringExecution:
|
||||||
|
- weight: 100
|
||||||
|
preference:
|
||||||
|
matchExpressions:
|
||||||
|
- key: workload-type
|
||||||
|
operator: In
|
||||||
|
values:
|
||||||
|
- stateful
|
||||||
@ -0,0 +1,73 @@
|
|||||||
|
Thank you for installing {{ .Chart.Name }}!
|
||||||
|
|
||||||
|
Your Openclaw gateway is starting up. This may take 1-2 minutes.
|
||||||
|
|
||||||
|
{{- if .Values.ingress.enabled }}
|
||||||
|
{{- $domain := .Values.ingress.domain }}
|
||||||
|
{{- $tlsEnabled := .Values.ingress.tls.enabled }}
|
||||||
|
{{- if $domain }}
|
||||||
|
|
||||||
|
==========================================================
|
||||||
|
Openclaw is accessible at:
|
||||||
|
|
||||||
|
{{- if $tlsEnabled }}
|
||||||
|
https://{{ $domain }}
|
||||||
|
{{- else }}
|
||||||
|
http://{{ $domain }}
|
||||||
|
{{- end }}
|
||||||
|
==========================================================
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
1. Check the gateway status:
|
||||||
|
|
||||||
|
kubectl get statefulset {{ include "openclaw.fullname" . }} -n {{ .Release.Namespace }}
|
||||||
|
kubectl logs -f {{ include "openclaw.fullname" . }}-0 -n {{ .Release.Namespace }}
|
||||||
|
|
||||||
|
2. Access the Control UI:
|
||||||
|
|
||||||
|
{{- if .Values.ingress.enabled }}
|
||||||
|
{{- $domain := .Values.ingress.domain }}
|
||||||
|
{{- $tlsEnabled := .Values.ingress.tls.enabled }}
|
||||||
|
{{- if $domain }}
|
||||||
|
{{- if $tlsEnabled }}
|
||||||
|
https://{{ $domain }}
|
||||||
|
{{- else }}
|
||||||
|
http://{{ $domain }}
|
||||||
|
{{- end }}
|
||||||
|
{{- else }}
|
||||||
|
{{- range .Values.ingress.hosts }}
|
||||||
|
https://{{ .host }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
{{- else }}
|
||||||
|
# Port-forward to access locally:
|
||||||
|
kubectl port-forward {{ include "openclaw.fullname" . }}-0 {{ .Values.gateway.port }}:{{ .Values.gateway.port }} -n {{ .Release.Namespace }}
|
||||||
|
|
||||||
|
Then visit: http://localhost:{{ .Values.gateway.port }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
3. Your gateway token (for authentication):
|
||||||
|
|
||||||
|
kubectl get secret {{ include "openclaw.secretName" . }} -n {{ .Release.Namespace }} -o jsonpath='{.data.gatewayToken}' | base64 -d && echo
|
||||||
|
|
||||||
|
4. Configure channels:
|
||||||
|
|
||||||
|
kubectl exec -it {{ include "openclaw.fullname" . }}-0 -n {{ .Release.Namespace }} -- node dist/index.js channels add --channel discord --token YOUR_BOT_TOKEN
|
||||||
|
|
||||||
|
5. Check health:
|
||||||
|
|
||||||
|
kubectl exec -it {{ include "openclaw.fullname" . }}-0 -n {{ .Release.Namespace }} -- node dist/index.js health
|
||||||
|
|
||||||
|
Documentation: https://docs.clawd.bot
|
||||||
|
Support: https://github.com/openclaw/openclaw/issues
|
||||||
|
|
||||||
|
{{- if not .Values.persistence.enabled }}
|
||||||
|
|
||||||
|
WARNING: Persistence is disabled. State will be lost on pod restart.
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{- if eq .Values.gateway.bind "loopback" }}
|
||||||
|
|
||||||
|
WARNING: Gateway is bound to loopback. External access will not work.
|
||||||
|
{{- end }}
|
||||||
@ -0,0 +1,84 @@
|
|||||||
|
{{/*
|
||||||
|
Expand the name of the chart.
|
||||||
|
*/}}
|
||||||
|
{{- define "openclaw.name" -}}
|
||||||
|
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{/*
|
||||||
|
Create a default fully qualified app name.
|
||||||
|
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
|
||||||
|
If release name contains chart name it will be used as a full name.
|
||||||
|
*/}}
|
||||||
|
{{- define "openclaw.fullname" -}}
|
||||||
|
{{- if .Values.fullnameOverride }}
|
||||||
|
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
|
||||||
|
{{- else }}
|
||||||
|
{{- $name := default .Chart.Name .Values.nameOverride }}
|
||||||
|
{{- if contains $name .Release.Name }}
|
||||||
|
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
|
||||||
|
{{- else }}
|
||||||
|
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{/*
|
||||||
|
Create chart name and version as used by the chart label.
|
||||||
|
*/}}
|
||||||
|
{{- define "openclaw.chart" -}}
|
||||||
|
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{/*
|
||||||
|
Common labels
|
||||||
|
*/}}
|
||||||
|
{{- define "openclaw.labels" -}}
|
||||||
|
helm.sh/chart: {{ include "openclaw.chart" . }}
|
||||||
|
{{ include "openclaw.selectorLabels" . }}
|
||||||
|
{{- if .Chart.AppVersion }}
|
||||||
|
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
|
||||||
|
{{- end }}
|
||||||
|
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{/*
|
||||||
|
Selector labels
|
||||||
|
*/}}
|
||||||
|
{{- define "openclaw.selectorLabels" -}}
|
||||||
|
app.kubernetes.io/name: {{ include "openclaw.name" . }}
|
||||||
|
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{/*
|
||||||
|
Create the name of the service account to use
|
||||||
|
*/}}
|
||||||
|
{{- define "openclaw.serviceAccountName" -}}
|
||||||
|
{{- if .Values.serviceAccount.create }}
|
||||||
|
{{- default (include "openclaw.fullname" .) .Values.serviceAccount.name }}
|
||||||
|
{{- else }}
|
||||||
|
{{- default "default" .Values.serviceAccount.name }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{/*
|
||||||
|
Create the name of the secret to use
|
||||||
|
*/}}
|
||||||
|
{{- define "openclaw.secretName" -}}
|
||||||
|
{{- if .Values.secrets.existingSecret }}
|
||||||
|
{{- .Values.secrets.existingSecret }}
|
||||||
|
{{- else }}
|
||||||
|
{{- include "openclaw.fullname" . }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{/*
|
||||||
|
Create the name of the config map to use
|
||||||
|
*/}}
|
||||||
|
{{- define "openclaw.configMapName" -}}
|
||||||
|
{{- if .Values.config.existingConfigMap }}
|
||||||
|
{{- .Values.config.existingConfigMap }}
|
||||||
|
{{- else }}
|
||||||
|
{{- printf "%s-config" (include "openclaw.fullname" .) }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
{{- if .Values.config.create -}}
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: {{ include "openclaw.configMapName" . }}
|
||||||
|
labels:
|
||||||
|
{{- include "openclaw.labels" . | nindent 4 }}
|
||||||
|
data:
|
||||||
|
openclaw.json: |
|
||||||
|
{{ .Values.config.data | toJson | nindent 4 }}
|
||||||
|
{{- end }}
|
||||||
@ -0,0 +1,62 @@
|
|||||||
|
{{- if .Values.ingress.enabled -}}
|
||||||
|
{{- $fullName := include "openclaw.fullname" . -}}
|
||||||
|
{{- $svcPort := .Values.service.port -}}
|
||||||
|
{{- $domain := .Values.ingress.domain -}}
|
||||||
|
{{- $tlsEnabled := .Values.ingress.tls.enabled -}}
|
||||||
|
{{- $tlsSecretName := .Values.ingress.tls.secretName | default (printf "%s-tls" $fullName) -}}
|
||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: Ingress
|
||||||
|
metadata:
|
||||||
|
name: {{ $fullName }}
|
||||||
|
labels:
|
||||||
|
{{- include "openclaw.labels" . | nindent 4 }}
|
||||||
|
annotations:
|
||||||
|
{{- if and $tlsEnabled .Values.ingress.tls.certManager.enabled }}
|
||||||
|
cert-manager.io/cluster-issuer: {{ .Values.ingress.tls.certManager.issuer | quote }}
|
||||||
|
{{- end }}
|
||||||
|
{{- with .Values.ingress.annotations }}
|
||||||
|
{{- toYaml . | nindent 4 }}
|
||||||
|
{{- end }}
|
||||||
|
spec:
|
||||||
|
{{- if .Values.ingress.className }}
|
||||||
|
ingressClassName: {{ .Values.ingress.className }}
|
||||||
|
{{- end }}
|
||||||
|
{{- if $tlsEnabled }}
|
||||||
|
tls:
|
||||||
|
- hosts:
|
||||||
|
{{- if $domain }}
|
||||||
|
- {{ $domain | quote }}
|
||||||
|
{{- end }}
|
||||||
|
{{- range .Values.ingress.hosts }}
|
||||||
|
- {{ .host | quote }}
|
||||||
|
{{- end }}
|
||||||
|
secretName: {{ $tlsSecretName }}
|
||||||
|
{{- end }}
|
||||||
|
rules:
|
||||||
|
{{- if $domain }}
|
||||||
|
- host: {{ $domain | quote }}
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
- path: /
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: {{ $fullName }}
|
||||||
|
port:
|
||||||
|
number: {{ $svcPort }}
|
||||||
|
{{- end }}
|
||||||
|
{{- range .Values.ingress.hosts }}
|
||||||
|
- host: {{ .host | quote }}
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
{{- range .paths }}
|
||||||
|
- path: {{ .path }}
|
||||||
|
pathType: {{ .pathType }}
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: {{ $fullName }}
|
||||||
|
port:
|
||||||
|
number: {{ $svcPort }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
{{- if .Values.secrets.create -}}
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: {{ include "openclaw.fullname" . }}
|
||||||
|
labels:
|
||||||
|
{{- include "openclaw.labels" . | nindent 4 }}
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
gatewayToken: {{ .Values.secrets.data.gatewayToken | default (randAlphaNum 32) | quote }}
|
||||||
|
{{- with .Values.secrets.data.anthropicApiKey }}
|
||||||
|
anthropicApiKey: {{ . | quote }}
|
||||||
|
{{- end }}
|
||||||
|
{{- with .Values.secrets.data.openaiApiKey }}
|
||||||
|
openaiApiKey: {{ . | quote }}
|
||||||
|
{{- end }}
|
||||||
|
{{- with .Values.secrets.data.discordBotToken }}
|
||||||
|
discordBotToken: {{ . | quote }}
|
||||||
|
{{- end }}
|
||||||
|
{{- with .Values.secrets.data.telegramBotToken }}
|
||||||
|
telegramBotToken: {{ . | quote }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: {{ include "openclaw.fullname" . }}
|
||||||
|
labels:
|
||||||
|
{{- include "openclaw.labels" . | nindent 4 }}
|
||||||
|
{{- with .Values.service.annotations }}
|
||||||
|
annotations:
|
||||||
|
{{- toYaml . | nindent 4 }}
|
||||||
|
{{- end }}
|
||||||
|
spec:
|
||||||
|
type: {{ .Values.service.type }}
|
||||||
|
ports:
|
||||||
|
- port: {{ .Values.service.port }}
|
||||||
|
targetPort: gateway
|
||||||
|
protocol: TCP
|
||||||
|
name: gateway
|
||||||
|
selector:
|
||||||
|
{{- include "openclaw.selectorLabels" . | nindent 4 }}
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
{{- if .Values.serviceAccount.create -}}
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata:
|
||||||
|
name: {{ include "openclaw.serviceAccountName" . }}
|
||||||
|
labels:
|
||||||
|
{{- include "openclaw.labels" . | nindent 4 }}
|
||||||
|
{{- with .Values.serviceAccount.annotations }}
|
||||||
|
annotations:
|
||||||
|
{{- toYaml . | nindent 4 }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
@ -0,0 +1,202 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: StatefulSet
|
||||||
|
metadata:
|
||||||
|
name: {{ include "openclaw.fullname" . }}
|
||||||
|
labels:
|
||||||
|
{{- include "openclaw.labels" . | nindent 4 }}
|
||||||
|
spec:
|
||||||
|
serviceName: {{ include "openclaw.fullname" . }}
|
||||||
|
replicas: {{ .Values.replicaCount }}
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
{{- include "openclaw.selectorLabels" . | nindent 6 }}
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
{{- if .Values.config.create }}
|
||||||
|
checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
|
||||||
|
{{- end }}
|
||||||
|
{{- if .Values.secrets.create }}
|
||||||
|
checksum/secret: {{ include (print $.Template.BasePath "/secret.yaml") . | sha256sum }}
|
||||||
|
{{- end }}
|
||||||
|
{{- with .Values.podAnnotations }}
|
||||||
|
{{- toYaml . | nindent 8 }}
|
||||||
|
{{- end }}
|
||||||
|
labels:
|
||||||
|
{{- include "openclaw.selectorLabels" . | nindent 8 }}
|
||||||
|
spec:
|
||||||
|
{{- with .Values.imagePullSecrets }}
|
||||||
|
imagePullSecrets:
|
||||||
|
{{- toYaml . | nindent 8 }}
|
||||||
|
{{- end }}
|
||||||
|
serviceAccountName: {{ include "openclaw.serviceAccountName" . }}
|
||||||
|
securityContext:
|
||||||
|
{{- toYaml .Values.podSecurityContext | nindent 8 }}
|
||||||
|
{{- if or .Values.config.create .Values.initContainers }}
|
||||||
|
initContainers:
|
||||||
|
{{- if .Values.config.create }}
|
||||||
|
- name: init-config
|
||||||
|
image: "{{ .Values.image.registry }}/{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
|
||||||
|
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
||||||
|
command:
|
||||||
|
- sh
|
||||||
|
- -c
|
||||||
|
- |
|
||||||
|
echo "Initializing configuration..."
|
||||||
|
mkdir -p /home/node/.openclaw
|
||||||
|
if [ ! -f /home/node/.openclaw/openclaw.json ]; then
|
||||||
|
echo "Copying default config..."
|
||||||
|
cp /config/openclaw.json /home/node/.openclaw/openclaw.json
|
||||||
|
echo "Config initialized"
|
||||||
|
else
|
||||||
|
echo "Config already exists, skipping"
|
||||||
|
fi
|
||||||
|
volumeMounts:
|
||||||
|
- name: config
|
||||||
|
mountPath: /config
|
||||||
|
- name: data
|
||||||
|
mountPath: /home/node/.openclaw
|
||||||
|
subPath: openclaw-state
|
||||||
|
securityContext:
|
||||||
|
{{- toYaml .Values.securityContext | nindent 12 }}
|
||||||
|
{{- end }}
|
||||||
|
{{- with .Values.initContainers }}
|
||||||
|
{{- toYaml . | nindent 8 }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
containers:
|
||||||
|
- name: gateway
|
||||||
|
image: "{{ .Values.image.registry }}/{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
|
||||||
|
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
||||||
|
command:
|
||||||
|
- node
|
||||||
|
- dist/index.js
|
||||||
|
- gateway
|
||||||
|
- --bind
|
||||||
|
- {{ .Values.gateway.bind }}
|
||||||
|
- --port
|
||||||
|
- {{ .Values.gateway.port | quote }}
|
||||||
|
{{- if .Values.gateway.allowUnconfigured }}
|
||||||
|
- --allow-unconfigured
|
||||||
|
{{- end }}
|
||||||
|
{{- range .Values.gateway.extraArgs }}
|
||||||
|
- {{ . }}
|
||||||
|
{{- end }}
|
||||||
|
env:
|
||||||
|
{{- range $key, $value := .Values.env }}
|
||||||
|
- name: {{ $key }}
|
||||||
|
value: {{ $value | quote }}
|
||||||
|
{{- end }}
|
||||||
|
- name: CLAWDBOT_GATEWAY_PORT
|
||||||
|
value: {{ .Values.gateway.port | quote }}
|
||||||
|
- name: CLAWDBOT_GATEWAY_TOKEN
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: {{ include "openclaw.secretName" . }}
|
||||||
|
key: gatewayToken
|
||||||
|
{{- if .Values.secrets.data.anthropicApiKey }}
|
||||||
|
- name: ANTHROPIC_API_KEY
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: {{ include "openclaw.secretName" . }}
|
||||||
|
key: anthropicApiKey
|
||||||
|
optional: true
|
||||||
|
{{- end }}
|
||||||
|
{{- if .Values.secrets.data.openaiApiKey }}
|
||||||
|
- name: OPENAI_API_KEY
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: {{ include "openclaw.secretName" . }}
|
||||||
|
key: openaiApiKey
|
||||||
|
optional: true
|
||||||
|
{{- end }}
|
||||||
|
{{- if .Values.secrets.data.discordBotToken }}
|
||||||
|
- name: DISCORD_BOT_TOKEN
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: {{ include "openclaw.secretName" . }}
|
||||||
|
key: discordBotToken
|
||||||
|
optional: true
|
||||||
|
{{- end }}
|
||||||
|
{{- if .Values.secrets.data.telegramBotToken }}
|
||||||
|
- name: TELEGRAM_BOT_TOKEN
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: {{ include "openclaw.secretName" . }}
|
||||||
|
key: telegramBotToken
|
||||||
|
optional: true
|
||||||
|
{{- end }}
|
||||||
|
ports:
|
||||||
|
- name: gateway
|
||||||
|
containerPort: {{ .Values.gateway.port }}
|
||||||
|
protocol: TCP
|
||||||
|
volumeMounts:
|
||||||
|
- name: data
|
||||||
|
mountPath: /home/node/.openclaw
|
||||||
|
subPath: openclaw-state
|
||||||
|
- name: data
|
||||||
|
mountPath: /home/node/clawd
|
||||||
|
subPath: openclaw-workspace
|
||||||
|
{{- if .Values.livenessProbe.enabled }}
|
||||||
|
livenessProbe:
|
||||||
|
{{- omit .Values.livenessProbe "enabled" | toYaml | nindent 12 }}
|
||||||
|
{{- end }}
|
||||||
|
{{- if .Values.readinessProbe.enabled }}
|
||||||
|
readinessProbe:
|
||||||
|
{{- omit .Values.readinessProbe "enabled" | toYaml | nindent 12 }}
|
||||||
|
{{- end }}
|
||||||
|
{{- if .Values.startupProbe.enabled }}
|
||||||
|
startupProbe:
|
||||||
|
{{- omit .Values.startupProbe "enabled" | toYaml | nindent 12 }}
|
||||||
|
{{- end }}
|
||||||
|
{{- with .Values.lifecycle }}
|
||||||
|
lifecycle:
|
||||||
|
{{- toYaml . | nindent 12 }}
|
||||||
|
{{- end }}
|
||||||
|
resources:
|
||||||
|
{{- toYaml .Values.resources | nindent 12 }}
|
||||||
|
securityContext:
|
||||||
|
{{- toYaml .Values.securityContext | nindent 12 }}
|
||||||
|
{{- with .Values.extraContainers }}
|
||||||
|
{{- toYaml . | nindent 8 }}
|
||||||
|
{{- end }}
|
||||||
|
volumes:
|
||||||
|
{{- if .Values.config.create }}
|
||||||
|
- name: config
|
||||||
|
configMap:
|
||||||
|
name: {{ include "openclaw.configMapName" . }}
|
||||||
|
{{- end }}
|
||||||
|
{{- with .Values.nodeSelector }}
|
||||||
|
nodeSelector:
|
||||||
|
{{- toYaml . | nindent 8 }}
|
||||||
|
{{- end }}
|
||||||
|
{{- with .Values.affinity }}
|
||||||
|
affinity:
|
||||||
|
{{- toYaml . | nindent 8 }}
|
||||||
|
{{- end }}
|
||||||
|
{{- with .Values.tolerations }}
|
||||||
|
tolerations:
|
||||||
|
{{- toYaml . | nindent 8 }}
|
||||||
|
{{- end }}
|
||||||
|
{{- if .Values.persistence.enabled }}
|
||||||
|
volumeClaimTemplates:
|
||||||
|
- metadata:
|
||||||
|
name: data
|
||||||
|
{{- with .Values.persistence.annotations }}
|
||||||
|
annotations:
|
||||||
|
{{- toYaml . | nindent 10 }}
|
||||||
|
{{- end }}
|
||||||
|
spec:
|
||||||
|
accessModes:
|
||||||
|
- {{ .Values.persistence.accessMode }}
|
||||||
|
{{- if .Values.persistence.storageClass }}
|
||||||
|
storageClassName: {{ .Values.persistence.storageClass }}
|
||||||
|
{{- end }}
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: {{ .Values.persistence.size }}
|
||||||
|
{{- with .Values.persistence.selector }}
|
||||||
|
selector:
|
||||||
|
{{- toYaml . | nindent 10 }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Pod
|
||||||
|
metadata:
|
||||||
|
name: "{{ include "openclaw.fullname" . }}-test-connection"
|
||||||
|
labels:
|
||||||
|
{{- include "openclaw.labels" . | nindent 4 }}
|
||||||
|
annotations:
|
||||||
|
"helm.sh/hook": test
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: wget
|
||||||
|
image: busybox
|
||||||
|
command: ['wget']
|
||||||
|
args: ['{{ include "openclaw.fullname" . }}:{{ .Values.service.port }}']
|
||||||
|
restartPolicy: Never
|
||||||
@ -0,0 +1,74 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://json-schema.org/draft-07/schema#",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"replicaCount": {
|
||||||
|
"type": "integer",
|
||||||
|
"minimum": 1,
|
||||||
|
"maximum": 1,
|
||||||
|
"default": 1,
|
||||||
|
"description": "Must be 1 for single-user architecture. Openclaw does not support horizontal scaling."
|
||||||
|
},
|
||||||
|
"image": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"repository": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"pullPolicy": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["Always", "IfNotPresent", "Never"]
|
||||||
|
},
|
||||||
|
"tag": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"gateway": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"bind": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["loopback", "lan", "auto"],
|
||||||
|
"description": "Gateway binding mode"
|
||||||
|
},
|
||||||
|
"port": {
|
||||||
|
"type": "integer",
|
||||||
|
"minimum": 1,
|
||||||
|
"maximum": 65535
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"persistence": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"enabled": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"size": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^[0-9]+(Gi|Mi|Ti)$"
|
||||||
|
},
|
||||||
|
"accessMode": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["ReadWriteOnce", "ReadOnlyMany", "ReadWriteMany"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"service": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["ClusterIP", "NodePort", "LoadBalancer"]
|
||||||
|
},
|
||||||
|
"port": {
|
||||||
|
"type": "integer",
|
||||||
|
"minimum": 1,
|
||||||
|
"maximum": 65535
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["replicaCount"]
|
||||||
|
}
|
||||||
@ -0,0 +1,233 @@
|
|||||||
|
# Default values for openclaw.
|
||||||
|
# This is a YAML-formatted file.
|
||||||
|
# Declare variables to be passed into your templates.
|
||||||
|
|
||||||
|
# Image configuration
|
||||||
|
image:
|
||||||
|
registry: ghcr.io
|
||||||
|
repository: openclaw/openclaw
|
||||||
|
pullPolicy: IfNotPresent
|
||||||
|
# Overrides the image tag whose default is the chart appVersion.
|
||||||
|
tag: ""
|
||||||
|
|
||||||
|
imagePullSecrets: []
|
||||||
|
nameOverride: ""
|
||||||
|
fullnameOverride: ""
|
||||||
|
|
||||||
|
# Replica count - MUST be 1 for single-user architecture
|
||||||
|
replicaCount: 1
|
||||||
|
|
||||||
|
# Service account
|
||||||
|
serviceAccount:
|
||||||
|
# Specifies whether a service account should be created
|
||||||
|
create: true
|
||||||
|
# Annotations to add to the service account
|
||||||
|
annotations: {}
|
||||||
|
# The name of the service account to use.
|
||||||
|
# If not set and create is true, a name is generated using the fullname template
|
||||||
|
name: ""
|
||||||
|
|
||||||
|
# Pod annotations
|
||||||
|
podAnnotations: {}
|
||||||
|
|
||||||
|
# Pod security context
|
||||||
|
podSecurityContext:
|
||||||
|
runAsNonRoot: true
|
||||||
|
runAsUser: 1000
|
||||||
|
runAsGroup: 1000
|
||||||
|
fsGroup: 1000
|
||||||
|
|
||||||
|
# Container security context
|
||||||
|
securityContext:
|
||||||
|
allowPrivilegeEscalation: false
|
||||||
|
capabilities:
|
||||||
|
drop:
|
||||||
|
- ALL
|
||||||
|
readOnlyRootFilesystem: false # Node needs write access to /tmp
|
||||||
|
|
||||||
|
# Gateway configuration
|
||||||
|
gateway:
|
||||||
|
# Binding mode: loopback, lan, auto
|
||||||
|
bind: lan
|
||||||
|
# Gateway port
|
||||||
|
port: 18789
|
||||||
|
# Allow unconfigured startup (creates minimal config)
|
||||||
|
allowUnconfigured: false
|
||||||
|
# Additional CLI arguments
|
||||||
|
extraArgs: []
|
||||||
|
|
||||||
|
# Environment variables (non-sensitive)
|
||||||
|
env:
|
||||||
|
NODE_ENV: production
|
||||||
|
CLAWDBOT_STATE_DIR: /home/node/.openclaw
|
||||||
|
CLAWDBOT_WORKSPACE_DIR: /home/node/clawd
|
||||||
|
NODE_OPTIONS: "--max-old-space-size=2048"
|
||||||
|
|
||||||
|
# Secrets configuration
|
||||||
|
secrets:
|
||||||
|
# Create secret from values (dev/testing only - use existingSecret in production)
|
||||||
|
create: true
|
||||||
|
# Use existing secret (production)
|
||||||
|
existingSecret: ""
|
||||||
|
# Secret data (only used if create is true)
|
||||||
|
data:
|
||||||
|
anthropicApiKey: ""
|
||||||
|
openaiApiKey: ""
|
||||||
|
discordBotToken: ""
|
||||||
|
telegramBotToken: ""
|
||||||
|
gatewayToken: "" # Auto-generated if empty
|
||||||
|
|
||||||
|
# Config file (openclaw.json)
|
||||||
|
config:
|
||||||
|
# Create ConfigMap from inline config
|
||||||
|
create: true
|
||||||
|
# Use existing ConfigMap
|
||||||
|
existingConfigMap: ""
|
||||||
|
# Config data (JSON5 format)
|
||||||
|
data:
|
||||||
|
agents:
|
||||||
|
defaults:
|
||||||
|
model:
|
||||||
|
primary: "anthropic/claude-opus-4-5"
|
||||||
|
fallbacks:
|
||||||
|
- "anthropic/claude-sonnet-4-5"
|
||||||
|
- "openai/gpt-4o"
|
||||||
|
maxConcurrent: 4
|
||||||
|
sandbox:
|
||||||
|
mode: "off" # Disable Docker-in-Docker for Kubernetes
|
||||||
|
list:
|
||||||
|
- id: main
|
||||||
|
default: true
|
||||||
|
auth:
|
||||||
|
profiles:
|
||||||
|
"anthropic:default":
|
||||||
|
mode: token
|
||||||
|
provider: anthropic
|
||||||
|
"openai:default":
|
||||||
|
mode: token
|
||||||
|
provider: openai
|
||||||
|
gateway:
|
||||||
|
mode: local
|
||||||
|
bind: auto
|
||||||
|
auth:
|
||||||
|
mode: token
|
||||||
|
controlUi:
|
||||||
|
enabled: true
|
||||||
|
channels: {}
|
||||||
|
|
||||||
|
# Persistence
|
||||||
|
persistence:
|
||||||
|
enabled: true
|
||||||
|
# Storage class (use cluster default if empty)
|
||||||
|
storageClass: ""
|
||||||
|
# Access mode
|
||||||
|
accessMode: ReadWriteOnce
|
||||||
|
# Volume size
|
||||||
|
size: 10Gi
|
||||||
|
# Annotations
|
||||||
|
annotations: {}
|
||||||
|
# Selector
|
||||||
|
selector: {}
|
||||||
|
|
||||||
|
# Service configuration
|
||||||
|
service:
|
||||||
|
type: ClusterIP
|
||||||
|
port: 18789
|
||||||
|
annotations: {}
|
||||||
|
|
||||||
|
# Ingress configuration
|
||||||
|
ingress:
|
||||||
|
enabled: false
|
||||||
|
className: nginx
|
||||||
|
|
||||||
|
# Simplified domain configuration (recommended)
|
||||||
|
# Set your domain here - TLS will be auto-configured if tls.enabled is true
|
||||||
|
domain: "" # e.g., "openclaw.yourdomain.com"
|
||||||
|
|
||||||
|
# TLS configuration
|
||||||
|
tls:
|
||||||
|
enabled: false
|
||||||
|
# Secret name for TLS certificate (auto-generated name if empty)
|
||||||
|
secretName: ""
|
||||||
|
# Use cert-manager for automatic certificate provisioning
|
||||||
|
certManager:
|
||||||
|
enabled: false
|
||||||
|
issuer: "letsencrypt-prod"
|
||||||
|
|
||||||
|
# Additional annotations
|
||||||
|
annotations: {}
|
||||||
|
# WebSocket support (recommended for real-time features):
|
||||||
|
# nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
|
||||||
|
# nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
|
||||||
|
|
||||||
|
# Advanced: Manual host configuration (overrides domain if set)
|
||||||
|
hosts: []
|
||||||
|
# - host: openclaw.example.com
|
||||||
|
# paths:
|
||||||
|
# - path: /
|
||||||
|
# pathType: Prefix
|
||||||
|
|
||||||
|
# Resource limits/requests
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
memory: 2Gi
|
||||||
|
cpu: 1000m
|
||||||
|
requests:
|
||||||
|
memory: 512Mi
|
||||||
|
cpu: 250m
|
||||||
|
|
||||||
|
# Node selector
|
||||||
|
nodeSelector: {}
|
||||||
|
|
||||||
|
# Tolerations
|
||||||
|
tolerations: []
|
||||||
|
|
||||||
|
# Affinity rules
|
||||||
|
affinity: {}
|
||||||
|
|
||||||
|
# Init containers (for setup tasks)
|
||||||
|
initContainers: []
|
||||||
|
|
||||||
|
# Extra containers (sidecars)
|
||||||
|
extraContainers: []
|
||||||
|
|
||||||
|
# Lifecycle hooks
|
||||||
|
lifecycle:
|
||||||
|
preStop:
|
||||||
|
exec:
|
||||||
|
command:
|
||||||
|
- sh
|
||||||
|
- -c
|
||||||
|
- rm -f /home/node/.openclaw/gateway.*.lock; sleep 10
|
||||||
|
|
||||||
|
# Probes
|
||||||
|
# Use tcpSocket for startup/readiness (lightweight), exec for liveness (thorough)
|
||||||
|
livenessProbe:
|
||||||
|
enabled: true
|
||||||
|
exec:
|
||||||
|
command:
|
||||||
|
- node
|
||||||
|
- dist/index.js
|
||||||
|
- health
|
||||||
|
initialDelaySeconds: 60
|
||||||
|
periodSeconds: 60
|
||||||
|
timeoutSeconds: 30
|
||||||
|
failureThreshold: 3
|
||||||
|
|
||||||
|
readinessProbe:
|
||||||
|
enabled: true
|
||||||
|
tcpSocket:
|
||||||
|
port: 18789
|
||||||
|
initialDelaySeconds: 10
|
||||||
|
periodSeconds: 5
|
||||||
|
timeoutSeconds: 3
|
||||||
|
failureThreshold: 3
|
||||||
|
|
||||||
|
startupProbe:
|
||||||
|
enabled: true
|
||||||
|
tcpSocket:
|
||||||
|
port: 18789
|
||||||
|
initialDelaySeconds: 10
|
||||||
|
periodSeconds: 5
|
||||||
|
timeoutSeconds: 3
|
||||||
|
failureThreshold: 30 # 150 seconds max startup time
|
||||||
Loading…
Reference in New Issue