feat: add Helm chart for Kubernetes deployment (#280)

* feat: add Helm chart for Kubernetes deployment

Comprehensive Helm chart for deploying Huly to Kubernetes:
- All core services (front, account, transactor, collaborator, etc.)
- Infrastructure (CockroachDB, Redpanda, Elasticsearch, MinIO)
- Optional AI bot with MongoDB
- Automated backup CronJobs to S3-compatible storage
- Configurable image registry and version pinning
- Auto-generated secrets with persistence across upgrades
- NGINX ingress with TLS via cert-manager
- CI workflow for lint, template validation, and OCI publish

Chart lives at helm/ (outside kube/) to avoid conflict with the
existing kubectl-based CI that recursively applies kube/*.yaml.

Signed-off-by: Daniel Kendall <dkendall@ledoweb.com>

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Don Kendall <kendall@donkendall.com>

* feat(helm): add optional GitHub integration service

Deploy pod-github for bidirectional sync of issues, PRs, and comments
between Huly and GitHub. Gated behind githubIntegration.enabled (false
by default).

Adds:
- templates/github/deployment.yaml + service.yaml
- GITHUB_URL to configmap and front deployment
- GitHub App credentials to shared secret
- /_github ingress backend
- README documentation for GitHub integration and AI bot

Signed-off-by: Daniel Kendall <dkendall@ledoweb.com>

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Don Kendall <kendall@donkendall.com>

---------

Signed-off-by: Don Kendall <kendall@donkendall.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Don Kendall
2026-03-21 13:38:08 +07:00
committed by GitHub
co-authored by Claude Opus 4.6
parent 8e8dce93b7
commit 5dc03d01aa
50 changed files with 2860 additions and 0 deletions
+52
View File
@@ -0,0 +1,52 @@
name: Helm Chart
on:
push:
branches: [main]
paths:
- "helm/**"
pull_request:
paths:
- "helm/**"
permissions:
contents: read
packages: write
env:
CHART_PATH: helm/huly
OCI_REGISTRY: oci://ghcr.io/${{ github.repository_owner }}/charts
jobs:
lint:
name: Lint & Template
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: azure/setup-helm@v4
- run: helm lint ${{ env.CHART_PATH }}
- run: helm template huly ${{ env.CHART_PATH }} --set domain=ci.example.com
publish:
name: Package & Push OCI
needs: lint
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: azure/setup-helm@v4
- name: Extract chart version
id: chart
run: |
version=$(grep '^version:' ${{ env.CHART_PATH }}/Chart.yaml | awk '{print $2}')
echo "version=$version" >> "$GITHUB_OUTPUT"
- name: Login to GHCR
run: echo "${{ secrets.GITHUB_TOKEN }}" | helm registry login ghcr.io -u ${{ github.actor }} --password-stdin
- name: Package chart
run: helm package ${{ env.CHART_PATH }} -d /tmp/charts
- name: Push to OCI registry
run: helm push /tmp/charts/huly-${{ steps.chart.outputs.version }}.tgz ${{ env.OCI_REGISTRY }}
+1
View File
@@ -0,0 +1 @@
values-ledoweb.yaml
+13
View File
@@ -0,0 +1,13 @@
apiVersion: v2
name: huly
description: Huly — open-source project management platform
type: application
version: 0.1.0
appVersion: "0.7.382"
home: https://huly.io
sources:
- https://github.com/hcengineering/huly-selfhost
keywords:
- huly
- project-management
- collaboration
+393
View File
@@ -0,0 +1,393 @@
# Huly Helm Chart
Deploy [Huly](https://huly.io) — an open-source project management platform — on Kubernetes with a single command.
## Prerequisites
- Kubernetes 1.25+
- Helm 3.10+
- [NGINX Ingress Controller](https://kubernetes.github.io/ingress-nginx/)
- [cert-manager](https://cert-manager.io/) (if using TLS)
## Quick Start
```bash
helm install huly ./helm/huly \
--set domain=huly.mysite.com
```
All secrets (server secret, CockroachDB password, MinIO credentials) are auto-generated on first install and preserved across `helm upgrade`.
## Authentication
At least one auth provider should be configured. Without one, users cannot sign in.
### Google OAuth
```bash
helm install huly ./helm/huly \
--set domain=huly.mysite.com \
--set auth.google.clientId=YOUR_CLIENT_ID \
--set auth.google.clientSecret=YOUR_CLIENT_SECRET
```
### GitHub OAuth
```bash
helm install huly ./helm/huly \
--set domain=huly.mysite.com \
--set auth.github.clientId=YOUR_CLIENT_ID \
--set auth.github.clientSecret=YOUR_CLIENT_SECRET
```
### OpenID Connect (OIDC)
```bash
helm install huly ./helm/huly \
--set domain=huly.mysite.com \
--set auth.oidc.clientId=YOUR_CLIENT_ID \
--set auth.oidc.clientSecret=YOUR_CLIENT_SECRET \
--set auth.oidc.issuer=https://accounts.google.com
```
### Disable Public Signup
```bash
--set auth.disableSignup=true
```
## External S3 Storage
By default, the chart deploys a built-in MinIO instance. To use external S3-compatible storage instead:
```bash
helm install huly ./helm/huly \
--set domain=huly.mysite.com \
--set storage.type=s3 \
--set storage.s3.endpoint=https://s3.amazonaws.com \
--set storage.s3.region=us-east-1 \
--set storage.s3.accessKey=YOUR_ACCESS_KEY \
--set storage.s3.secretKey=YOUR_SECRET_KEY \
--set storage.s3.rootBucket=huly-data
```
Setting `storage.type=s3` automatically disables the built-in MinIO deployment and PVC.
**Bucket modes:**
- `rootBucket` — all workspaces share one bucket, isolated by workspace-ID prefix (recommended)
- `bucketPrefix` — each workspace gets its own bucket, prefixed with this string
## External Infrastructure
Each built-in infra service (CockroachDB, Redpanda, Elasticsearch) can be replaced with an external instance.
### External CockroachDB / PostgreSQL
```bash
helm install huly ./helm/huly \
--set domain=huly.mysite.com \
--set cockroach.enabled=false \
--set secrets.crDbUrl='postgres://user:pass@db.example.com:26257/huly'
```
### External Redpanda / Kafka
```bash
--set redpanda.enabled=false \
--set external.redpanda=kafka.example.com:9092
```
### External Elasticsearch
```bash
--set elastic.enabled=false \
--set external.elastic=https://es.example.com:9200
```
## GitHub Integration
Bidirectional sync of issues, PRs, and comments between Huly and GitHub. Requires a [GitHub App](https://docs.github.com/en/apps/creating-github-apps).
```bash
helm install huly ./helm/huly \
--set domain=huly.mysite.com \
--set githubIntegration.enabled=true \
--set githubIntegration.appId=123456 \
--set githubIntegration.clientId=Iv1.abc123 \
--set githubIntegration.clientSecret=YOUR_SECRET \
--set-file githubIntegration.privateKey=path/to/private-key.pem \
--set githubIntegration.webhookSecret=YOUR_WEBHOOK_SECRET \
--set githubIntegration.botName="your-app-name[bot]"
```
**GitHub App settings:**
| Field | Value |
|-------|-------|
| Callback URL | `https://<domain>/github` |
| Setup URL | `https://<domain>/github?op=installation` |
| Webhook URL | `https://<domain>/_github/api/webhook` |
| Permissions | Issues R/W, PRs R/W, Contents R, Metadata R |
| Events | Issues, Issue comment, Pull request, PR review, PR review comment, PR review thread |
## AI Bot
Optional AI assistant powered by OpenAI. Requires an OpenAI API key.
```bash
helm install huly ./helm/huly \
--set domain=huly.mysite.com \
--set aibot.enabled=true \
--set secrets.openaiApiKey=sk-...
```
## Admin Configuration
```bash
--set appSettings.adminEmails="admin@example.com,ops@example.com"
```
## Upgrading
### Version bumps
Update `hulyVersion` to the desired release tag:
```bash
helm upgrade huly ./helm/huly \
--reuse-values \
--set hulyVersion=v0.7.400
```
All app service pods restart automatically (via checksum annotations) when the chart version or config changes. Infrastructure services (CockroachDB, Redpanda, Elasticsearch) are **not** restarted on config changes to avoid data-layer disruption.
### From MinIO to S3
When switching storage backends, set the new storage type explicitly — it overrides any previously persisted config:
```bash
helm upgrade huly ./helm/huly \
--reuse-values \
--set storage.type=s3 \
--set storage.s3.endpoint=https://s3.example.com \
--set storage.s3.region=us-east-1 \
--set storage.s3.accessKey=KEY \
--set storage.s3.secretKey=SECRET \
--set storage.s3.rootBucket=huly-data
```
After confirming S3 works, clean up the orphaned MinIO PVC:
```bash
kubectl delete pvc minio-data -n <namespace>
```
## Troubleshooting
### Services fail to start with connection errors
App services (account, transactor, workspace, fulltext) depend on CockroachDB and Redpanda. The chart includes init containers that wait for these services to accept connections before starting. If you see `ECONNREFUSED` or `ENOTFOUND` errors in logs, restart the affected deployments:
```bash
kubectl rollout restart deployment/account deployment/transactor \
deployment/workspace deployment/fulltext -n <namespace>
```
### CockroachDB user/password issues
When running CockroachDB with `--insecure` (the default), authentication is disabled. The `COCKROACH_PASSWORD` env var creates the user during init but is not enforced for connections. If using the `root` user, set:
```bash
--set cockroach.username=root \
--set secrets.crDbUrl='postgres://root@cockroach:26257/defaultdb?sslmode=disable'
```
### Checking pod health
```bash
kubectl get pods -l app.kubernetes.io/part-of=huly -n <namespace>
kubectl logs deployment/<service> -n <namespace> --tail=20
```
## Values Reference
### Required
| Key | Description | Default |
|-----|-------------|---------|
| `domain` | Your Huly domain (e.g. `huly.mysite.com`) | `huly.example` |
### Authentication
| Key | Description | Default |
|-----|-------------|---------|
| `auth.google.clientId` | Google OAuth client ID | `""` |
| `auth.google.clientSecret` | Google OAuth client secret | `""` |
| `auth.github.clientId` | GitHub OAuth client ID | `""` |
| `auth.github.clientSecret` | GitHub OAuth client secret | `""` |
| `auth.oidc.clientId` | OIDC client ID | `""` |
| `auth.oidc.clientSecret` | OIDC client secret | `""` |
| `auth.oidc.issuer` | OIDC issuer URL | `""` |
| `auth.disableSignup` | Prevent new user registration | `false` |
### Storage
| Key | Description | Default |
|-----|-------------|---------|
| `storage.type` | `minio` (built-in) or `s3` (external) | `minio` |
| `storage.s3.endpoint` | S3 endpoint URL | `""` |
| `storage.s3.region` | S3 region | `""` |
| `storage.s3.accessKey` | S3 access key | `""` |
| `storage.s3.secretKey` | S3 secret key | `""` |
| `storage.s3.rootBucket` | Single bucket for all workspaces (prefixed by workspace ID) | `""` |
| `storage.s3.bucketPrefix` | Prefix for per-workspace bucket names (if rootBucket empty) | `""` |
### Secrets
All secrets are auto-generated if left empty. They persist across `helm upgrade` via Kubernetes secret lookup.
| Key | Description | Default |
|-----|-------------|---------|
| `secrets.serverSecret` | Shared JWT signing secret | auto |
| `secrets.storageConfig` | Full storage connection string override | auto |
| `secrets.cockroachPassword` | CockroachDB password | auto |
| `secrets.redpandaPassword` | Redpanda superuser password | auto |
| `secrets.crDbUrl` | CockroachDB connection URL | auto |
### Ingress
| Key | Description | Default |
|-----|-------------|---------|
| `ingress.enabled` | Enable ingress resources | `true` |
| `ingress.className` | Ingress class | `nginx` |
| `ingress.annotations` | Extra annotations for all ingress resources | `{}` |
| `ingress.tls.enabled` | Enable TLS via cert-manager | `true` |
| `ingress.tls.clusterIssuer` | cert-manager ClusterIssuer name | `letsencrypt-prod` |
### App Settings
| Key | Description | Default |
|-----|-------------|---------|
| `appSettings.title` | Browser title | `Huly Self Host` |
| `appSettings.defaultLanguage` | Default UI language | `en` |
| `appSettings.lastNameFirst` | Display last name first | `true` |
| `appSettings.modelEnabled` | Enabled platform models | `*` |
| `appSettings.adminEmails` | Comma-separated admin emails | `""` |
| `appSettings.desktopChannel` | Desktop update channel | `selfhost` |
### Infrastructure
Each infra service can be disabled to use an external instance. When disabled, provide connection details via the corresponding external/secret keys.
| Key | Description | Default |
|-----|-------------|---------|
| `cockroach.enabled` | Deploy built-in CockroachDB | `true` |
| `cockroach.image` | CockroachDB image | `cockroachdb/cockroach:latest-v24.2` |
| `cockroach.storage` | Data PVC size | `10Gi` |
| `cockroach.storageClassName` | PVC storage class (empty = cluster default) | `""` |
| `cockroach.database` | Database name | `defaultdb` |
| `cockroach.username` | Database user | `selfhost` |
| `redpanda.enabled` | Deploy built-in Redpanda | `true` |
| `redpanda.image` | Redpanda image | `docker.redpanda.com/...` |
| `redpanda.storage` | Data PVC size | `5Gi` |
| `redpanda.storageClassName` | PVC storage class | `""` |
| `elastic.enabled` | Deploy built-in Elasticsearch | `true` |
| `elastic.image` | Elasticsearch image | `elasticsearch:7.14.2` |
| `elastic.storage` | Data PVC size | `10Gi` |
| `elastic.storageClassName` | PVC storage class | `""` |
| `elastic.javaOpts` | JVM heap options | `-Xms1024m -Xmx1024m` |
| `minio.enabled` | Deploy built-in MinIO | `true` |
| `minio.image` | MinIO image | `minio/minio` |
| `minio.storage` | Data PVC size | `50Gi` |
| `minio.storageClassName` | PVC storage class | `""` |
### External Infrastructure
| Key | Description | Default |
|-----|-------------|---------|
| `external.redpanda` | Kafka-compatible broker address (when `redpanda.enabled=false`) | `""` |
| `external.elastic` | Elasticsearch URL (when `elastic.enabled=false`) | `""` |
### Application Services
All app services share these overridable keys: `<svc>.replicas`, `<svc>.resources`.
| Key | Description | Default |
|-----|-------------|---------|
| `hulyVersion` | Image tag for all Huly services | `v0.7.382` |
| `kvs.enabled` | Deploy KVS (key-value store) service | `true` |
### GitHub Integration
| Key | Description | Default |
|-----|-------------|---------|
| `githubIntegration.enabled` | Deploy GitHub integration service | `false` |
| `githubIntegration.replicas` | Replica count | `1` |
| `githubIntegration.botName` | Bot display name (must match GitHub App slug + `[bot]`) | `""` |
| `githubIntegration.appId` | GitHub App ID | `""` |
| `githubIntegration.clientId` | GitHub App Client ID | `""` |
| `githubIntegration.clientSecret` | GitHub App Client Secret | `""` |
| `githubIntegration.privateKey` | GitHub App Private Key (PEM) | `""` |
| `githubIntegration.webhookSecret` | GitHub App Webhook Secret | `""` |
### AI Bot
| Key | Description | Default |
|-----|-------------|---------|
| `aibot.enabled` | Deploy AI bot service | `false` |
| `aibot.replicas` | Replica count | `1` |
| `aibot.firstName` | Bot display first name | `Huly` |
| `aibot.lastName` | Bot display last name | `AI` |
| `secrets.openaiApiKey` | OpenAI API key (required when enabled) | `""` |
| `secrets.openaiBaseUrl` | OpenAI API base URL override | `""` |
### Global Pod Settings
| Key | Description | Default |
|-----|-------------|---------|
| `global.nodeSelector` | Node selector for all pods | `{}` |
| `global.tolerations` | Tolerations for all pods | `[]` |
| `global.affinity` | Affinity rules for all pods | `{}` |
## Architecture
The chart deploys 13+ services (optional services marked with *):
| Service | Port | Description |
|---------|------|-------------|
| **cockroach** | 26257 | SQL database (CockroachDB) |
| **redpanda** | 9092 | Message queue (Kafka-compatible) |
| **elastic** | 9200 | Full-text search (Elasticsearch) |
| **minio** | 9000 | Object storage (S3-compatible) |
| **front** | 8080 | Web UI |
| **account** | 3000 | Authentication & user management |
| **transactor** | 3333 | Core transaction engine |
| **collaborator** | 3078 | Real-time collaboration (WebSocket) |
| **workspace** | — | Workspace lifecycle (background worker) |
| **fulltext** | 4700 | Search indexing |
| **rekoni** | 4004 | Content intelligence |
| **stats** | 4900 | Metrics collection |
| **kvs** | 8094 | Key-value store |
| **github*** | 3500 | GitHub integration (bidirectional sync) |
| **aibot*** | 4010 | AI assistant (requires OpenAI key) |
| **mongodb*** | 27017 | Document database (for aibot) |
All services are exposed under a single domain via path-based NGINX ingress routing:
- `/` → front
- `/_accounts` → account
- `/_transactor` → transactor (WebSocket)
- `/_collaborator` → collaborator (WebSocket)
- `/_rekoni` → rekoni
- `/_stats` → stats
- `/_github` → github *
- `/_aibot` → aibot *
## CI / OCI Registry
On merge to `main`, the GitHub Actions workflow packages and pushes the chart to GHCR:
```bash
helm install huly oci://ghcr.io/hcengineering/charts/huly \
--version 0.1.0 \
--set domain=huly.mysite.com
```
+19
View File
@@ -0,0 +1,19 @@
Huly has been installed!
{{- $proto := ternary "https" "http" (and .Values.ingress.enabled .Values.ingress.tls.enabled) }}
Your Huly instance will be available at:
{{ $proto }}://{{ .Values.domain }}
{{- if .Values.ingress.enabled }}
Ingress is enabled with class "{{ .Values.ingress.className }}".
{{- if .Values.ingress.tls.enabled }}
TLS is enabled via cluster-issuer "{{ .Values.ingress.tls.clusterIssuer }}".
{{- end }}
{{- end }}
To check the rollout status:
kubectl get pods -l app.kubernetes.io/part-of=huly -n {{ .Release.Namespace }}
+156
View File
@@ -0,0 +1,156 @@
{{/*
Expand the name of the chart.
*/}}
{{- define "huly.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Create a default fully qualified app name.
*/}}
{{- define "huly.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 }}
{{/*
Secret resource name.
*/}}
{{- define "huly.secretName" -}}
{{- printf "%s-secret" (include "huly.fullname" .) }}
{{- end }}
{{/*
ConfigMap resource name.
*/}}
{{- define "huly.configName" -}}
{{- printf "%s-config" (include "huly.fullname" .) }}
{{- end }}
{{/*
Common labels applied to every resource.
*/}}
{{- define "huly.labels" -}}
helm.sh/chart: {{ printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
app.kubernetes.io/part-of: huly
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
{{/*
Global scheduling (nodeSelector, tolerations, affinity).
*/}}
{{- define "huly.scheduling" -}}
{{- with .Values.global.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 2 }}
{{- end }}
{{- with .Values.global.tolerations }}
tolerations:
{{- toYaml . | nindent 2 }}
{{- end }}
{{- with .Values.global.affinity }}
affinity:
{{- toYaml . | nindent 2 }}
{{- end }}
{{- end }}
{{/*
Effective MinIO enabled (disabled if storage.type == s3).
*/}}
{{- define "huly.minioEnabled" -}}
{{- if and .Values.minio.enabled (eq .Values.storage.type "minio") }}true{{- else }}false{{- end }}
{{- end }}
{{/*
Checksum annotations triggers pod restart when secret/configmap changes.
Usage: {{- include "huly.checksumAnnotations" . | nindent 8 }}
*/}}
{{- define "huly.checksumAnnotations" -}}
checksum/secret: {{ include (print $.Template.BasePath "/secret.yaml") . | sha256sum }}
checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
{{- end }}
{{/*
Init container that waits for CockroachDB to accept connections.
*/}}
{{- define "huly.waitForCockroach" -}}
{{- if .Values.cockroach.enabled }}
- name: wait-cockroach
image: busybox:1.36
command: ['sh', '-c', 'until nc -z cockroach 26257; do echo "waiting for cockroach..."; sleep 2; done']
{{- end }}
{{- end }}
{{/*
Init container that waits for MongoDB to accept connections.
*/}}
{{- define "huly.waitForMongodb" -}}
{{- if .Values.mongodb.enabled }}
- name: wait-mongodb
image: busybox:1.36
command: ['sh', '-c', 'until nc -z mongodb 27017; do echo "waiting for mongodb..."; sleep 2; done']
{{- end }}
{{- end }}
{{/*
Init container that waits for Redpanda to accept connections.
*/}}
{{- define "huly.waitForRedpanda" -}}
{{- if .Values.redpanda.enabled }}
- name: wait-redpanda
image: busybox:1.36
command: ['sh', '-c', 'until nc -z redpanda 9092; do echo "waiting for redpanda..."; sleep 2; done']
{{- end }}
{{- end }}
{{/*
Backup secret resource name.
*/}}
{{- define "huly.backupSecretName" -}}
{{- printf "%s-backup-secret" (include "huly.fullname" .) }}
{{- end }}
{{/*
Env var from backup Secret helper.
Usage: {{- include "huly.envBackupSecret" (dict "name" "BACKUP_S3_ENDPOINT" "key" "BACKUP_S3_ENDPOINT" "root" .) }}
*/}}
{{- define "huly.envBackupSecret" -}}
- name: {{ .name }}
valueFrom:
secretKeyRef:
name: {{ include "huly.backupSecretName" .root }}
key: {{ .key }}
{{- end }}
{{/*
Env var from Secret helper reduces boilerplate.
Usage: {{- include "huly.envSecret" (dict "name" "SERVER_SECRET" "key" "SERVER_SECRET" "root" .) }}
*/}}
{{- define "huly.envSecret" -}}
- name: {{ .name }}
valueFrom:
secretKeyRef:
name: {{ include "huly.secretName" .root }}
key: {{ .key }}
{{- end }}
{{/*
Env var from ConfigMap helper reduces boilerplate.
Usage: {{- include "huly.envConfig" (dict "name" "FRONT_URL" "key" "FRONT_URL" "root" .) }}
*/}}
{{- define "huly.envConfig" -}}
- name: {{ .name }}
valueFrom:
configMapKeyRef:
name: {{ include "huly.configName" .root }}
key: {{ .key }}
{{- end }}
@@ -0,0 +1,75 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: account
labels:
{{- include "huly.labels" . | nindent 4 }}
app: account
spec:
replicas: {{ .Values.account.replicas }}
selector:
matchLabels:
app: account
template:
metadata:
annotations:
{{- include "huly.checksumAnnotations" . | nindent 8 }}
labels:
{{- include "huly.labels" . | nindent 8 }}
app: account
spec:
{{- include "huly.scheduling" . | nindent 6 }}
initContainers:
{{- include "huly.waitForCockroach" . | nindent 8 }}
{{- include "huly.waitForRedpanda" . | nindent 8 }}
containers:
- name: account
image: {{ .Values.hulyRegistry }}/account:{{ .Values.hulyVersion }}
ports:
- name: http
containerPort: 3000
env:
- name: ACCOUNT_PORT
value: "3000"
- name: SERVER_PORT
value: "3000"
{{- include "huly.envSecret" (dict "name" "SERVER_SECRET" "key" "SERVER_SECRET" "root" .) | nindent 12 }}
{{- include "huly.envSecret" (dict "name" "DB_URL" "key" "CR_DB_URL" "root" .) | nindent 12 }}
{{- include "huly.envSecret" (dict "name" "STORAGE_CONFIG" "key" "STORAGE_CONFIG" "root" .) | nindent 12 }}
{{- include "huly.envConfig" (dict "name" "ACCOUNTS_URL" "key" "ACCOUNTS_URL" "root" .) | nindent 12 }}
{{- include "huly.envConfig" (dict "name" "FRONT_URL" "key" "FRONT_URL" "root" .) | nindent 12 }}
{{- include "huly.envConfig" (dict "name" "STATS_URL" "key" "STATS_URL_INTERNAL" "root" .) | nindent 12 }}
{{- include "huly.envConfig" (dict "name" "MODEL_ENABLED" "key" "MODEL_ENABLED" "root" .) | nindent 12 }}
{{- include "huly.envConfig" (dict "name" "TRANSACTOR_URL" "key" "TRANSACTOR_URL" "root" .) | nindent 12 }}
{{- include "huly.envConfig" (dict "name" "QUEUE_CONFIG" "key" "QUEUE_CONFIG" "root" .) | nindent 12 }}
{{- if .Values.auth.disableSignup }}
- name: DISABLE_SIGNUP
value: "true"
{{- end }}
{{- if .Values.auth.google.clientId }}
{{- include "huly.envSecret" (dict "name" "GOOGLE_CLIENT_ID" "key" "GOOGLE_CLIENT_ID" "root" .) | nindent 12 }}
{{- include "huly.envSecret" (dict "name" "GOOGLE_CLIENT_SECRET" "key" "GOOGLE_CLIENT_SECRET" "root" .) | nindent 12 }}
{{- end }}
{{- if .Values.auth.github.clientId }}
{{- include "huly.envSecret" (dict "name" "GITHUB_CLIENT_ID" "key" "GITHUB_CLIENT_ID" "root" .) | nindent 12 }}
{{- include "huly.envSecret" (dict "name" "GITHUB_CLIENT_SECRET" "key" "GITHUB_CLIENT_SECRET" "root" .) | nindent 12 }}
{{- end }}
{{- if .Values.auth.oidc.clientId }}
{{- include "huly.envSecret" (dict "name" "OPENID_CLIENT_ID" "key" "OPENID_CLIENT_ID" "root" .) | nindent 12 }}
{{- include "huly.envSecret" (dict "name" "OPENID_CLIENT_SECRET" "key" "OPENID_CLIENT_SECRET" "root" .) | nindent 12 }}
{{- include "huly.envSecret" (dict "name" "OPENID_ISSUER" "key" "OPENID_ISSUER" "root" .) | nindent 12 }}
{{- end }}
readinessProbe:
tcpSocket:
port: 3000
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
tcpSocket:
port: 3000
initialDelaySeconds: 30
periodSeconds: 30
{{- with .Values.account.resources }}
resources:
{{- toYaml . | nindent 12 }}
{{- end }}
+14
View File
@@ -0,0 +1,14 @@
apiVersion: v1
kind: Service
metadata:
name: account
labels:
{{- include "huly.labels" . | nindent 4 }}
app: account
spec:
selector:
app: account
ports:
- name: http
port: 3000
targetPort: 3000
+82
View File
@@ -0,0 +1,82 @@
{{- if .Values.aibot.enabled }}
apiVersion: apps/v1
kind: Deployment
metadata:
name: aibot
labels:
{{- include "huly.labels" . | nindent 4 }}
app: aibot
spec:
replicas: {{ .Values.aibot.replicas }}
selector:
matchLabels:
app: aibot
template:
metadata:
annotations:
{{- include "huly.checksumAnnotations" . | nindent 8 }}
labels:
{{- include "huly.labels" . | nindent 8 }}
app: aibot
spec:
{{- include "huly.scheduling" . | nindent 6 }}
initContainers:
{{- include "huly.waitForCockroach" . | nindent 8 }}
{{- include "huly.waitForMongodb" . | nindent 8 }}
containers:
- name: aibot
image: {{ .Values.hulyRegistry }}/ai-bot:{{ .Values.hulyVersion }}
ports:
- name: http
containerPort: 4010
env:
- name: SERVER_PORT
value: "4010"
- name: FIRST_NAME
value: {{ .Values.aibot.firstName | quote }}
- name: LAST_NAME
value: {{ .Values.aibot.lastName | quote }}
{{- include "huly.envSecret" (dict "name" "SERVER_SECRET" "key" "SERVER_SECRET" "root" .) | nindent 12 }}
{{- include "huly.envSecret" (dict "name" "STORAGE_CONFIG" "key" "STORAGE_CONFIG" "root" .) | nindent 12 }}
{{- include "huly.envSecret" (dict "name" "DB_URL" "key" "CR_DB_URL" "root" .) | nindent 12 }}
{{- include "huly.envSecret" (dict "name" "OPENAI_API_KEY" "key" "OPENAI_API_KEY" "root" .) | nindent 12 }}
{{- include "huly.envSecret" (dict "name" "PASSWORD" "key" "AIBOT_PASSWORD" "root" .) | nindent 12 }}
{{- if .Values.secrets.openaiBaseUrl }}
{{- include "huly.envSecret" (dict "name" "OPENAI_BASE_URL" "key" "OPENAI_BASE_URL" "root" .) | nindent 12 }}
{{- end }}
{{- include "huly.envConfig" (dict "name" "ACCOUNTS_URL" "key" "ACCOUNTS_URL_INTERNAL" "root" .) | nindent 12 }}
{{- include "huly.envConfig" (dict "name" "MONGO_URL" "key" "MONGO_URL" "root" .) | nindent 12 }}
{{- include "huly.envConfig" (dict "name" "STATS_URL" "key" "STATS_URL_INTERNAL" "root" .) | nindent 12 }}
{{- include "huly.envConfig" (dict "name" "QUEUE_CONFIG" "key" "QUEUE_CONFIG" "root" .) | nindent 12 }}
{{- include "huly.envConfig" (dict "name" "LOVE_ENDPOINT" "key" "LOVE_ENDPOINT" "root" .) | nindent 12 }}
{{- with .Values.aibot.openaiModel }}
- name: OPENAI_MODEL
value: {{ . | quote }}
{{- end }}
{{- with .Values.aibot.openaiEmbeddingModel }}
- name: OPENAI_EMBEDDING_MODEL
value: {{ . | quote }}
{{- end }}
{{- with .Values.aibot.openaiTranslateModel }}
- name: AI_OPENAI_TRANSLATE_MODEL
value: {{ . | quote }}
{{- end }}
{{- with .Values.aibot.openaiSummaryModel }}
- name: AI_OPENAI_SUMMARY_MODEL
value: {{ . | quote }}
{{- end }}
readinessProbe:
tcpSocket:
port: 4010
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
tcpSocket:
port: 4010
initialDelaySeconds: 15
periodSeconds: 30
{{- with .Values.aibot.resources }}
resources:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- end }}
+16
View File
@@ -0,0 +1,16 @@
{{- if .Values.aibot.enabled }}
apiVersion: v1
kind: Service
metadata:
name: aibot
labels:
{{- include "huly.labels" . | nindent 4 }}
app: aibot
spec:
selector:
app: aibot
ports:
- name: http
port: 4010
targetPort: 4010
{{- end }}
@@ -0,0 +1,122 @@
{{- if and .Values.backup.enabled .Values.backup.cockroachdb.enabled .Values.cockroach.enabled }}
apiVersion: batch/v1
kind: CronJob
metadata:
name: {{ include "huly.fullname" . }}-backup-cockroachdb
labels:
{{- include "huly.labels" . | nindent 4 }}
app: backup-cockroachdb
spec:
schedule: {{ .Values.backup.cockroachdb.schedule | default .Values.backup.schedule | quote }}
concurrencyPolicy: Forbid
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 3
jobTemplate:
spec:
backoffLimit: 2
template:
metadata:
labels:
{{- include "huly.labels" . | nindent 12 }}
app: backup-cockroachdb
spec:
{{- include "huly.scheduling" . | nindent 10 }}
restartPolicy: OnFailure
volumes:
- name: backup
emptyDir: {}
initContainers:
- name: crdb-dump
image: cockroachdb/cockroach:latest-v24.2
volumeMounts:
- name: backup
mountPath: /backup
command:
- sh
- -c
- |
set -e
STAMP=$(date +%Y%m%d-%H%M%S)
DEST="/backup/cockroachdb_${STAMP}.sql.gz"
CRDB="cockroach sql --insecure --host=cockroach:26257 -d {{ .Values.cockroach.database }}"
echo "=== CockroachDB backup started at $(date -u) ==="
{
# 1. Schema DDL
echo "-- Schema dump"
$CRDB --format=raw -e "SHOW CREATE ALL TABLES"
# 2. Per-table data as CSV (COPY format)
TABLES=$($CRDB --format=csv -e "
SELECT schema_name || '.' || table_name
FROM [SHOW TABLES FROM {{ .Values.cockroach.database }}]
WHERE schema_name NOT IN ('crdb_internal','pg_catalog','pg_extension','information_schema')
ORDER BY schema_name, table_name
" | tail -n +2)
for tbl in $TABLES; do
ROW_COUNT=$($CRDB --format=csv -e "SELECT count(*) FROM ${tbl}" | tail -1)
if [ "$ROW_COUNT" -gt 0 ] 2>/dev/null; then
echo ""
echo "-- Data for ${tbl} (${ROW_COUNT} rows)"
$CRDB --format=raw -e "SELECT
'INSERT INTO ${tbl} (' ||
array_to_string(
(SELECT array_agg(column_name ORDER BY ordinal_position)
FROM information_schema.columns
WHERE table_schema = split_part('${tbl}', '.', 1)
AND table_name = split_part('${tbl}', '.', 2)),
', '
) || ') VALUES' AS header" 2>/dev/null || true
# Export as CSV, can be loaded with IMPORT INTO
echo "-- CSV data for IMPORT INTO ${tbl}:"
echo "COPY ${tbl} FROM stdin WITH CSV HEADER;"
$CRDB --format=csv -e "TABLE ${tbl}"
echo "\\."
fi
done
} | gzip > "$DEST"
SIZE=$(ls -lh "$DEST" | awk '{print $5}')
echo "=== Dump complete: ${DEST} (${SIZE}) ==="
containers:
- name: rclone-upload
image: {{ .Values.backup.rcloneImage }}
volumeMounts:
- name: backup
mountPath: /backup
env:
{{- include "huly.envBackupSecret" (dict "name" "BACKUP_S3_ENDPOINT" "key" "BACKUP_S3_ENDPOINT" "root" .) | nindent 16 }}
{{- include "huly.envBackupSecret" (dict "name" "BACKUP_S3_REGION" "key" "BACKUP_S3_REGION" "root" .) | nindent 16 }}
{{- include "huly.envBackupSecret" (dict "name" "BACKUP_S3_BUCKET" "key" "BACKUP_S3_BUCKET" "root" .) | nindent 16 }}
{{- include "huly.envBackupSecret" (dict "name" "BACKUP_S3_PATH_PREFIX" "key" "BACKUP_S3_PATH_PREFIX" "root" .) | nindent 16 }}
{{- include "huly.envBackupSecret" (dict "name" "BACKUP_S3_ACCESS_KEY" "key" "BACKUP_S3_ACCESS_KEY" "root" .) | nindent 16 }}
{{- include "huly.envBackupSecret" (dict "name" "BACKUP_S3_SECRET_KEY" "key" "BACKUP_S3_SECRET_KEY" "root" .) | nindent 16 }}
command:
- sh
- -c
- |
set -e
# Configure rclone remote
rclone config create backup s3 \
provider=Other \
env_auth=false \
access_key_id="$BACKUP_S3_ACCESS_KEY" \
secret_access_key="$BACKUP_S3_SECRET_KEY" \
endpoint="$BACKUP_S3_ENDPOINT" \
region="$BACKUP_S3_REGION" \
--non-interactive
REMOTE_PATH="backup:${BACKUP_S3_BUCKET}/${BACKUP_S3_PATH_PREFIX}/cockroachdb/"
echo "Uploading to ${REMOTE_PATH}..."
rclone copy /backup/ "$REMOTE_PATH" --include "*.sql.gz" -v
echo "Cleaning up backups older than {{ .Values.backup.retentionDays }} days..."
rclone delete "$REMOTE_PATH" --min-age {{ .Values.backup.retentionDays }}d -v
echo "Backup complete."
rclone ls "$REMOTE_PATH" | tail -5
{{- end }}
@@ -0,0 +1,107 @@
{{- if and .Values.backup.enabled .Values.backup.files.enabled }}
apiVersion: batch/v1
kind: CronJob
metadata:
name: {{ include "huly.fullname" . }}-backup-files
labels:
{{- include "huly.labels" . | nindent 4 }}
app: backup-files
spec:
schedule: {{ .Values.backup.files.schedule | default .Values.backup.schedule | quote }}
concurrencyPolicy: Forbid
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 3
jobTemplate:
spec:
backoffLimit: 2
template:
metadata:
labels:
{{- include "huly.labels" . | nindent 12 }}
app: backup-files
spec:
{{- include "huly.scheduling" . | nindent 10 }}
restartPolicy: OnFailure
containers:
- name: rclone-sync
image: {{ .Values.backup.rcloneImage }}
env:
{{- include "huly.envSecret" (dict "name" "STORAGE_CONFIG" "key" "STORAGE_CONFIG" "root" .) | nindent 16 }}
{{- include "huly.envBackupSecret" (dict "name" "BACKUP_S3_ENDPOINT" "key" "BACKUP_S3_ENDPOINT" "root" .) | nindent 16 }}
{{- include "huly.envBackupSecret" (dict "name" "BACKUP_S3_REGION" "key" "BACKUP_S3_REGION" "root" .) | nindent 16 }}
{{- include "huly.envBackupSecret" (dict "name" "BACKUP_S3_BUCKET" "key" "BACKUP_S3_BUCKET" "root" .) | nindent 16 }}
{{- include "huly.envBackupSecret" (dict "name" "BACKUP_S3_PATH_PREFIX" "key" "BACKUP_S3_PATH_PREFIX" "root" .) | nindent 16 }}
{{- include "huly.envBackupSecret" (dict "name" "BACKUP_S3_ACCESS_KEY" "key" "BACKUP_S3_ACCESS_KEY" "root" .) | nindent 16 }}
{{- include "huly.envBackupSecret" (dict "name" "BACKUP_S3_SECRET_KEY" "key" "BACKUP_S3_SECRET_KEY" "root" .) | nindent 16 }}
command:
- sh
- -c
- |
set -e
# Parse STORAGE_CONFIG to extract source S3 details.
# Format: s3|https://endpoint?accessKey=X&secretKey=Y&region=Z&rootBucket=B
# or: minio|minio?accessKey=X&secretKey=Y
PROTO=$(echo "$STORAGE_CONFIG" | cut -d'|' -f1)
REST=$(echo "$STORAGE_CONFIG" | cut -d'|' -f2)
if [ "$PROTO" = "minio" ]; then
# MinIO: host is "minio", creds in query string
SRC_ENDPOINT="http://minio:9000"
SRC_PROVIDER="Minio"
else
# External S3: endpoint is the URL before '?'
SRC_ENDPOINT=$(echo "$REST" | cut -d'?' -f1)
SRC_PROVIDER="Other"
fi
PARAMS=$(echo "$REST" | cut -d'?' -f2)
SRC_ACCESS_KEY=$(echo "$PARAMS" | tr '&' '\n' | grep '^accessKey=' | cut -d= -f2)
SRC_SECRET_KEY=$(echo "$PARAMS" | tr '&' '\n' | grep '^secretKey=' | cut -d= -f2)
SRC_REGION=$(echo "$PARAMS" | tr '&' '\n' | grep '^region=' | cut -d= -f2)
SRC_ROOT_BUCKET=$(echo "$PARAMS" | tr '&' '\n' | grep '^rootBucket=' | cut -d= -f2)
SRC_BUCKET_PREFIX=$(echo "$PARAMS" | tr '&' '\n' | grep '^bucketPrefix=' | cut -d= -f2)
# Configure source remote
rclone config create source s3 \
provider="$SRC_PROVIDER" \
env_auth=false \
access_key_id="$SRC_ACCESS_KEY" \
secret_access_key="$SRC_SECRET_KEY" \
endpoint="$SRC_ENDPOINT" \
region="${SRC_REGION:-us-east-1}" \
--non-interactive
# Configure backup remote
rclone config create backup s3 \
provider=Other \
env_auth=false \
access_key_id="$BACKUP_S3_ACCESS_KEY" \
secret_access_key="$BACKUP_S3_SECRET_KEY" \
endpoint="$BACKUP_S3_ENDPOINT" \
region="$BACKUP_S3_REGION" \
--non-interactive
# Determine source path
if [ -n "$SRC_ROOT_BUCKET" ]; then
SRC_PATH="source:${SRC_ROOT_BUCKET}"
elif [ -n "$SRC_BUCKET_PREFIX" ]; then
echo "Warning: bucketPrefix mode — syncing all buckets with prefix '${SRC_BUCKET_PREFIX}'"
SRC_PATH="source:${SRC_BUCKET_PREFIX}"
else
echo "Error: cannot determine source bucket from STORAGE_CONFIG"
exit 1
fi
DEST_PATH="backup:${BACKUP_S3_BUCKET}/${BACKUP_S3_PATH_PREFIX}/files/"
echo "Syncing files from ${SRC_PATH} to ${DEST_PATH}..."
rclone sync "$SRC_PATH" "$DEST_PATH" \
--transfers 8 \
--checkers 16 \
--fast-list \
-v
echo "File sync complete."
{{- end }}
@@ -0,0 +1,84 @@
{{- if and .Values.backup.enabled .Values.backup.mongodb.enabled .Values.aibot.enabled }}
apiVersion: batch/v1
kind: CronJob
metadata:
name: {{ include "huly.fullname" . }}-backup-mongodb
labels:
{{- include "huly.labels" . | nindent 4 }}
app: backup-mongodb
spec:
schedule: {{ .Values.backup.mongodb.schedule | default .Values.backup.schedule | quote }}
concurrencyPolicy: Forbid
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 3
jobTemplate:
spec:
backoffLimit: 2
template:
metadata:
labels:
{{- include "huly.labels" . | nindent 12 }}
app: backup-mongodb
spec:
{{- include "huly.scheduling" . | nindent 10 }}
restartPolicy: OnFailure
volumes:
- name: backup
emptyDir: {}
initContainers:
- name: mongodump
image: {{ .Values.mongodb.image }}
volumeMounts:
- name: backup
mountPath: /backup
env:
{{- include "huly.envConfig" (dict "name" "MONGO_URL" "key" "MONGO_URL" "root" .) | nindent 16 }}
command:
- sh
- -c
- |
set -e
STAMP=$(date +%Y%m%d-%H%M%S)
DEST="/backup/mongodb_${STAMP}.gz"
echo "Dumping MongoDB to ${DEST}..."
mongodump --uri="$MONGO_URL" --archive="$DEST" --gzip
ls -lh "$DEST"
echo "mongodump complete."
containers:
- name: rclone-upload
image: {{ .Values.backup.rcloneImage }}
volumeMounts:
- name: backup
mountPath: /backup
env:
{{- include "huly.envBackupSecret" (dict "name" "BACKUP_S3_ENDPOINT" "key" "BACKUP_S3_ENDPOINT" "root" .) | nindent 16 }}
{{- include "huly.envBackupSecret" (dict "name" "BACKUP_S3_REGION" "key" "BACKUP_S3_REGION" "root" .) | nindent 16 }}
{{- include "huly.envBackupSecret" (dict "name" "BACKUP_S3_BUCKET" "key" "BACKUP_S3_BUCKET" "root" .) | nindent 16 }}
{{- include "huly.envBackupSecret" (dict "name" "BACKUP_S3_PATH_PREFIX" "key" "BACKUP_S3_PATH_PREFIX" "root" .) | nindent 16 }}
{{- include "huly.envBackupSecret" (dict "name" "BACKUP_S3_ACCESS_KEY" "key" "BACKUP_S3_ACCESS_KEY" "root" .) | nindent 16 }}
{{- include "huly.envBackupSecret" (dict "name" "BACKUP_S3_SECRET_KEY" "key" "BACKUP_S3_SECRET_KEY" "root" .) | nindent 16 }}
command:
- sh
- -c
- |
set -e
rclone config create backup s3 \
provider=Other \
env_auth=false \
access_key_id="$BACKUP_S3_ACCESS_KEY" \
secret_access_key="$BACKUP_S3_SECRET_KEY" \
endpoint="$BACKUP_S3_ENDPOINT" \
region="$BACKUP_S3_REGION" \
--non-interactive
REMOTE_PATH="backup:${BACKUP_S3_BUCKET}/${BACKUP_S3_PATH_PREFIX}/mongodb/"
echo "Uploading to ${REMOTE_PATH}..."
rclone copy /backup/ "$REMOTE_PATH" --include "*.gz" -v
echo "Cleaning up backups older than {{ .Values.backup.retentionDays }} days..."
rclone delete "$REMOTE_PATH" --min-age {{ .Values.backup.retentionDays }}d -v
echo "Backup complete."
rclone ls "$REMOTE_PATH" | tail -5
{{- end }}
+43
View File
@@ -0,0 +1,43 @@
{{- if .Values.backup.enabled }}
{{- $secretName := include "huly.backupSecretName" . -}}
{{- $existing := lookup "v1" "Secret" .Release.Namespace $secretName -}}
{{- $hasExisting := not (empty $existing) -}}
{{- /* Resolve active credential set */ -}}
{{- $accessKey := "" -}}
{{- $secretKey := "" -}}
{{- if eq .Values.backup.s3.activeCredential "secondary" -}}
{{- $accessKey = .Values.backup.s3.secondaryAccessKey -}}
{{- $secretKey = .Values.backup.s3.secondarySecretKey -}}
{{- else -}}
{{- $accessKey = .Values.backup.s3.accessKey -}}
{{- $secretKey = .Values.backup.s3.secretKey -}}
{{- end -}}
{{- /* Fall back to existing secret if keys are empty */ -}}
{{- if and (not $accessKey) $hasExisting (hasKey $existing.data "BACKUP_S3_ACCESS_KEY") -}}
{{- $accessKey = index $existing.data "BACKUP_S3_ACCESS_KEY" | b64dec -}}
{{- end -}}
{{- if and (not $secretKey) $hasExisting (hasKey $existing.data "BACKUP_S3_SECRET_KEY") -}}
{{- $secretKey = index $existing.data "BACKUP_S3_SECRET_KEY" | b64dec -}}
{{- end -}}
{{- if or (not $accessKey) (not $secretKey) -}}
{{- fail "backup.s3.accessKey and backup.s3.secretKey are required when backup.enabled=true" }}
{{- end -}}
apiVersion: v1
kind: Secret
metadata:
name: {{ $secretName }}
labels:
{{- include "huly.labels" . | nindent 4 }}
type: Opaque
data:
BACKUP_S3_ENDPOINT: {{ .Values.backup.s3.endpoint | b64enc | quote }}
BACKUP_S3_REGION: {{ .Values.backup.s3.region | b64enc | quote }}
BACKUP_S3_BUCKET: {{ .Values.backup.s3.bucket | b64enc | quote }}
BACKUP_S3_PATH_PREFIX: {{ .Values.backup.s3.pathPrefix | b64enc | quote }}
BACKUP_S3_ACCESS_KEY: {{ $accessKey | b64enc | quote }}
BACKUP_S3_SECRET_KEY: {{ $secretKey | b64enc | quote }}
{{- end }}
@@ -0,0 +1,62 @@
{{- if .Values.cockroach.enabled }}
apiVersion: apps/v1
kind: Deployment
metadata:
name: cockroach
labels:
{{- include "huly.labels" . | nindent 4 }}
app: cockroach
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app: cockroach
template:
metadata:
labels:
{{- include "huly.labels" . | nindent 8 }}
app: cockroach
spec:
{{- include "huly.scheduling" . | nindent 6 }}
containers:
- name: cockroach
image: {{ .Values.cockroach.image }}
args:
- start-single-node
- --insecure
ports:
- name: grpc
containerPort: 26257
- name: http
containerPort: 8080
env:
- name: COCKROACH_DATABASE
value: {{ .Values.cockroach.database }}
- name: COCKROACH_USER
value: {{ .Values.cockroach.username }}
{{- include "huly.envSecret" (dict "name" "COCKROACH_PASSWORD" "key" "COCKROACH_PASSWORD" "root" .) | nindent 12 }}
readinessProbe:
exec:
command: ['sh', '-c', 'curl -sf http://localhost:8080/health?ready=1']
initialDelaySeconds: 10
periodSeconds: 10
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 30
periodSeconds: 30
volumeMounts:
- name: data
mountPath: /cockroach/cockroach-data
{{- with .Values.cockroach.resources }}
resources:
{{- toYaml . | nindent 12 }}
{{- end }}
volumes:
- name: data
persistentVolumeClaim:
claimName: cockroach-data
{{- end }}
+17
View File
@@ -0,0 +1,17 @@
{{- if .Values.cockroach.enabled }}
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: cockroach-data
labels:
{{- include "huly.labels" . | nindent 4 }}
spec:
accessModes:
- ReadWriteOnce
{{- if .Values.cockroach.storageClassName }}
storageClassName: {{ .Values.cockroach.storageClassName }}
{{- end }}
resources:
requests:
storage: {{ .Values.cockroach.storage }}
{{- end }}
@@ -0,0 +1,20 @@
{{- if .Values.cockroach.enabled }}
apiVersion: v1
kind: Service
metadata:
name: cockroach
labels:
{{- include "huly.labels" . | nindent 4 }}
app: cockroach
spec:
clusterIP: None
selector:
app: cockroach
ports:
- name: grpc
port: 26257
targetPort: 26257
- name: http
port: 8080
targetPort: 8080
{{- end }}
@@ -0,0 +1,48 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: collaborator
labels:
{{- include "huly.labels" . | nindent 4 }}
app: collaborator
spec:
replicas: {{ .Values.collaborator.replicas }}
selector:
matchLabels:
app: collaborator
template:
metadata:
annotations:
{{- include "huly.checksumAnnotations" . | nindent 8 }}
labels:
{{- include "huly.labels" . | nindent 8 }}
app: collaborator
spec:
{{- include "huly.scheduling" . | nindent 6 }}
containers:
- name: collaborator
image: {{ .Values.hulyRegistry }}/collaborator:{{ .Values.hulyVersion }}
ports:
- name: http
containerPort: 3078
env:
- name: COLLABORATOR_PORT
value: "3078"
{{- include "huly.envSecret" (dict "name" "SECRET" "key" "SERVER_SECRET" "root" .) | nindent 12 }}
{{- include "huly.envSecret" (dict "name" "STORAGE_CONFIG" "key" "STORAGE_CONFIG" "root" .) | nindent 12 }}
{{- include "huly.envConfig" (dict "name" "ACCOUNTS_URL" "key" "ACCOUNTS_URL_INTERNAL" "root" .) | nindent 12 }}
{{- include "huly.envConfig" (dict "name" "STATS_URL" "key" "STATS_URL_INTERNAL" "root" .) | nindent 12 }}
readinessProbe:
tcpSocket:
port: 3078
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
tcpSocket:
port: 3078
initialDelaySeconds: 30
periodSeconds: 30
{{- with .Values.collaborator.resources }}
resources:
{{- toYaml . | nindent 12 }}
{{- end }}
@@ -0,0 +1,14 @@
apiVersion: v1
kind: Service
metadata:
name: collaborator
labels:
{{- include "huly.labels" . | nindent 4 }}
app: collaborator
spec:
selector:
app: collaborator
ports:
- name: http
port: 3078
targetPort: 3078
+60
View File
@@ -0,0 +1,60 @@
{{- $proto := ternary "https" "http" (and .Values.ingress.enabled .Values.ingress.tls.enabled) -}}
{{- $wsProto := ternary "wss" "ws" (and .Values.ingress.enabled .Values.ingress.tls.enabled) -}}
{{- /* Resolve infra endpoints: built-in pod name or external URL */ -}}
{{- $elasticUrl := ternary "http://elastic:9200" .Values.external.elastic .Values.elastic.enabled -}}
{{- $queueConfig := ternary "redpanda:9092" .Values.external.redpanda .Values.redpanda.enabled -}}
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "huly.configName" . }}
labels:
{{- include "huly.labels" . | nindent 4 }}
data:
# External URLs (via ingress)
ACCOUNTS_URL: "{{ $proto }}://{{ .Values.domain }}/_accounts"
COLLABORATOR_URL: "{{ $wsProto }}://{{ .Values.domain }}/_collaborator"
FRONT_URL: "{{ $proto }}://{{ .Values.domain }}"
REKONI_URL: "{{ $proto }}://{{ .Values.domain }}/_rekoni"
STATS_URL: "{{ $proto }}://{{ .Values.domain }}/_stats"
TRANSACTOR_URL: "ws://transactor:3333;{{ $wsProto }}://{{ .Values.domain }}/_transactor"
# Internal URLs (pod-to-pod)
ELASTIC_URL: {{ $elasticUrl | quote }}
ELASTIC_INDEX_NAME: "huly_storage_index"
FULLTEXT_URL: "http://fulltext:4700"
ACCOUNTS_URL_INTERNAL: "http://account:3000"
REKONI_URL_INTERNAL: "http://rekoni:4004"
STATS_URL_INTERNAL: "http://stats:4900"
QUEUE_CONFIG: {{ $queueConfig | quote }}
# App settings
MODEL_ENABLED: {{ .Values.appSettings.modelEnabled | quote }}
LAST_NAME_FIRST: {{ .Values.appSettings.lastNameFirst | quote }}
TITLE: {{ .Values.appSettings.title | quote }}
DEFAULT_LANGUAGE: {{ .Values.appSettings.defaultLanguage | quote }}
UPLOAD_URL: "/files"
DESKTOP_UPDATES_CHANNEL: {{ .Values.appSettings.desktopChannel | quote }}
{{- if .Values.appSettings.adminEmails }}
ADMIN_EMAILS: {{ .Values.appSettings.adminEmails | quote }}
{{- end }}
{{- if .Values.auth.disableSignup }}
DISABLE_SIGNUP: "true"
{{- end }}
# Optional integration URLs (required by front even if unused)
GMAIL_URL: "{{ $proto }}://{{ .Values.domain }}/_gmail"
TELEGRAM_URL: "{{ $proto }}://{{ .Values.domain }}/_telegram"
CALENDAR_URL: "{{ $proto }}://{{ .Values.domain }}/_calendar"
LOVE_ENDPOINT: "{{ $proto }}://{{ .Values.domain }}/_love"
{{- if .Values.githubIntegration.enabled }}
GITHUB_URL: "{{ $proto }}://{{ .Values.domain }}/_github"
{{- end }}
{{- if .Values.aibot.enabled }}
AI_URL: "{{ $proto }}://{{ .Values.domain }}/_aibot"
AI_BOT_URL: "http://aibot:4010"
MONGO_URL: {{ ternary "mongodb://mongodb:27017" .Values.external.mongodb .Values.mongodb.enabled | quote }}
{{- end }}
@@ -0,0 +1,82 @@
{{- if .Values.elastic.enabled }}
apiVersion: apps/v1
kind: Deployment
metadata:
name: elastic
labels:
{{- include "huly.labels" . | nindent 4 }}
app: elastic
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app: elastic
template:
metadata:
labels:
{{- include "huly.labels" . | nindent 8 }}
app: elastic
spec:
securityContext:
runAsUser: 1000
runAsGroup: 1000
fsGroup: 1000
{{- include "huly.scheduling" . | nindent 6 }}
containers:
- name: elastic
image: {{ .Values.elastic.image }}
args:
- /bin/sh
- -c
- |
bin/elasticsearch-plugin install --batch ingest-attachment;
/usr/local/bin/docker-entrypoint.sh elasticsearch
ports:
- name: http
containerPort: 9200
env:
- name: BITNAMI_DEBUG
value: "true"
- name: ELASTICSEARCH_PORT_NUMBER
value: "9200"
- name: ES_JAVA_OPTS
value: {{ .Values.elastic.javaOpts | quote }}
- name: discovery.type
value: single-node
- name: http.cors.enabled
value: "true"
- name: http.cors.allow-origin
value: "http://localhost:8082"
livenessProbe:
exec:
command:
- /bin/sh
- -c
- |
curl -sf http://localhost:9200/_cluster/health | grep -vq '"status":"red"'
initialDelaySeconds: 60
periodSeconds: 20
failureThreshold: 10
readinessProbe:
exec:
command:
- /bin/sh
- -c
- |
curl -sf http://localhost:9200/_cluster/health
initialDelaySeconds: 30
periodSeconds: 10
volumeMounts:
- name: data
mountPath: /usr/share/elasticsearch/data
{{- with .Values.elastic.resources }}
resources:
{{- toYaml . | nindent 12 }}
{{- end }}
volumes:
- name: data
persistentVolumeClaim:
claimName: elastic-data
{{- end }}
+17
View File
@@ -0,0 +1,17 @@
{{- if .Values.elastic.enabled }}
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: elastic-data
labels:
{{- include "huly.labels" . | nindent 4 }}
spec:
accessModes:
- ReadWriteOnce
{{- if .Values.elastic.storageClassName }}
storageClassName: {{ .Values.elastic.storageClassName }}
{{- end }}
resources:
requests:
storage: {{ .Values.elastic.storage }}
{{- end }}
+16
View File
@@ -0,0 +1,16 @@
{{- if .Values.elastic.enabled }}
apiVersion: v1
kind: Service
metadata:
name: elastic
labels:
{{- include "huly.labels" . | nindent 4 }}
app: elastic
spec:
selector:
app: elastic
ports:
- name: http
port: 9200
targetPort: 9200
{{- end }}
+73
View File
@@ -0,0 +1,73 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: front
labels:
{{- include "huly.labels" . | nindent 4 }}
app: front
spec:
replicas: {{ .Values.front.replicas }}
selector:
matchLabels:
app: front
template:
metadata:
annotations:
{{- include "huly.checksumAnnotations" . | nindent 8 }}
labels:
{{- include "huly.labels" . | nindent 8 }}
app: front
spec:
{{- include "huly.scheduling" . | nindent 6 }}
containers:
- name: front
image: {{ .Values.hulyRegistry }}/front:{{ .Values.hulyVersion }}
ports:
- name: http
containerPort: 8080
env:
- name: SERVER_PORT
value: "8080"
{{- include "huly.envSecret" (dict "name" "SERVER_SECRET" "key" "SERVER_SECRET" "root" .) | nindent 12 }}
{{- include "huly.envSecret" (dict "name" "STORAGE_CONFIG" "key" "STORAGE_CONFIG" "root" .) | nindent 12 }}
{{- include "huly.envConfig" (dict "name" "ACCOUNTS_URL" "key" "ACCOUNTS_URL" "root" .) | nindent 12 }}
{{- include "huly.envConfig" (dict "name" "ACCOUNTS_URL_INTERNAL" "key" "ACCOUNTS_URL_INTERNAL" "root" .) | nindent 12 }}
{{- include "huly.envConfig" (dict "name" "COLLABORATOR_URL" "key" "COLLABORATOR_URL" "root" .) | nindent 12 }}
{{- include "huly.envConfig" (dict "name" "REKONI_URL" "key" "REKONI_URL" "root" .) | nindent 12 }}
{{- include "huly.envConfig" (dict "name" "ELASTIC_URL" "key" "ELASTIC_URL" "root" .) | nindent 12 }}
{{- include "huly.envConfig" (dict "name" "STATS_URL" "key" "STATS_URL" "root" .) | nindent 12 }}
{{- include "huly.envConfig" (dict "name" "DEFAULT_LANGUAGE" "key" "DEFAULT_LANGUAGE" "root" .) | nindent 12 }}
{{- include "huly.envConfig" (dict "name" "TITLE" "key" "TITLE" "root" .) | nindent 12 }}
{{- include "huly.envConfig" (dict "name" "UPLOAD_URL" "key" "UPLOAD_URL" "root" .) | nindent 12 }}
{{- include "huly.envConfig" (dict "name" "LAST_NAME_FIRST" "key" "LAST_NAME_FIRST" "root" .) | nindent 12 }}
{{- include "huly.envConfig" (dict "name" "DESKTOP_UPDATES_CHANNEL" "key" "DESKTOP_UPDATES_CHANNEL" "root" .) | nindent 12 }}
{{- include "huly.envConfig" (dict "name" "CALENDAR_URL" "key" "CALENDAR_URL" "root" .) | nindent 12 }}
{{- include "huly.envConfig" (dict "name" "GMAIL_URL" "key" "GMAIL_URL" "root" .) | nindent 12 }}
{{- include "huly.envConfig" (dict "name" "TELEGRAM_URL" "key" "TELEGRAM_URL" "root" .) | nindent 12 }}
{{- include "huly.envConfig" (dict "name" "LOVE_ENDPOINT" "key" "LOVE_ENDPOINT" "root" .) | nindent 12 }}
{{- if .Values.githubIntegration.enabled }}
{{- include "huly.envConfig" (dict "name" "GITHUB_URL" "key" "GITHUB_URL" "root" .) | nindent 12 }}
{{- end }}
{{- if .Values.aibot.enabled }}
{{- include "huly.envConfig" (dict "name" "AI_URL" "key" "AI_URL" "root" .) | nindent 12 }}
{{- end }}
{{- if .Values.auth.disableSignup }}
- name: DISABLE_SIGNUP
value: "true"
{{- end }}
readinessProbe:
httpGet:
path: /
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
httpGet:
path: /
port: 8080
initialDelaySeconds: 15
periodSeconds: 30
{{- with .Values.front.resources }}
resources:
{{- toYaml . | nindent 12 }}
{{- end }}
+14
View File
@@ -0,0 +1,14 @@
apiVersion: v1
kind: Service
metadata:
name: front
labels:
{{- include "huly.labels" . | nindent 4 }}
app: front
spec:
selector:
app: front
ports:
- name: http
port: 8080
targetPort: 8080
@@ -0,0 +1,54 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: fulltext
labels:
{{- include "huly.labels" . | nindent 4 }}
app: fulltext
spec:
replicas: {{ .Values.fulltext.replicas }}
selector:
matchLabels:
app: fulltext
template:
metadata:
annotations:
{{- include "huly.checksumAnnotations" . | nindent 8 }}
labels:
{{- include "huly.labels" . | nindent 8 }}
app: fulltext
spec:
{{- include "huly.scheduling" . | nindent 6 }}
initContainers:
{{- include "huly.waitForCockroach" . | nindent 8 }}
{{- include "huly.waitForRedpanda" . | nindent 8 }}
containers:
- name: fulltext
image: {{ .Values.hulyRegistry }}/fulltext:{{ .Values.hulyVersion }}
ports:
- name: http
containerPort: 4700
env:
{{- include "huly.envSecret" (dict "name" "SERVER_SECRET" "key" "SERVER_SECRET" "root" .) | nindent 12 }}
{{- include "huly.envSecret" (dict "name" "DB_URL" "key" "CR_DB_URL" "root" .) | nindent 12 }}
{{- include "huly.envSecret" (dict "name" "STORAGE_CONFIG" "key" "STORAGE_CONFIG" "root" .) | nindent 12 }}
{{- include "huly.envConfig" (dict "name" "FULLTEXT_DB_URL" "key" "ELASTIC_URL" "root" .) | nindent 12 }}
{{- include "huly.envConfig" (dict "name" "ELASTIC_INDEX_NAME" "key" "ELASTIC_INDEX_NAME" "root" .) | nindent 12 }}
{{- include "huly.envConfig" (dict "name" "REKONI_URL" "key" "REKONI_URL_INTERNAL" "root" .) | nindent 12 }}
{{- include "huly.envConfig" (dict "name" "ACCOUNTS_URL" "key" "ACCOUNTS_URL_INTERNAL" "root" .) | nindent 12 }}
{{- include "huly.envConfig" (dict "name" "STATS_URL" "key" "STATS_URL_INTERNAL" "root" .) | nindent 12 }}
{{- include "huly.envConfig" (dict "name" "QUEUE_CONFIG" "key" "QUEUE_CONFIG" "root" .) | nindent 12 }}
readinessProbe:
tcpSocket:
port: 4700
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
tcpSocket:
port: 4700
initialDelaySeconds: 30
periodSeconds: 30
{{- with .Values.fulltext.resources }}
resources:
{{- toYaml . | nindent 12 }}
{{- end }}
+14
View File
@@ -0,0 +1,14 @@
apiVersion: v1
kind: Service
metadata:
name: fulltext
labels:
{{- include "huly.labels" . | nindent 4 }}
app: fulltext
spec:
selector:
app: fulltext
ports:
- name: http
port: 4700
targetPort: 4700
@@ -0,0 +1,61 @@
{{- if .Values.githubIntegration.enabled -}}
apiVersion: apps/v1
kind: Deployment
metadata:
name: github
labels:
{{- include "huly.labels" . | nindent 4 }}
app: github
spec:
replicas: {{ .Values.githubIntegration.replicas }}
selector:
matchLabels:
app: github
template:
metadata:
annotations:
{{- include "huly.checksumAnnotations" . | nindent 8 }}
labels:
{{- include "huly.labels" . | nindent 8 }}
app: github
spec:
{{- include "huly.scheduling" . | nindent 6 }}
containers:
- name: github
image: {{ .Values.hulyRegistry }}/github:{{ .Values.hulyVersion }}
ports:
- name: http
containerPort: 3500
env:
- name: PORT
value: "3500"
{{- include "huly.envSecret" (dict "name" "SERVER_SECRET" "key" "SERVER_SECRET" "root" .) | nindent 12 }}
{{- include "huly.envSecret" (dict "name" "STORAGE_CONFIG" "key" "STORAGE_CONFIG" "root" .) | nindent 12 }}
{{- include "huly.envSecret" (dict "name" "APP_ID" "key" "GITHUB_APP_ID" "root" .) | nindent 12 }}
{{- include "huly.envSecret" (dict "name" "CLIENT_ID" "key" "GITHUB_APP_CLIENT_ID" "root" .) | nindent 12 }}
{{- include "huly.envSecret" (dict "name" "CLIENT_SECRET" "key" "GITHUB_APP_CLIENT_SECRET" "root" .) | nindent 12 }}
{{- include "huly.envSecret" (dict "name" "PRIVATE_KEY" "key" "GITHUB_APP_PRIVATE_KEY" "root" .) | nindent 12 }}
{{- include "huly.envSecret" (dict "name" "WEBHOOK_SECRET" "key" "GITHUB_APP_WEBHOOK_SECRET" "root" .) | nindent 12 }}
{{- include "huly.envConfig" (dict "name" "ACCOUNTS_URL" "key" "ACCOUNTS_URL" "root" .) | nindent 12 }}
{{- include "huly.envConfig" (dict "name" "FRONT_URL" "key" "FRONT_URL" "root" .) | nindent 12 }}
{{- include "huly.envConfig" (dict "name" "COLLABORATOR_URL" "key" "COLLABORATOR_URL" "root" .) | nindent 12 }}
{{- include "huly.envConfig" (dict "name" "QUEUE_CONFIG" "key" "QUEUE_CONFIG" "root" .) | nindent 12 }}
- name: BOT_NAME
value: {{ .Values.githubIntegration.botName | quote }}
- name: SERVICE_ID
value: "github-service"
readinessProbe:
tcpSocket:
port: 3500
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
tcpSocket:
port: 3500
initialDelaySeconds: 30
periodSeconds: 30
{{- with .Values.githubIntegration.resources }}
resources:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- end }}
+16
View File
@@ -0,0 +1,16 @@
{{- if .Values.githubIntegration.enabled -}}
apiVersion: v1
kind: Service
metadata:
name: github
labels:
{{- include "huly.labels" . | nindent 4 }}
app: github
spec:
selector:
app: github
ports:
- name: http
port: 3500
targetPort: 3500
{{- end }}
+98
View File
@@ -0,0 +1,98 @@
{{- if .Values.ingress.enabled -}}
{{- $fullname := include "huly.fullname" . -}}
{{- $domain := .Values.domain -}}
{{- $tlsEnabled := .Values.ingress.tls.enabled -}}
{{- $clusterIssuer := .Values.ingress.tls.clusterIssuer -}}
{{- $className := .Values.ingress.className -}}
{{- $extraAnnotations := .Values.ingress.annotations -}}
{{- /* Backend services exposed via path-based routing */ -}}
{{- $backends := list
(dict "name" "accounts" "svc" "account" "port" 3000 "ws" false)
(dict "name" "transactor" "svc" "transactor" "port" 3333 "ws" true)
(dict "name" "collaborator" "svc" "collaborator" "port" 3078 "ws" true)
(dict "name" "rekoni" "svc" "rekoni" "port" 4004 "ws" false)
(dict "name" "stats" "svc" "stats" "port" 4900 "ws" false)
-}}
{{- if .Values.aibot.enabled }}
{{- $backends = append $backends (dict "name" "aibot" "svc" "aibot" "port" 4010 "ws" true) }}
{{- end -}}
{{- if .Values.githubIntegration.enabled }}
{{- $backends = append $backends (dict "name" "github" "svc" "github" "port" 3500 "ws" false) }}
{{- end -}}
{{- range $backends }}
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ $fullname }}-{{ .name }}
labels:
{{- include "huly.labels" $ | nindent 4 }}
annotations:
nginx.ingress.kubernetes.io/use-regex: "true"
nginx.ingress.kubernetes.io/rewrite-target: /$2
{{- if .ws }}
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
nginx.ingress.kubernetes.io/proxy-http-version: "1.1"
{{- end }}
{{- with $extraAnnotations }}
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
ingressClassName: {{ $className }}
{{- if $tlsEnabled }}
tls:
- hosts:
- {{ $domain }}
secretName: {{ $fullname }}-tls
{{- end }}
rules:
- host: {{ $domain }}
http:
paths:
- path: /_{{ .name }}(/|$)(.*)
pathType: ImplementationSpecific
backend:
service:
name: {{ .svc }}
port:
number: {{ .port }}
{{- end }}
---
# Front catch-all — also owns the TLS certificate
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ $fullname }}-front
labels:
{{- include "huly.labels" . | nindent 4 }}
annotations:
{{- if $tlsEnabled }}
cert-manager.io/cluster-issuer: {{ $clusterIssuer }}
{{- end }}
{{- with $extraAnnotations }}
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
ingressClassName: {{ $className }}
{{- if $tlsEnabled }}
tls:
- hosts:
- {{ $domain }}
secretName: {{ $fullname }}-tls
{{- end }}
rules:
- host: {{ $domain }}
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: front
port:
number: 8080
{{- end }}
+49
View File
@@ -0,0 +1,49 @@
{{- if .Values.kvs.enabled }}
apiVersion: apps/v1
kind: Deployment
metadata:
name: kvs
labels:
{{- include "huly.labels" . | nindent 4 }}
app: kvs
spec:
replicas: {{ .Values.kvs.replicas }}
selector:
matchLabels:
app: kvs
template:
metadata:
annotations:
{{- include "huly.checksumAnnotations" . | nindent 8 }}
labels:
{{- include "huly.labels" . | nindent 8 }}
app: kvs
spec:
{{- include "huly.scheduling" . | nindent 6 }}
initContainers:
{{- include "huly.waitForCockroach" . | nindent 8 }}
containers:
- name: kvs
image: {{ .Values.hulyRegistry }}/hulykvs:{{ .Values.kvs.version | default .Values.hulyVersion }}
imagePullPolicy: Always
ports:
- name: http
containerPort: 8094
env:
{{- include "huly.envSecret" (dict "name" "HULY_DB_CONNECTION" "key" "CR_DB_URL" "root" .) | nindent 12 }}
{{- include "huly.envSecret" (dict "name" "HULY_TOKEN_SECRET" "key" "SERVER_SECRET" "root" .) | nindent 12 }}
readinessProbe:
tcpSocket:
port: 8094
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
tcpSocket:
port: 8094
initialDelaySeconds: 15
periodSeconds: 30
{{- with .Values.kvs.resources }}
resources:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- end }}
+16
View File
@@ -0,0 +1,16 @@
{{- if .Values.kvs.enabled }}
apiVersion: v1
kind: Service
metadata:
name: kvs
labels:
{{- include "huly.labels" . | nindent 4 }}
app: kvs
spec:
selector:
app: kvs
ports:
- name: http
port: 8094
targetPort: 8094
{{- end }}
+61
View File
@@ -0,0 +1,61 @@
{{- if eq (include "huly.minioEnabled" .) "true" }}
apiVersion: apps/v1
kind: Deployment
metadata:
name: minio
labels:
{{- include "huly.labels" . | nindent 4 }}
app: minio
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app: minio
template:
metadata:
labels:
{{- include "huly.labels" . | nindent 8 }}
app: minio
spec:
{{- include "huly.scheduling" . | nindent 6 }}
containers:
- name: minio
image: {{ .Values.minio.image }}
args:
- server
- /data
- --address
- ":9000"
- --console-address
- ":9001"
ports:
- name: api
containerPort: 9000
- name: console
containerPort: 9001
readinessProbe:
httpGet:
path: /minio/health/ready
port: 9000
initialDelaySeconds: 10
periodSeconds: 10
livenessProbe:
httpGet:
path: /minio/health/live
port: 9000
initialDelaySeconds: 30
periodSeconds: 20
volumeMounts:
- name: data
mountPath: /data
{{- with .Values.minio.resources }}
resources:
{{- toYaml . | nindent 12 }}
{{- end }}
volumes:
- name: data
persistentVolumeClaim:
claimName: minio-data
{{- end }}
+17
View File
@@ -0,0 +1,17 @@
{{- if eq (include "huly.minioEnabled" .) "true" }}
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: minio-data
labels:
{{- include "huly.labels" . | nindent 4 }}
spec:
accessModes:
- ReadWriteOnce
{{- if .Values.minio.storageClassName }}
storageClassName: {{ .Values.minio.storageClassName }}
{{- end }}
resources:
requests:
storage: {{ .Values.minio.storage }}
{{- end }}
+19
View File
@@ -0,0 +1,19 @@
{{- if eq (include "huly.minioEnabled" .) "true" }}
apiVersion: v1
kind: Service
metadata:
name: minio
labels:
{{- include "huly.labels" . | nindent 4 }}
app: minio
spec:
selector:
app: minio
ports:
- name: api
port: 9000
targetPort: 9000
- name: console
port: 9001
targetPort: 9001
{{- end }}
@@ -0,0 +1,50 @@
{{- if and .Values.aibot.enabled .Values.mongodb.enabled }}
apiVersion: apps/v1
kind: Deployment
metadata:
name: mongodb
labels:
{{- include "huly.labels" . | nindent 4 }}
app: mongodb
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app: mongodb
template:
metadata:
labels:
{{- include "huly.labels" . | nindent 8 }}
app: mongodb
spec:
{{- include "huly.scheduling" . | nindent 6 }}
containers:
- name: mongodb
image: {{ .Values.mongodb.image }}
ports:
- name: mongo
containerPort: 27017
readinessProbe:
tcpSocket:
port: 27017
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
tcpSocket:
port: 27017
initialDelaySeconds: 15
periodSeconds: 30
volumeMounts:
- name: data
mountPath: /data/db
{{- with .Values.mongodb.resources }}
resources:
{{- toYaml . | nindent 12 }}
{{- end }}
volumes:
- name: data
persistentVolumeClaim:
claimName: mongodb-data
{{- end }}
+17
View File
@@ -0,0 +1,17 @@
{{- if and .Values.aibot.enabled .Values.mongodb.enabled }}
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mongodb-data
labels:
{{- include "huly.labels" . | nindent 4 }}
spec:
accessModes:
- ReadWriteOnce
{{- if .Values.mongodb.storageClassName }}
storageClassName: {{ .Values.mongodb.storageClassName }}
{{- end }}
resources:
requests:
storage: {{ .Values.mongodb.storage }}
{{- end }}
+17
View File
@@ -0,0 +1,17 @@
{{- if and .Values.aibot.enabled .Values.mongodb.enabled }}
apiVersion: v1
kind: Service
metadata:
name: mongodb
labels:
{{- include "huly.labels" . | nindent 4 }}
app: mongodb
spec:
clusterIP: None
selector:
app: mongodb
ports:
- name: mongo
port: 27017
targetPort: 27017
{{- end }}
@@ -0,0 +1,87 @@
{{- if .Values.redpanda.enabled }}
apiVersion: apps/v1
kind: Deployment
metadata:
name: redpanda
labels:
{{- include "huly.labels" . | nindent 4 }}
app: redpanda
spec:
replicas: 1
selector:
matchLabels:
app: redpanda
template:
metadata:
labels:
{{- include "huly.labels" . | nindent 8 }}
app: redpanda
spec:
{{- include "huly.scheduling" . | nindent 6 }}
initContainers:
- name: set-datadir-ownership
image: busybox:1.36
command: ["sh", "-c", "chown -R 101:101 /var/lib/redpanda/data"]
volumeMounts:
- name: data
mountPath: /var/lib/redpanda/data
containers:
- name: redpanda
image: {{ .Values.redpanda.image }}
args:
- redpanda
- start
- --kafka-addr
- internal://0.0.0.0:9092,external://0.0.0.0:19092
- --advertise-kafka-addr
- internal://redpanda:9092,external://localhost:19092
- --pandaproxy-addr
- internal://0.0.0.0:8082,external://0.0.0.0:18082
- --advertise-pandaproxy-addr
- internal://redpanda:8082,external://localhost:18082
- --schema-registry-addr
- internal://0.0.0.0:8081,external://0.0.0.0:18081
- --rpc-addr
- 0.0.0.0:33145
- --advertise-rpc-addr
- redpanda:33145
- --mode
- dev-container
- --smp
- "1"
- --default-log-level=info
ports:
- name: kafka
containerPort: 9092
- name: rpc
containerPort: 33145
env:
- name: REDPANDA_SUPERUSER_USERNAME
value: superuser
- name: REDPANDA_SUPERUSER_PASSWORD
valueFrom:
secretKeyRef:
name: {{ include "huly.secretName" . }}
key: REDPANDA_SUPERUSER_PASSWORD
- name: NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
readinessProbe:
exec:
command: ["/bin/sh", "-c", "rpk cluster info"]
initialDelaySeconds: 10
periodSeconds: 10
failureThreshold: 5
volumeMounts:
- name: data
mountPath: /var/lib/redpanda/data
{{- with .Values.redpanda.resources }}
resources:
{{- toYaml . | nindent 12 }}
{{- end }}
volumes:
- name: data
persistentVolumeClaim:
claimName: redpanda-data
{{- end }}
+17
View File
@@ -0,0 +1,17 @@
{{- if .Values.redpanda.enabled }}
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: redpanda-data
labels:
{{- include "huly.labels" . | nindent 4 }}
spec:
accessModes:
- ReadWriteOnce
{{- if .Values.redpanda.storageClassName }}
storageClassName: {{ .Values.redpanda.storageClassName }}
{{- end }}
resources:
requests:
storage: {{ .Values.redpanda.storage }}
{{- end }}
+19
View File
@@ -0,0 +1,19 @@
{{- if .Values.redpanda.enabled }}
apiVersion: v1
kind: Service
metadata:
name: redpanda
labels:
{{- include "huly.labels" . | nindent 4 }}
app: redpanda
spec:
selector:
app: redpanda
ports:
- name: kafka
port: 9092
targetPort: 9092
- name: rpc
port: 33145
targetPort: 33145
{{- end }}
@@ -0,0 +1,43 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: rekoni
labels:
{{- include "huly.labels" . | nindent 4 }}
app: rekoni
spec:
replicas: {{ .Values.rekoni.replicas }}
selector:
matchLabels:
app: rekoni
template:
metadata:
annotations:
{{- include "huly.checksumAnnotations" . | nindent 8 }}
labels:
{{- include "huly.labels" . | nindent 8 }}
app: rekoni
spec:
{{- include "huly.scheduling" . | nindent 6 }}
containers:
- name: rekoni
image: {{ .Values.hulyRegistry }}/rekoni-service:{{ .Values.hulyVersion }}
ports:
- name: http
containerPort: 4004
env:
{{- include "huly.envSecret" (dict "name" "SECRET" "key" "SERVER_SECRET" "root" .) | nindent 12 }}
readinessProbe:
tcpSocket:
port: 4004
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
tcpSocket:
port: 4004
initialDelaySeconds: 15
periodSeconds: 30
{{- with .Values.rekoni.resources }}
resources:
{{- toYaml . | nindent 12 }}
{{- end }}
+14
View File
@@ -0,0 +1,14 @@
apiVersion: v1
kind: Service
metadata:
name: rekoni
labels:
{{- include "huly.labels" . | nindent 4 }}
app: rekoni
spec:
selector:
app: rekoni
ports:
- name: http
port: 4004
targetPort: 4004
+124
View File
@@ -0,0 +1,124 @@
{{- $secretName := include "huly.secretName" . -}}
{{- $existing := lookup "v1" "Secret" .Release.Namespace $secretName -}}
{{- $hasExisting := not (empty $existing) -}}
{{- /* SERVER_SECRET */ -}}
{{- $serverSecret := "" -}}
{{- if .Values.secrets.serverSecret -}}
{{- $serverSecret = .Values.secrets.serverSecret -}}
{{- else if and $hasExisting (hasKey $existing.data "SERVER_SECRET") -}}
{{- $serverSecret = index $existing.data "SERVER_SECRET" | b64dec -}}
{{- else -}}
{{- $serverSecret = randAlphaNum 32 -}}
{{- end -}}
{{- /* COCKROACH_PASSWORD */ -}}
{{- $cockroachPwd := "" -}}
{{- if .Values.secrets.cockroachPassword -}}
{{- $cockroachPwd = .Values.secrets.cockroachPassword -}}
{{- else if and $hasExisting (hasKey $existing.data "COCKROACH_PASSWORD") -}}
{{- $cockroachPwd = index $existing.data "COCKROACH_PASSWORD" | b64dec -}}
{{- else -}}
{{- $cockroachPwd = randAlphaNum 24 -}}
{{- end -}}
{{- /* REDPANDA_SUPERUSER_PASSWORD */ -}}
{{- $redpandaPwd := "" -}}
{{- if .Values.secrets.redpandaPassword -}}
{{- $redpandaPwd = .Values.secrets.redpandaPassword -}}
{{- else if and $hasExisting (hasKey $existing.data "REDPANDA_SUPERUSER_PASSWORD") -}}
{{- $redpandaPwd = index $existing.data "REDPANDA_SUPERUSER_PASSWORD" | b64dec -}}
{{- else -}}
{{- $redpandaPwd = randAlphaNum 24 -}}
{{- end -}}
{{- /* STORAGE_CONFIG — explicit values > storage.type config > existing secret > auto-generate */ -}}
{{- $storageConfig := "" -}}
{{- if .Values.secrets.storageConfig -}}
{{- $storageConfig = .Values.secrets.storageConfig -}}
{{- else if eq .Values.storage.type "s3" -}}
{{- $s3Params := printf "accessKey=%s&secretKey=%s&region=%s" .Values.storage.s3.accessKey .Values.storage.s3.secretKey .Values.storage.s3.region -}}
{{- if .Values.storage.s3.rootBucket -}}
{{- $s3Params = printf "%s&rootBucket=%s" $s3Params .Values.storage.s3.rootBucket -}}
{{- end -}}
{{- if .Values.storage.s3.bucketPrefix -}}
{{- $s3Params = printf "%s&bucketPrefix=%s" $s3Params .Values.storage.s3.bucketPrefix -}}
{{- end -}}
{{- $storageConfig = printf "s3|%s?%s" .Values.storage.s3.endpoint $s3Params -}}
{{- else if and $hasExisting (hasKey $existing.data "STORAGE_CONFIG") -}}
{{- $storageConfig = index $existing.data "STORAGE_CONFIG" | b64dec -}}
{{- else -}}
{{- $storageConfig = printf "minio|minio?accessKey=%s&secretKey=%s" (randAlphaNum 20) (randAlphaNum 40) -}}
{{- end -}}
{{- /* CR_DB_URL */ -}}
{{- $crDbUrl := "" -}}
{{- if .Values.secrets.crDbUrl -}}
{{- $crDbUrl = .Values.secrets.crDbUrl -}}
{{- else if and $hasExisting (hasKey $existing.data "CR_DB_URL") -}}
{{- $crDbUrl = index $existing.data "CR_DB_URL" | b64dec -}}
{{- else -}}
{{- $crDbUrl = printf "postgres://%s:%s@cockroach:26257/%s" .Values.cockroach.username $cockroachPwd .Values.cockroach.database -}}
{{- end -}}
{{- /* AIBOT_PASSWORD */ -}}
{{- $aibotPwd := "" -}}
{{- if .Values.secrets.aibotPassword -}}
{{- $aibotPwd = .Values.secrets.aibotPassword -}}
{{- else if and $hasExisting (hasKey $existing.data "AIBOT_PASSWORD") -}}
{{- $aibotPwd = index $existing.data "AIBOT_PASSWORD" | b64dec -}}
{{- else -}}
{{- $aibotPwd = randAlphaNum 24 -}}
{{- end -}}
apiVersion: v1
kind: Secret
metadata:
name: {{ $secretName }}
labels:
{{- include "huly.labels" . | nindent 4 }}
type: Opaque
data:
SERVER_SECRET: {{ $serverSecret | b64enc | quote }}
COCKROACH_PASSWORD: {{ $cockroachPwd | b64enc | quote }}
REDPANDA_SUPERUSER_PASSWORD: {{ $redpandaPwd | b64enc | quote }}
STORAGE_CONFIG: {{ $storageConfig | b64enc | quote }}
CR_DB_URL: {{ $crDbUrl | b64enc | quote }}
AIBOT_PASSWORD: {{ $aibotPwd | b64enc | quote }}
{{- if .Values.aibot.enabled }}
{{- if not .Values.secrets.openaiApiKey }}
{{- fail "secrets.openaiApiKey is required when aibot.enabled=true" }}
{{- end }}
OPENAI_API_KEY: {{ .Values.secrets.openaiApiKey | b64enc | quote }}
{{- with .Values.secrets.openaiBaseUrl }}
OPENAI_BASE_URL: {{ . | b64enc | quote }}
{{- end }}
{{- end }}
{{- with .Values.auth.google.clientId }}
GOOGLE_CLIENT_ID: {{ . | b64enc | quote }}
{{- end }}
{{- with .Values.auth.google.clientSecret }}
GOOGLE_CLIENT_SECRET: {{ . | b64enc | quote }}
{{- end }}
{{- with .Values.auth.github.clientId }}
GITHUB_CLIENT_ID: {{ . | b64enc | quote }}
{{- end }}
{{- with .Values.auth.github.clientSecret }}
GITHUB_CLIENT_SECRET: {{ . | b64enc | quote }}
{{- end }}
{{- with .Values.auth.oidc.clientId }}
OPENID_CLIENT_ID: {{ . | b64enc | quote }}
{{- end }}
{{- with .Values.auth.oidc.clientSecret }}
OPENID_CLIENT_SECRET: {{ . | b64enc | quote }}
{{- end }}
{{- with .Values.auth.oidc.issuer }}
OPENID_ISSUER: {{ . | b64enc | quote }}
{{- end }}
{{- if .Values.githubIntegration.enabled }}
GITHUB_APP_ID: {{ .Values.githubIntegration.appId | toString | b64enc | quote }}
GITHUB_APP_CLIENT_ID: {{ .Values.githubIntegration.clientId | b64enc | quote }}
GITHUB_APP_CLIENT_SECRET: {{ .Values.githubIntegration.clientSecret | b64enc | quote }}
GITHUB_APP_PRIVATE_KEY: {{ .Values.githubIntegration.privateKey | b64enc | quote }}
GITHUB_APP_WEBHOOK_SECRET: {{ .Values.githubIntegration.webhookSecret | b64enc | quote }}
{{- end }}
+45
View File
@@ -0,0 +1,45 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: stats
labels:
{{- include "huly.labels" . | nindent 4 }}
app: stats
spec:
replicas: {{ .Values.stats.replicas }}
selector:
matchLabels:
app: stats
template:
metadata:
annotations:
{{- include "huly.checksumAnnotations" . | nindent 8 }}
labels:
{{- include "huly.labels" . | nindent 8 }}
app: stats
spec:
{{- include "huly.scheduling" . | nindent 6 }}
containers:
- name: stats
image: {{ .Values.hulyRegistry }}/stats:{{ .Values.hulyVersion }}
ports:
- name: http
containerPort: 4900
env:
- name: PORT
value: "4900"
{{- include "huly.envSecret" (dict "name" "SERVER_SECRET" "key" "SERVER_SECRET" "root" .) | nindent 12 }}
readinessProbe:
tcpSocket:
port: 4900
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
tcpSocket:
port: 4900
initialDelaySeconds: 15
periodSeconds: 30
{{- with .Values.stats.resources }}
resources:
{{- toYaml . | nindent 12 }}
{{- end }}
+14
View File
@@ -0,0 +1,14 @@
apiVersion: v1
kind: Service
metadata:
name: stats
labels:
{{- include "huly.labels" . | nindent 4 }}
app: stats
spec:
selector:
app: stats
ports:
- name: http
port: 4900
targetPort: 4900
@@ -0,0 +1,61 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: transactor
labels:
{{- include "huly.labels" . | nindent 4 }}
app: transactor
spec:
replicas: {{ .Values.transactor.replicas }}
selector:
matchLabels:
app: transactor
template:
metadata:
annotations:
{{- include "huly.checksumAnnotations" . | nindent 8 }}
labels:
{{- include "huly.labels" . | nindent 8 }}
app: transactor
spec:
{{- include "huly.scheduling" . | nindent 6 }}
initContainers:
{{- include "huly.waitForCockroach" . | nindent 8 }}
{{- include "huly.waitForRedpanda" . | nindent 8 }}
containers:
- name: transactor
image: {{ .Values.hulyRegistry }}/transactor:{{ .Values.hulyVersion }}
ports:
- name: http
containerPort: 3333
env:
- name: SERVER_PORT
value: "3333"
{{- include "huly.envSecret" (dict "name" "SERVER_SECRET" "key" "SERVER_SECRET" "root" .) | nindent 12 }}
{{- include "huly.envSecret" (dict "name" "DB_URL" "key" "CR_DB_URL" "root" .) | nindent 12 }}
{{- include "huly.envSecret" (dict "name" "STORAGE_CONFIG" "key" "STORAGE_CONFIG" "root" .) | nindent 12 }}
{{- include "huly.envConfig" (dict "name" "FRONT_URL" "key" "FRONT_URL" "root" .) | nindent 12 }}
{{- include "huly.envConfig" (dict "name" "ACCOUNTS_URL" "key" "ACCOUNTS_URL_INTERNAL" "root" .) | nindent 12 }}
{{- include "huly.envConfig" (dict "name" "FULLTEXT_URL" "key" "FULLTEXT_URL" "root" .) | nindent 12 }}
{{- include "huly.envConfig" (dict "name" "STATS_URL" "key" "STATS_URL_INTERNAL" "root" .) | nindent 12 }}
{{- include "huly.envConfig" (dict "name" "QUEUE_CONFIG" "key" "QUEUE_CONFIG" "root" .) | nindent 12 }}
{{- include "huly.envConfig" (dict "name" "LAST_NAME_FIRST" "key" "LAST_NAME_FIRST" "root" .) | nindent 12 }}
{{- if .Values.aibot.enabled }}
{{- include "huly.envConfig" (dict "name" "AI_BOT_URL" "key" "AI_BOT_URL" "root" .) | nindent 12 }}
{{- end }}
- name: SERVER_CURSOR_MAXTIMEMS
value: "30000"
readinessProbe:
tcpSocket:
port: 3333
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
tcpSocket:
port: 3333
initialDelaySeconds: 30
periodSeconds: 30
{{- with .Values.transactor.resources }}
resources:
{{- toYaml . | nindent 12 }}
{{- end }}
@@ -0,0 +1,14 @@
apiVersion: v1
kind: Service
metadata:
name: transactor
labels:
{{- include "huly.labels" . | nindent 4 }}
app: transactor
spec:
selector:
app: transactor
ports:
- name: http
port: 3333
targetPort: 3333
@@ -0,0 +1,41 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: workspace
labels:
{{- include "huly.labels" . | nindent 4 }}
app: workspace
spec:
replicas: {{ .Values.workspace.replicas }}
selector:
matchLabels:
app: workspace
template:
metadata:
annotations:
{{- include "huly.checksumAnnotations" . | nindent 8 }}
labels:
{{- include "huly.labels" . | nindent 8 }}
app: workspace
spec:
{{- include "huly.scheduling" . | nindent 6 }}
initContainers:
{{- include "huly.waitForCockroach" . | nindent 8 }}
{{- include "huly.waitForRedpanda" . | nindent 8 }}
containers:
- name: workspace
image: {{ .Values.hulyRegistry }}/workspace:{{ .Values.hulyVersion }}
env:
{{- include "huly.envSecret" (dict "name" "SERVER_SECRET" "key" "SERVER_SECRET" "root" .) | nindent 12 }}
{{- include "huly.envSecret" (dict "name" "DB_URL" "key" "CR_DB_URL" "root" .) | nindent 12 }}
{{- include "huly.envSecret" (dict "name" "ACCOUNTS_DB_URL" "key" "CR_DB_URL" "root" .) | nindent 12 }}
{{- include "huly.envSecret" (dict "name" "STORAGE_CONFIG" "key" "STORAGE_CONFIG" "root" .) | nindent 12 }}
{{- include "huly.envConfig" (dict "name" "ACCOUNTS_URL" "key" "ACCOUNTS_URL_INTERNAL" "root" .) | nindent 12 }}
{{- include "huly.envConfig" (dict "name" "STATS_URL" "key" "STATS_URL_INTERNAL" "root" .) | nindent 12 }}
{{- include "huly.envConfig" (dict "name" "TRANSACTOR_URL" "key" "TRANSACTOR_URL" "root" .) | nindent 12 }}
{{- include "huly.envConfig" (dict "name" "MODEL_ENABLED" "key" "MODEL_ENABLED" "root" .) | nindent 12 }}
{{- include "huly.envConfig" (dict "name" "QUEUE_CONFIG" "key" "QUEUE_CONFIG" "root" .) | nindent 12 }}
{{- with .Values.workspace.resources }}
resources:
{{- toYaml . | nindent 12 }}
{{- end }}
+292
View File
@@ -0,0 +1,292 @@
# ──────────────────────────────────────────────────────────────
# REQUIRED — set this to your domain
# ──────────────────────────────────────────────────────────────
domain: huly.example
# Override the chart name / full release name
nameOverride: ""
fullnameOverride: ""
# ──────────────────────────────────────────────────────────────
# Global image tag for all Huly services
# ──────────────────────────────────────────────────────────────
hulyVersion: v0.7.382
# Docker registry prefix for Huly service images.
# Default "hardcoreeng" pulls from Docker Hub (hardcoreeng/front, etc.).
# Override to use a private registry, e.g. europe-west3-docker.pkg.dev/kendall-ledo/docker/huly
hulyRegistry: hardcoreeng
# ──────────────────────────────────────────────────────────────
# Secrets — leave empty to auto-generate (persists across upgrades)
# ──────────────────────────────────────────────────────────────
secrets:
# Shared JWT signing secret (auto-generated if empty)
serverSecret: ""
# Full storage config string override.
# When empty, auto-derived from storage.type + storage.s3.* (or random MinIO creds).
# Format: s3|https://s3.example.com?accessKey=X&secretKey=Y&region=us-east-1&rootBucket=data
# or: minio|minio?accessKey=<key>&secretKey=<secret>
storageConfig: ""
# CockroachDB password (auto-generated if empty)
cockroachPassword: ""
# Redpanda superuser password (auto-generated if empty)
redpandaPassword: ""
# CockroachDB connection URL (auto-derived from cockroachPassword if empty)
# Set this when using an external database (cockroach.enabled=false).
# Format: postgres://<user>:<pass>@<host>:26257/<db>
crDbUrl: ""
# OpenAI API key (required when aibot.enabled=true)
openaiApiKey: ""
# OpenAI base URL override (optional, e.g. for Azure OpenAI or proxies)
openaiBaseUrl: ""
# AI bot password (auto-generated if empty)
aibotPassword: ""
# ──────────────────────────────────────────────────────────────
# Authentication providers (set on the account service)
# At least one provider should be configured for user login.
# ──────────────────────────────────────────────────────────────
auth:
# Google OAuth 2.0
google:
clientId: ""
clientSecret: ""
# GitHub OAuth
github:
clientId: ""
clientSecret: ""
# OpenID Connect (generic OIDC provider)
oidc:
clientId: ""
clientSecret: ""
issuer: "" # e.g. https://accounts.google.com
# Set to true to prevent new user registration
disableSignup: false
# ──────────────────────────────────────────────────────────────
# Storage backend — built-in MinIO or external S3
# ──────────────────────────────────────────────────────────────
storage:
# "minio" = use built-in MinIO (default)
# "s3" = use external S3-compatible storage (disables built-in MinIO)
type: minio
s3:
endpoint: "" # e.g. https://s3.amazonaws.com
region: "" # e.g. us-east-1
accessKey: ""
secretKey: ""
# rootBucket: store all workspaces as prefixes inside a single bucket
rootBucket: "" # e.g. huly-data
# bucketPrefix: prefix prepended to per-workspace bucket names (only used if rootBucket is empty)
bucketPrefix: "" # e.g. huly-
# ──────────────────────────────────────────────────────────────
# Ingress
# ──────────────────────────────────────────────────────────────
ingress:
enabled: true
className: nginx
# Extra annotations merged into every ingress resource
annotations: {}
tls:
enabled: true
clusterIssuer: letsencrypt-prod
# ──────────────────────────────────────────────────────────────
# App settings (ConfigMap)
# ──────────────────────────────────────────────────────────────
appSettings:
modelEnabled: "*"
lastNameFirst: "true"
title: "Huly Self Host"
defaultLanguage: "en"
# Comma-separated list of admin email addresses
adminEmails: ""
# Desktop update channel
desktopChannel: "selfhost"
# ──────────────────────────────────────────────────────────────
# Global pod settings (applied to all services)
# ──────────────────────────────────────────────────────────────
global:
nodeSelector: {}
tolerations: []
affinity: {}
# ──────────────────────────────────────────────────────────────
# Infrastructure — set enabled: false to use external services
#
# When disabled, provide connection details via:
# cockroach: secrets.crDbUrl
# redpanda: external.redpanda
# elastic: external.elastic
# minio: storage.type=s3 + storage.s3.*
# mongodb: external.mongodb (only when aibot.enabled=true)
# ──────────────────────────────────────────────────────────────
cockroach:
enabled: true
image: cockroachdb/cockroach:latest-v24.2
database: defaultdb
username: selfhost
storage: 10Gi
storageClassName: ""
resources: {}
redpanda:
enabled: true
image: docker.redpanda.com/redpandadata/redpanda:v24.3.6
storage: 5Gi
storageClassName: ""
resources:
limits:
memory: 512Mi
elastic:
enabled: true
image: elasticsearch:7.14.2
storage: 10Gi
storageClassName: ""
javaOpts: "-Xms1024m -Xmx1024m"
resources: {}
minio:
# Automatically disabled when storage.type=s3
enabled: true
image: minio/minio
storage: 50Gi
storageClassName: ""
resources: {}
mongodb:
# Built-in MongoDB — only deployed when aibot.enabled=true
enabled: true
image: mongo:7
storage: 5Gi
storageClassName: ""
resources: {}
# ──────────────────────────────────────────────────────────────
# External infrastructure endpoints
# Used when the corresponding built-in service is disabled.
# ──────────────────────────────────────────────────────────────
external:
# Used when redpanda.enabled=false
redpanda: "" # e.g. kafka.example.com:9092
# Used when elastic.enabled=false
elastic: "" # e.g. https://elasticsearch.example.com:9200
# Used when mongodb.enabled=false (requires aibot.enabled=true)
mongodb: "" # e.g. mongodb://user:pass@host:27017
# ──────────────────────────────────────────────────────────────
# Application services
# ──────────────────────────────────────────────────────────────
front:
replicas: 1
resources: {}
account:
replicas: 1
resources:
limits:
memory: 512Mi
transactor:
replicas: 1
resources: {}
collaborator:
replicas: 1
resources:
limits:
memory: 512Mi
workspace:
replicas: 1
resources:
limits:
memory: 512Mi
fulltext:
replicas: 1
resources:
limits:
memory: 512Mi
rekoni:
replicas: 1
resources:
limits:
memory: 500Mi
stats:
replicas: 1
resources:
limits:
memory: 500Mi
kvs:
enabled: true
replicas: 1
resources: {}
githubIntegration:
# GitHub App integration — syncs issues, PRs, and comments with GitHub
enabled: false
replicas: 1
# Bot name shown on GitHub (must match the GitHub App's slug + [bot])
botName: ""
# GitHub App credentials (stored in the shared secret)
appId: ""
clientId: ""
clientSecret: ""
privateKey: "" # PEM-encoded private key (use multiline YAML |)
webhookSecret: ""
resources:
limits:
memory: 512Mi
aibot:
# AI bot service — requires OpenAI API key
enabled: false
replicas: 1
firstName: "Huly"
lastName: "AI"
# Override default models (leave empty for upstream defaults)
openaiModel: ""
openaiEmbeddingModel: ""
openaiTranslateModel: ""
openaiSummaryModel: ""
resources:
limits:
memory: 512Mi
# ──────────────────────────────────────────────────────────────
# Backup — nightly CronJobs to S3-compatible storage
# ──────────────────────────────────────────────────────────────
backup:
enabled: false
schedule: "0 2 * * *" # 2 AM daily
retentionDays: 30
rcloneImage: rclone/rclone:latest
s3:
endpoint: "" # e.g. https://nbg1.your-objectstorage.com
region: ""
bucket: "" # e.g. ledo-backups
pathPrefix: "" # e.g. huly-ledo
accessKey: ""
secretKey: ""
# Secondary credentials for rotation (optional)
secondaryAccessKey: ""
secondarySecretKey: ""
# Which credential set is active: "primary" or "secondary"
activeCredential: "primary"
cockroachdb:
enabled: true
schedule: "" # override global schedule
files:
enabled: true
schedule: ""
mongodb:
enabled: true # only runs when aibot.enabled=true
schedule: ""