Files
multica/deploy/helm/multica/values.yaml
feifeigood 30e1e546cf MUL-3355: feat(helm): support external PostgreSQL via postgres.external.enabled
Closes #4237

Add a `postgres.external.enabled` toggle (default: false) so operators
can skip the built-in Deployment/PVC/Service and point Multica at any
externally managed PostgreSQL (CNPG, RDS, Cloud SQL, Neon, etc.).

When enabled:
- postgres Deployment, PVC, and ClusterIP Service are not rendered
- The hard-coded DATABASE_URL env var is omitted from the backend pod;
  DATABASE_URL must be supplied in existingSecret and reaches the
  container through the existing envFrom.secretRef mechanism
- POSTGRES_DB / POSTGRES_USER are omitted from the ConfigMap (unused
  when DATABASE_URL is provided directly)

Also update the existingSecret creation example in values.yaml to show
both the built-in and external-postgres variants so users know to add
DATABASE_URL instead of POSTGRES_PASSWORD when switching modes.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-03 13:56:10 +08:00

194 lines
7.7 KiB
YAML

# -----------------------------------------------------------------------------
# Container images
# -----------------------------------------------------------------------------
images:
backend:
repository: ghcr.io/multica-ai/multica-backend
# Empty defaults to Chart.appVersion. Released OCI charts set appVersion to
# the Git tag (for example v0.3.5), so installs use matching app images.
tag: ""
pullPolicy: IfNotPresent
frontend:
repository: ghcr.io/multica-ai/multica-web
# Empty defaults to Chart.appVersion. Set explicitly to override.
tag: ""
pullPolicy: IfNotPresent
postgres:
repository: pgvector/pgvector
tag: pg17
pullPolicy: IfNotPresent
# -----------------------------------------------------------------------------
# Pre-created Secret with sensitive values. Create it before `helm install`:
#
# Built-in postgres (default):
# kubectl -n <namespace> create secret generic multica-secrets \
# --from-literal=JWT_SECRET="$(openssl rand -hex 32)" \
# --from-literal=POSTGRES_PASSWORD="$(openssl rand -hex 16)" \
# --from-literal=RESEND_API_KEY="" \
# --from-literal=GOOGLE_CLIENT_SECRET="" \
# --from-literal=CLOUDFRONT_PRIVATE_KEY="" \
# --from-literal=MULTICA_DEV_VERIFICATION_CODE=""
#
# External postgres (postgres.external.enabled=true):
# Replace POSTGRES_PASSWORD with DATABASE_URL pointing at your external cluster.
# kubectl -n <namespace> create secret generic multica-secrets \
# --from-literal=JWT_SECRET="$(openssl rand -hex 32)" \
# --from-literal=DATABASE_URL="postgres://user:pass@host:5432/multica?sslmode=require" \
# --from-literal=RESEND_API_KEY="" \
# --from-literal=GOOGLE_CLIENT_SECRET="" \
# --from-literal=CLOUDFRONT_PRIVATE_KEY="" \
# --from-literal=MULTICA_DEV_VERIFICATION_CODE=""
#
# The chart references this Secret by name; it does not template it, so real
# values never need to land in git.
# -----------------------------------------------------------------------------
existingSecret: multica-secrets
# -----------------------------------------------------------------------------
# PostgreSQL (pgvector)
# -----------------------------------------------------------------------------
postgres:
# Set external.enabled=true to skip the built-in postgres Deployment/PVC and
# supply DATABASE_URL directly via the existingSecret instead. When true,
# the postgres Deployment, PVC, and Service are not created.
# Add DATABASE_URL to your existingSecret pointing at the external cluster.
external:
enabled: false
database: multica
user: multica
persistence:
size: 10Gi
# Leave empty to use the cluster's default StorageClass.
storageClass: ""
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
cpu: 1000m
memory: 1Gi
# -----------------------------------------------------------------------------
# Backend (Go API + WS server)
# -----------------------------------------------------------------------------
backend:
# Concurrent backend pods are now safe to start: cmd/migrate uses a
# Postgres session-level advisory lock to serialize the startup
# `migrate up` run, so late-arriving pods queue and then no-op (see
# GitHub multica-ai/multica#3647). 1 is still the recommended default
# because the chart Deployment uses strategy: Recreate and there is no
# leader-elected workload split — increase this only if you actually
# need horizontal scale of the API/WS server.
replicas: 1
uploads:
persistence:
# When true (default) the chart provisions a PersistentVolumeClaim for
# /app/data/uploads and mounts it into the backend pod. The backend only
# uses this directory when S3 is NOT configured (`backend.config.s3Bucket`
# empty) — see server/cmd/server/router.go for the storage selection.
#
# Set to false when you have configured S3/CloudFront and don't want the
# chart to declare the uploads PVC at all. The default access mode is
# ReadWriteOnce, which prevents `backend.replicas > 1` from scheduling a
# second pod (Multi-Attach error); disabling persistence — or switching
# accessModes to a ReadWriteMany class — removes that constraint on the
# storage side. Note: backend startup migrations are not yet serialized
# across pods, so multi-replica still requires care beyond the chart.
enabled: true
size: 5Gi
# Leave empty to use the cluster's default StorageClass.
storageClass: ""
# PVC accessModes. Defaults to [ReadWriteOnce] to match the typical
# local-path / hostPath / EBS StorageClass on a single-node setup. Switch
# to [ReadWriteMany] (with a compatible StorageClass such as NFS, EFS,
# CephFS) if you intend to share the uploads volume across multiple
# backend replicas without S3.
accessModes:
- ReadWriteOnce
# All non-secret backend env. Secret values come from `existingSecret`.
config:
appEnv: production
appUrl: http://multica.dev.lan
frontendOrigin: http://multica.dev.lan
corsAllowedOrigins: ""
cookieDomain: ""
resendFromEmail: noreply@multica.ai
allowSignup: true
allowedEmails: ""
allowedEmailDomains: ""
# Self-host gate (#3433): set true to make POST /api/workspaces 403 for
# every caller. Bootstrap the workspace with this false, then flip to
# true so users can only join via invitation.
disableWorkspaceCreation: false
googleClientId: ""
googleRedirectUri: http://multica.dev.lan/auth/callback
s3Bucket: ""
s3Region: us-west-2
cloudfrontDomain: ""
cloudfrontKeyPairId: ""
localUploadBaseUrl: http://api.multica.dev.lan
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
cpu: 1000m
memory: 1Gi
# -----------------------------------------------------------------------------
# Frontend (Next.js standalone)
#
# The multica-web image bakes REMOTE_API_URL=http://backend:8080 at build time;
# the chart ships an ExternalName Service named "backend" so that bare host
# resolves to the in-cluster backend Service.
# -----------------------------------------------------------------------------
frontend:
replicas: 1
# Compatibility shim for the prebuilt multica-web image.
compatibility:
# When true (default) the chart creates an ExternalName Service literally
# named "backend" so the REMOTE_API_URL=http://backend:8080 baked into the
# web image resolves in-cluster. Because that name is unprefixed, only ONE
# release of this chart can run per namespace, and it will collide with any
# pre-existing Service/backend (helm install then fails without
# --take-ownership). Set to false if you run a web image built with a
# patched REMOTE_API_URL and don't need the alias.
backendAlias: true
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
cpu: 1000m
memory: 1Gi
# -----------------------------------------------------------------------------
# Ingress
# -----------------------------------------------------------------------------
ingress:
enabled: true
className: traefik
annotations: {}
frontend:
host: multica.dev.lan
backend:
host: api.multica.dev.lan
# tls:
# - hosts: [multica.dev.lan, api.multica.dev.lan]
# secretName: multica-tls
# -----------------------------------------------------------------------------
# Monitoring
# -----------------------------------------------------------------------------
monitoring:
prometheusRule:
# Requires the Prometheus Operator CRD (monitoring.coreos.com/v1). Kept
# disabled by default so minimal self-host installs without that CRD still
# render and install.
enabled: false
additionalLabels: {}
samplerQueryErrorsFor: 5m
samplerQueryLatencyFor: 10m
severity: warning