mirror of
https://github.com/dograh-hq/dograh.git
synced 2026-07-16 11:31:04 +02:00
* fix(web): honor X-Forwarded-Proto in uvicorn so request.url is https behind a reverse proxy ## Problem When Dograh runs behind a TLS-terminating reverse proxy (Cloudflare → Traefik in Kubernetes, nginx in the docker-compose install), the inside of the cluster/host is plain HTTP. Uvicorn defaults to trusting `scope["scheme"]` from the socket, so `request.url.scheme` reads `http` even though the client dialed `https`. That breaks any code path that hashes or echoes the request URL back to the caller. Concrete symptom seen in production: **Vobiz inbound webhook signatures fail with "signature validation failed for vobiz"** because Vobiz computes HMAC over the URL it dialed (`https://.../inbound/run`) while Dograh recomputes it as `http://...`. Log excerpt from the failing call: ``` WARNING | provider.py | Vobiz webhook signature mismatch. Expected: daOpAZPm..., Got: 1+eW/RxE... WARNING | telephony.py | /inbound/run: signature validation failed for vobiz ``` Twilio, Plivo and any other provider that signs over the callback URL have the same failure mode when Dograh is deployed behind a proxy. ## Fix Start uvicorn with `--proxy-headers --forwarded-allow-ips="*"` in `scripts/run_web.sh`. Uvicorn rewrites `scope["scheme"]` and client address from `X-Forwarded-Proto` / `X-Forwarded-For` when the request originates from a trusted upstream — Traefik and Cloudflare set both correctly, so `request.url.scheme == "https"` inside the app once again and provider signature checks pass. Verified end-to-end on a production k3s install (Traefik + Cloudflare edge → dograh-web pod) — after the change, the very next Vobiz inbound webhook validated successfully and the call connected past the previous 11-second signature-failure hangup. * address review: let operators narrow FORWARDED_ALLOW_IPS Both bot reviewers on #515 flagged `--forwarded-allow-ips="*"` as a defence-in-depth concern: if uvicorn is directly reachable from an untrusted network (bypassing the proxy), any client can spoof `X-Forwarded-Proto` / `X-Forwarded-For`, and uvicorn will rewrite `request.client` / `request.url` from those attacker-controlled headers. Fix: consume `FORWARDED_ALLOW_IPS` from the environment (uvicorn already recognizes this env var; see `deploy/hostinger/docker-compose.yaml:179` for the existing precedent). Default stays `"*"` so the behavior of the original fix is preserved for the standard docker-compose / helm layouts where the app pod is only reachable via the proxy Service. Operators who terminate uvicorn on a host that's also reachable directly can narrow it to the proxy CIDR: FORWARDED_ALLOW_IPS="10.42.0.0/16" ./scripts/run_web.sh * address review: declare FORWARDED_ALLOW_IPS in the helm chart, not the script uvicorn already enables proxy-header handling by default and falls back to the FORWARDED_ALLOW_IPS env var when --forwarded-allow-ips is absent, so the CLI flags were redundant and the script-level "*" default hid a security-relevant trust decision away from operators. Drop the flags, keep run_web.sh deployment-agnostic, and declare the env var where the other deployment config lives — web.forwardedAllowIps in values.yaml (default "*", narrowable to a proxy CIDR) — mirroring how docker-compose already sets it on the api service. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * simplify run_web.sh comment Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: prabhat pankaj <prabhatiitbhu@gmail.com> Co-authored-by: Abhishek Kumar <abhishek@a6k.me> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
581 lines
20 KiB
YAML
581 lines
20 KiB
YAML
# Dograh Helm chart — default values.
|
|
#
|
|
# Conventions:
|
|
# - "mode" fields are enums; see values.schema.json for allowed values.
|
|
# - Anything sensitive (passwords, tokens, signing keys) is split into the
|
|
# `secrets:` section and rendered as a Kubernetes Secret. Non-sensitive
|
|
# config lives in `config:` and renders as a ConfigMap.
|
|
# - The chart never ships real defaults for credentials. Operators must
|
|
# override `secrets.*` (or supply an existing Secret name).
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# Global image config — applied to web, workers, ariManager, campaignOrchestrator
|
|
# -----------------------------------------------------------------------------
|
|
image:
|
|
registry: docker.io
|
|
repository: dograhai/dograh-api
|
|
tag: latest
|
|
pullPolicy: IfNotPresent
|
|
|
|
imagePullSecrets: []
|
|
# - name: regcred
|
|
|
|
nameOverride: ""
|
|
fullnameOverride: ""
|
|
|
|
serviceAccount:
|
|
create: true
|
|
name: ""
|
|
annotations: {}
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# Stateful dependency modes.
|
|
#
|
|
# database.mode:
|
|
# internal — bundled Postgres manifest (pgvector image; see `postgresql` below)
|
|
# external — operator supplies DATABASE_URL via secrets.databaseUrl
|
|
# redis.mode:
|
|
# internal — bundled Redis manifest (see `redisinternal` below)
|
|
# external — operator supplies REDIS_URL via secrets.redisUrl
|
|
# storage.mode:
|
|
# internalMinio — bundled MinIO manifest (see `minio` below)
|
|
# externalMinio — operator supplies a MinIO-compatible endpoint + creds
|
|
# s3 — sets ENABLE_AWS_S3=true; uses AWS S3
|
|
# exposure.mode:
|
|
# gatewayApi — renders Gateway + HTTPRoute (gateway.networking.k8s.io/v1)
|
|
# ingress — renders Ingress resources (networking.k8s.io/v1)
|
|
# -----------------------------------------------------------------------------
|
|
database:
|
|
mode: internal
|
|
# For external mode, secrets.databaseUrl must be set.
|
|
|
|
redis:
|
|
mode: internal
|
|
# For external mode, secrets.redisUrl must be set.
|
|
|
|
storage:
|
|
mode: internalMinio
|
|
# For externalMinio mode, set externalMinio.endpoint + secrets.minioAccessKey
|
|
# + secrets.minioSecretKey.
|
|
externalMinio:
|
|
endpoint: "" # e.g. minio.example.com
|
|
publicEndpoint: "" # browser-visible URL
|
|
secure: false
|
|
bucket: voice-audio
|
|
# For s3 mode, set s3.region. AWS credentials are picked up from the pod's
|
|
# IAM role (IRSA recommended) or from secrets.awsAccessKeyId + secrets.awsSecretAccessKey.
|
|
s3:
|
|
region: us-east-1
|
|
bucket: voice-audio
|
|
publicEndpoint: "" # e.g. https://s3.amazonaws.com
|
|
|
|
exposure:
|
|
# Default is `ingress` because it works out-of-the-box on any cluster
|
|
# without requiring Gateway API CRDs. Production deployments should
|
|
# prefer `gatewayApi` per HELM_DEPLOYMENT_PLAN.md — switch the mode
|
|
# and supply gatewayClassName.
|
|
mode: ingress
|
|
# Gateway API config (when mode=gatewayApi).
|
|
gatewayApi:
|
|
# Set to false to skip rendering the Gateway resource and instead
|
|
# attach HTTPRoutes to a pre-existing Gateway (parentRef.name below).
|
|
createGateway: true
|
|
gatewayClassName: "" # required when createGateway=true (e.g. "istio", "envoy-gateway", "aws-alb")
|
|
listenerHostname: "" # optional SNI hostname for the listener; empty = wildcard
|
|
# Reference an existing Gateway instead of creating one.
|
|
# Ignored when createGateway=true.
|
|
parentRefs:
|
|
- name: dograh
|
|
namespace: "" # empty = same namespace as the release
|
|
# Ingress config (when mode=ingress).
|
|
ingress:
|
|
className: "" # e.g. "nginx", "alb"
|
|
annotations: {}
|
|
# Hostname for the API/UI. UI is served at / and API under /api/.
|
|
# MinIO browser-visible path uses the same hostname under /voice-audio/.
|
|
host: "" # e.g. dograh.example.com
|
|
tls:
|
|
enabled: false
|
|
secretName: "" # operator-managed TLS secret in the release namespace
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# Non-sensitive runtime config — rendered into a ConfigMap and injected via
|
|
# envFrom on every backend pod. Sensitive values live under `secrets:` below.
|
|
# -----------------------------------------------------------------------------
|
|
config:
|
|
environment: production
|
|
logLevel: INFO
|
|
backendApiEndpoint: "" # public URL the browser uses to reach the API; auto-derived from exposure.host if empty in NOTES
|
|
minioBucket: voice-audio
|
|
minioEndpoint: "" # internal cluster endpoint (auto-set when internalMinio)
|
|
minioPublicEndpoint: "" # browser-visible endpoint (auto-set when ingress/gateway path exposes MinIO)
|
|
minioSecure: false
|
|
enableAwsS3: false
|
|
enableTelemetry: true
|
|
posthogHost: https://us.i.posthog.com
|
|
posthogApiKey: phc_ItizB1dP6yv7ZYobbcqrpxTdbomDA8hJFSEmAMdYvIr
|
|
forceTurnRelay: false
|
|
turnHost: "" # public hostname/IP of coturn (the LoadBalancer address)
|
|
fastapiWorkers: 1 # informational only; web tier scales by pod, not in-pod workers
|
|
enableSignup: true # set false to 403 the /api/v1/auth/signup endpoint (invite-only lockdown)
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# Secrets — rendered into a Kubernetes Secret unless secrets.existingSecret is
|
|
# set. NEVER commit real values here; override via -f overrides.yaml or
|
|
# --set-string at install time.
|
|
# -----------------------------------------------------------------------------
|
|
secrets:
|
|
# If set, the chart skips rendering its own Secret and assumes this Secret
|
|
# already exists in the release namespace with all keys below.
|
|
existingSecret: ""
|
|
|
|
# Required when database.mode=external.
|
|
databaseUrl: "" # e.g. postgresql+asyncpg://user:pass@host:5432/dograh
|
|
# Required when redis.mode=external.
|
|
redisUrl: "" # e.g. redis://:pass@host:6379
|
|
|
|
# MinIO / S3 credentials.
|
|
minioAccessKey: ""
|
|
minioSecretKey: ""
|
|
awsAccessKeyId: "" # only used when storage.mode=s3 and not using IRSA
|
|
awsSecretAccessKey: ""
|
|
|
|
# JWT signing key for the OSS auth path. MUST be overridden in production.
|
|
ossJwtSecret: "ChangeMeInProduction"
|
|
|
|
# TURN REST API shared secret (matches coturn.staticAuthSecret below).
|
|
turnSecret: ""
|
|
|
|
# Optional Langfuse tracing.
|
|
langfuseSecretKey: ""
|
|
langfusePublicKey: ""
|
|
langfuseHost: ""
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# Web tier (FastAPI + WebSocket signaling)
|
|
# -----------------------------------------------------------------------------
|
|
web:
|
|
replicaCount: 2
|
|
port: 8000
|
|
|
|
# Peers uvicorn trusts X-Forwarded-Proto / X-Forwarded-For from (exported
|
|
# as FORWARDED_ALLOW_IPS). The ingress proxy reaches the pod from a
|
|
# pod-network IP — not loopback — so uvicorn's 127.0.0.1 default ignores
|
|
# the headers, request.url reads back as http://, and telephony providers
|
|
# that sign their webhook URL (Vobiz, Twilio, Plivo) fail signature
|
|
# validation. "*" trusts every peer, which is fine while only the ingress
|
|
# can reach the pod; narrow to your proxy/pod CIDR (e.g. "10.42.0.0/16")
|
|
# if the pod is reachable from untrusted networks.
|
|
forwardedAllowIps: "*"
|
|
|
|
# Long-lived signaling WebSockets keep per-connection state in process
|
|
# memory (api/routes/webrtc_signaling.py). A naive pod restart drops every
|
|
# in-flight call. The two settings below give the gateway time to stop
|
|
# routing new connections to a terminating pod and give in-flight calls
|
|
# time to finish.
|
|
terminationGracePeriodSeconds: 600
|
|
# preStop sleep: long enough for the load balancer to observe the pod going
|
|
# NotReady and stop sending new connections. 15s is conservative for most
|
|
# controllers (gateway/nginx/ALB).
|
|
preStopSleepSeconds: 15
|
|
|
|
resources:
|
|
# These are conservative starting numbers. Tune to your workload —
|
|
# WebRTC signaling is mostly idle but bursty during call setup.
|
|
requests:
|
|
cpu: 200m
|
|
memory: 512Mi
|
|
limits:
|
|
cpu: "2"
|
|
memory: 2Gi
|
|
|
|
# Distinct probes so the pod can fail readiness during drain without being
|
|
# killed for liveness. liveness has a longer threshold (process is alive)
|
|
# while readiness flips quickly (stop receiving new connections).
|
|
livenessProbe:
|
|
httpGet:
|
|
path: /api/v1/health
|
|
port: 8000
|
|
initialDelaySeconds: 30
|
|
periodSeconds: 30
|
|
timeoutSeconds: 5
|
|
failureThreshold: 6
|
|
readinessProbe:
|
|
httpGet:
|
|
path: /api/v1/health
|
|
port: 8000
|
|
initialDelaySeconds: 5
|
|
periodSeconds: 5
|
|
timeoutSeconds: 3
|
|
failureThreshold: 2
|
|
|
|
service:
|
|
type: ClusterIP
|
|
port: 8000
|
|
annotations: {}
|
|
|
|
pdb:
|
|
enabled: true
|
|
minAvailable: 1
|
|
|
|
podAnnotations: {}
|
|
nodeSelector: {}
|
|
tolerations: []
|
|
# Recommend spreading web pods across zones / nodes.
|
|
topologySpreadConstraints: []
|
|
affinity: {}
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# ARQ background workers
|
|
# -----------------------------------------------------------------------------
|
|
workers:
|
|
replicaCount: 1
|
|
|
|
resources:
|
|
requests:
|
|
cpu: 100m
|
|
memory: 256Mi
|
|
limits:
|
|
cpu: "1"
|
|
memory: 1Gi
|
|
|
|
# exec probe — workers have no HTTP endpoint.
|
|
livenessProbe:
|
|
exec:
|
|
# The entrypoint exec's the worker, so it runs as PID 1; grep its cmdline.
|
|
# Avoids pgrep/procps, which isn't in the slim runtime image. Matching a
|
|
# single argv token (no spaces) — argv is NUL-separated in /proc.
|
|
command: ["sh", "-c", "grep -qa api.tasks.arq.WorkerSettings /proc/1/cmdline"]
|
|
initialDelaySeconds: 30
|
|
periodSeconds: 30
|
|
timeoutSeconds: 5
|
|
failureThreshold: 3
|
|
|
|
podAnnotations: {}
|
|
nodeSelector: {}
|
|
tolerations: []
|
|
affinity: {}
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# ARI manager — TELEPHONY SINGLETON
|
|
#
|
|
# Maintains an outbound WebSocket to Asterisk and is the canonical receiver of
|
|
# ARI events. Running >1 replica produces duplicate event handling. The chart
|
|
# hard-codes replicas:1 and strategy:Recreate; there is NO replica knob here
|
|
# on purpose. Add proper leader election before relaxing this.
|
|
# -----------------------------------------------------------------------------
|
|
ariManager:
|
|
enabled: true
|
|
|
|
resources:
|
|
requests:
|
|
cpu: 100m
|
|
memory: 256Mi
|
|
limits:
|
|
cpu: 500m
|
|
memory: 512Mi
|
|
|
|
livenessProbe:
|
|
exec:
|
|
# PID 1 cmdline check (procps-free; see workers.livenessProbe).
|
|
command: ["sh", "-c", "grep -qa api.services.telephony.ari_manager /proc/1/cmdline"]
|
|
initialDelaySeconds: 30
|
|
periodSeconds: 30
|
|
timeoutSeconds: 5
|
|
failureThreshold: 3
|
|
|
|
podAnnotations: {}
|
|
nodeSelector: {}
|
|
tolerations: []
|
|
affinity: {}
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# Campaign orchestrator — CAMPAIGN SINGLETON
|
|
#
|
|
# Uses in-memory deduplication locks (api/services/campaign/campaign_orchestrator.py
|
|
# `_processing_locks`). Running >1 replica would silently break scheduling.
|
|
# Same singleton rules as ariManager: no replica knob, Recreate strategy.
|
|
# -----------------------------------------------------------------------------
|
|
campaignOrchestrator:
|
|
enabled: true
|
|
|
|
resources:
|
|
requests:
|
|
cpu: 100m
|
|
memory: 256Mi
|
|
limits:
|
|
cpu: 500m
|
|
memory: 512Mi
|
|
|
|
livenessProbe:
|
|
exec:
|
|
# PID 1 cmdline check (procps-free; see workers.livenessProbe).
|
|
command: ["sh", "-c", "grep -qa api.services.campaign.campaign_orchestrator /proc/1/cmdline"]
|
|
initialDelaySeconds: 30
|
|
periodSeconds: 30
|
|
timeoutSeconds: 5
|
|
failureThreshold: 3
|
|
|
|
podAnnotations: {}
|
|
nodeSelector: {}
|
|
tolerations: []
|
|
affinity: {}
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# Next.js UI
|
|
# -----------------------------------------------------------------------------
|
|
ui:
|
|
enabled: true
|
|
replicaCount: 2
|
|
|
|
image:
|
|
registry: docker.io
|
|
repository: dograhai/dograh-ui
|
|
tag: latest
|
|
pullPolicy: IfNotPresent
|
|
|
|
port: 3010
|
|
|
|
# Server-side (SSR) URL. Defaults to the in-cluster web Service.
|
|
backendUrl: "" # auto-set in template when empty
|
|
|
|
resources:
|
|
requests:
|
|
cpu: 100m
|
|
memory: 256Mi
|
|
limits:
|
|
cpu: 500m
|
|
memory: 512Mi
|
|
|
|
livenessProbe:
|
|
httpGet:
|
|
path: /
|
|
port: 3010
|
|
initialDelaySeconds: 30
|
|
periodSeconds: 30
|
|
timeoutSeconds: 5
|
|
failureThreshold: 3
|
|
readinessProbe:
|
|
httpGet:
|
|
path: /
|
|
port: 3010
|
|
initialDelaySeconds: 5
|
|
periodSeconds: 10
|
|
timeoutSeconds: 3
|
|
failureThreshold: 2
|
|
|
|
service:
|
|
type: ClusterIP
|
|
port: 3010
|
|
|
|
pdb:
|
|
enabled: true
|
|
minAvailable: 1
|
|
|
|
podAnnotations: {}
|
|
nodeSelector: {}
|
|
tolerations: []
|
|
affinity: {}
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# coturn — TURN media relay
|
|
# -----------------------------------------------------------------------------
|
|
coturn:
|
|
enabled: true
|
|
|
|
image:
|
|
registry: docker.io
|
|
repository: coturn/coturn
|
|
tag: "4.8.0"
|
|
pullPolicy: IfNotPresent
|
|
|
|
# External IP advertised by coturn for NAT traversal. This is the
|
|
# LoadBalancer IP of the coturn Service. There is a chicken-and-egg here:
|
|
# the LB IP may not be known until after install. See NOTES.txt for the
|
|
# supported workflow (install with placeholder, kubectl get svc, helm
|
|
# upgrade --set coturn.externalIp=<IP>).
|
|
externalIp: ""
|
|
|
|
realm: dograh.com
|
|
|
|
# Coturn uses TURN REST API authentication (HMAC-SHA1). The secret here
|
|
# MUST match secrets.turnSecret — the chart will warn at install time if
|
|
# they diverge.
|
|
staticAuthSecretFromSecretsKey: turnSecret
|
|
|
|
# Relay port range. AWS NLB has a default quota of 50 listeners per LB,
|
|
# so the default 49 ports (49152-49200) sits just inside the limit.
|
|
# Increasing this requires either a higher NLB listener quota or
|
|
# additional TURN deployments.
|
|
relayPortRange:
|
|
min: 49152
|
|
max: 49200
|
|
|
|
# Standard TURN ports.
|
|
ports:
|
|
plain: 3478
|
|
tls: 5349
|
|
|
|
# TLS for turns:// — NOT WIRED IN v1. The original docker-compose exposes
|
|
# 5349 but does not configure cert paths. v1 scopes to plain TURN over
|
|
# UDP/TCP. See README.md "Open TODOs".
|
|
tls:
|
|
enabled: false
|
|
|
|
service:
|
|
type: LoadBalancer
|
|
annotations: {}
|
|
# externalTrafficPolicy: Local preserves the client IP, which TURN auth
|
|
# benefits from. Some LBs need this set to "Cluster" to be reachable.
|
|
externalTrafficPolicy: Local
|
|
|
|
resources:
|
|
requests:
|
|
cpu: 200m
|
|
memory: 256Mi
|
|
limits:
|
|
cpu: "2"
|
|
memory: 1Gi
|
|
|
|
podAnnotations: {}
|
|
nodeSelector: {}
|
|
tolerations: []
|
|
affinity: {}
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# Migration Job
|
|
# -----------------------------------------------------------------------------
|
|
migrate:
|
|
# Run alembic upgrade head as a Helm hook: post-install (so the bundled
|
|
# Postgres exists first) and pre-upgrade (so migrations land before new code).
|
|
enabled: true
|
|
|
|
# Hard cap on how long a migration may run. A failed/exceeded migration
|
|
# rolls back the install/upgrade because backoffLimit is 0.
|
|
activeDeadlineSeconds: 600
|
|
|
|
resources:
|
|
requests:
|
|
cpu: 100m
|
|
memory: 256Mi
|
|
limits:
|
|
cpu: 500m
|
|
memory: 512Mi
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# Horizontal Pod Autoscaling.
|
|
#
|
|
# All tiers default to `enabled: false`; opt in per tier. Requires
|
|
# metrics-server in the cluster. HPA reads Deployment `replicas` on first sync
|
|
# and then owns it, so `web.replicaCount` etc. are ignored once the
|
|
# corresponding autoscaling block is enabled — treat `minReplicas` as the new
|
|
# floor. Enabling a tier's HPA also removes `replicas` from its Deployment, so
|
|
# on the first upgrade after enabling, the tier briefly resets to 1 pod until
|
|
# the HPA reconciles — enable during a quiet window.
|
|
#
|
|
# WARNING (web): CPU/memory is a poor signal for WebRTC signaling workloads.
|
|
# WebSockets are long-lived, low-CPU, and steady-memory; CPU will look flat
|
|
# while you saturate per-pod connection limits. Replace this with a custom
|
|
# metric (active WS connections, active calls) once one is exposed.
|
|
#
|
|
# Workers (ARQ): jobs are mostly IO-bound (webhook delivery, embeddings, LLM
|
|
# calls), so CPU can stay flat while the queue backs up. CPU HPA is a coarse
|
|
# stopgap; the long-term plan is queue-depth / active-call driven scaling via
|
|
# KEDA or a custom metric (see README TODOs). If an external scaler (e.g. a
|
|
# KEDA ScaledObject) owns the worker Deployment, keep this block disabled so
|
|
# the chart does not render a competing HPA.
|
|
#
|
|
# UI (Next.js SSR) correlates reasonably with CPU; resource-metric HPA is a
|
|
# fine choice there.
|
|
# -----------------------------------------------------------------------------
|
|
autoscaling:
|
|
web:
|
|
enabled: false
|
|
minReplicas: 2
|
|
maxReplicas: 10
|
|
targetCPUUtilizationPercentage: 70
|
|
targetMemoryUtilizationPercentage: 80
|
|
workers:
|
|
enabled: false
|
|
minReplicas: 1
|
|
maxReplicas: 5
|
|
targetCPUUtilizationPercentage: 70
|
|
# NOTE: memory HPA disabled by default. Idle Python (ARQ worker) at the
|
|
# chart's default 256Mi request sits near the 80% target on cold start,
|
|
# so HPA would scale to maxReplicas with no real load. Enable only after
|
|
# sizing your workload's steady-state memory well below the target.
|
|
targetMemoryUtilizationPercentage: null
|
|
ui:
|
|
enabled: false
|
|
# Floor of 2 matches ui.replicaCount and keeps ui.pdb (minAvailable: 1)
|
|
# satisfiable during node drains. Single-node installs that drop this to 1
|
|
# should also disable ui.pdb (see examples/values-k3s-prod.yaml).
|
|
minReplicas: 2
|
|
maxReplicas: 5
|
|
targetCPUUtilizationPercentage: 70
|
|
# Same reason as workers — idle Next.js SSR sits close to the 256Mi request.
|
|
targetMemoryUtilizationPercentage: null
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# Bundled stateful deps for the internal/all-in-one modes. These are plain
|
|
# in-chart manifests (templates/internal-*.yaml) on official upstream images —
|
|
# not subcharts. Each renders only in its internal mode:
|
|
# postgresql -> database.mode == internal
|
|
# redisinternal-> redis.mode == internal
|
|
# minio -> storage.mode == internalMinio
|
|
# For production, use the external/managed modes instead (see database/redis/
|
|
# storage above).
|
|
# -----------------------------------------------------------------------------
|
|
postgresql:
|
|
# Official pgvector image = upstream Postgres + the `vector` extension the app
|
|
# migrations require. Keep the tag on a supported Postgres major.
|
|
image:
|
|
registry: docker.io
|
|
repository: pgvector/pgvector
|
|
tag: pg17
|
|
pullPolicy: IfNotPresent
|
|
auth:
|
|
username: dograh # also the initdb superuser (needed for CREATE EXTENSION)
|
|
password: "" # auto-generated and persisted across upgrades if empty
|
|
database: dograh
|
|
persistence:
|
|
size: 8Gi
|
|
storageClass: "" # "" = cluster default StorageClass
|
|
resources:
|
|
requests: { cpu: 100m, memory: 256Mi }
|
|
limits: { cpu: "1", memory: 1Gi }
|
|
|
|
redisinternal:
|
|
# Official upstream Redis. Single node, password-protected, AOF persistence.
|
|
# (Key kept as `redisinternal` so it doesn't collide with `redis.mode` above.)
|
|
image:
|
|
registry: docker.io
|
|
repository: redis
|
|
tag: 7.4-alpine
|
|
pullPolicy: IfNotPresent
|
|
auth:
|
|
password: "" # auto-generated and persisted across upgrades if empty
|
|
persistence:
|
|
size: 8Gi
|
|
storageClass: ""
|
|
resources:
|
|
requests: { cpu: 50m, memory: 64Mi }
|
|
limits: { cpu: 500m, memory: 256Mi }
|
|
|
|
minio:
|
|
# Official upstream MinIO. Root creds are shared with the app (single source of
|
|
# truth) so they can't drift; the app creates its bucket on first use.
|
|
image:
|
|
registry: docker.io
|
|
repository: minio/minio
|
|
tag: RELEASE.2025-04-22T22-12-26Z
|
|
pullPolicy: IfNotPresent
|
|
auth:
|
|
rootUser: minioadmin
|
|
rootPassword: "" # auto-generated and persisted across upgrades if empty
|
|
persistence:
|
|
size: 20Gi
|
|
storageClass: ""
|
|
resources:
|
|
requests: { cpu: 100m, memory: 256Mi }
|
|
limits: { cpu: "1", memory: 1Gi }
|