From 425d91c49b1b94d8697ad3cacb8145368c0e85f6 Mon Sep 17 00:00:00 2001 From: CHANGE Date: Fri, 3 Apr 2026 17:31:02 -0400 Subject: [PATCH 01/13] Migrate from Bitnami Keycloak chart to Codecentric KeycloakX Replace the deprecated Bitnami Keycloak Helm subchart (v24.7.5, bitnamilegacy/keycloak) with the Codecentric KeycloakX chart (v7.1.9) using the official quay.io/keycloak/keycloak:26.5.5 image. Key changes: - Use alias: keycloak in Chart.yaml to preserve .Values.keycloak.* paths - Restructure values from Bitnami format (externalDatabase, extraEnvVars, auth) to KeycloakX format (database, extraEnv, KEYCLOAK_ADMIN env var) - Replace custom wait-for-db init container with built-in dbchecker - Update ingress from Bitnami hostname/secrets to standard rules/tls - Update service URL from http://keycloak to http://keycloak-http - Update all Replicated sections (proxy, external_postgres, local registry) - Remove keycloakConfigCli and embedded PostgreSQL subchart references Migration note: existing StatefulSet must be deleted before upgrade (kubectl delete statefulset keycloak --cascade=orphan) due to selector label changes. --- .../templates/keycloak-realm.yaml | 2 +- charts/openhands/Chart.lock | 15 ++- charts/openhands/Chart.yaml | 7 +- charts/openhands/example-values.yaml | 10 +- .../openhands/templates/_init-containers.yaml | 2 +- .../troubleshoot/support-bundle.yaml | 2 +- charts/openhands/values.yaml | 126 +++++++++--------- replicated/application.yaml | 2 +- replicated/openhands.yaml | 97 +++++++++----- 9 files changed, 150 insertions(+), 113 deletions(-) diff --git a/charts/openhands-secrets/templates/keycloak-realm.yaml b/charts/openhands-secrets/templates/keycloak-realm.yaml index 326835f0..79f68db5 100644 --- a/charts/openhands-secrets/templates/keycloak-realm.yaml +++ b/charts/openhands-secrets/templates/keycloak-realm.yaml @@ -6,7 +6,7 @@ metadata: type: Opaque data: realm-name: {{ .Values.config.keycloak_realm_name | b64enc | quote }} - server-url: {{ "http://keycloak" | b64enc | quote }} + server-url: {{ "http://keycloak-http" | b64enc | quote }} client-id: {{ .Values.config.keycloak_client_id | b64enc | quote }} client-secret: {{ .Values.config.keycloak_client_secret | b64enc | quote }} smtp-password: {{ .Values.config.keycloak_smtp_password | b64enc | quote }} \ No newline at end of file diff --git a/charts/openhands/Chart.lock b/charts/openhands/Chart.lock index 04e282b0..0aa92561 100644 --- a/charts/openhands/Chart.lock +++ b/charts/openhands/Chart.lock @@ -2,9 +2,9 @@ dependencies: - name: clickhouse repository: oci://registry-1.docker.io/bitnamicharts version: 9.2.5 -- name: keycloak - repository: oci://registry-1.docker.io/bitnamicharts - version: 24.7.5 +- name: keycloakx + repository: https://codecentric.github.io/helm-charts + version: 7.1.9 - name: langfuse repository: https://langfuse.github.io/langfuse-k8s version: 1.2.13 @@ -25,6 +25,9 @@ dependencies: version: 1.9.0 - name: runtime-api repository: oci://ghcr.io/all-hands-ai/helm-charts - version: 0.1.24 -digest: sha256:bca3722cdd4840a4557955ea2b80e38991cc2d0a0211855a791cf98e37410e45 -generated: "2026-03-18T00:28:58.972983917-04:00" + version: 0.2.6 +- name: automation + repository: oci://ghcr.io/all-hands-ai/helm-charts + version: 0.1.0 +digest: sha256:9704eb8e0893624e1ab033871f04966cc9446437d785a89dce7b249de8c05ac0 +generated: "2026-04-03T17:26:28.296356-04:00" diff --git a/charts/openhands/Chart.yaml b/charts/openhands/Chart.yaml index 042c0af0..e397da4c 100644 --- a/charts/openhands/Chart.yaml +++ b/charts/openhands/Chart.yaml @@ -12,9 +12,10 @@ dependencies: repository: oci://registry-1.docker.io/bitnamicharts version: 9.2.5 condition: clickhouse.enabled - - name: keycloak - version: 24.7.5 - repository: oci://registry-1.docker.io/bitnamicharts + - name: keycloakx + alias: keycloak + version: 7.1.9 + repository: https://codecentric.github.io/helm-charts condition: keycloak.enabled - name: langfuse repository: https://langfuse.github.io/langfuse-k8s diff --git a/charts/openhands/example-values.yaml b/charts/openhands/example-values.yaml index 86ab99df..3d1a5282 100644 --- a/charts/openhands/example-values.yaml +++ b/charts/openhands/example-values.yaml @@ -34,10 +34,18 @@ keycloak: enabled: true ingress: enabled: false - hostname: "auth.app.example.com" annotations: {} # Value should match your Issuer/ClusterIssuer and uncomment if you're using cert-manager for certificates # cert-manager.io/cluster-issuer: letsencrypt + rules: [] + # - host: auth.app.example.com + # paths: + # - path: / + # pathType: Prefix + tls: [] + # - secretName: keycloak-tls + # hosts: + # - auth.app.example.com postgresql: enabled: true diff --git a/charts/openhands/templates/_init-containers.yaml b/charts/openhands/templates/_init-containers.yaml index a69ad312..1fa9545c 100644 --- a/charts/openhands/templates/_init-containers.yaml +++ b/charts/openhands/templates/_init-containers.yaml @@ -30,7 +30,7 @@ done env: - name: DATABASES - value: "{{ .Values.keycloak.externalDatabase.database }}{{- if (index .Values "litellm-helm").enabled }} {{ (index .Values "litellm-helm").db.database }}{{- end }}{{- if .Values.langfuse.enabled }} {{ .Values.langfuse.postgresql.auth.database }}{{- end }}" + value: "{{ .Values.keycloak.database.database }}{{- if (index .Values "litellm-helm").enabled }} {{ (index .Values "litellm-helm").db.database }}{{- end }}{{- if .Values.langfuse.enabled }} {{ .Values.langfuse.postgresql.auth.database }}{{- end }}" {{- include "openhands.env" . | nindent 4 }} {{- end }} {{- if .Values.databaseMigrations.migrate }} diff --git a/charts/openhands/templates/troubleshoot/support-bundle.yaml b/charts/openhands/templates/troubleshoot/support-bundle.yaml index 00f1ce68..9e04e4e6 100644 --- a/charts/openhands/templates/troubleshoot/support-bundle.yaml +++ b/charts/openhands/templates/troubleshoot/support-bundle.yaml @@ -75,7 +75,7 @@ spec: maxAge: 168h {{- end }} {{- if .Values.keycloak.enabled }} - # Keycloak logs (Bitnami chart) + # Keycloak logs (KeycloakX chart) - logs: name: app/{{ .Release.Name }}-keycloak/logs namespace: {{ .Release.Namespace }} diff --git a/charts/openhands/values.yaml b/charts/openhands/values.yaml index 53f19a85..d01cfa4b 100644 --- a/charts/openhands/values.yaml +++ b/charts/openhands/values.yaml @@ -287,38 +287,68 @@ clickhouse: keycloak: enabled: false - url: "http://keycloak" + url: "http://keycloak-http" fullnameOverride: keycloak - replicaCount: 1 - production: true - proxyHeaders: forwarded - ingress: - enabled: false - # REQUIRED: Update to a hostname in a DNS domain you own - # hostname: auth.app.example.com - servicePort: 80 - tls: true - annotations: {} - # UPDATE: if you use cert-manager, enter your clusterIssuer may not match. - # cert-manager.io/cluster-issuer: letsencrypt-production - ingressClassName: traefik - auth: - adminUser: tmpadmin - existingSecret: keycloak-admin - passwordSecretKey: admin-password + replicas: 1 + + image: + repository: quay.io/keycloak/keycloak + tag: "26.5.5" + pullPolicy: IfNotPresent + + database: + vendor: postgres + hostname: oh-main-postgresql + port: 5432 + database: bitnami_keycloak + existingSecret: postgres-password + existingSecretKey: password + + dbchecker: + enabled: true + image: + repository: docker.io/busybox + tag: "1.37" + + proxy: + enabled: true + mode: xforwarded + + http: + relativePath: "/" + service: type: ClusterIP + httpPort: 80 + serviceAccount: + create: true name: "keycloak-sa" - postgresql: + + ingress: enabled: false - externalDatabase: - host: oh-main-postgresql - database: bitnami_keycloak - existingSecret: postgres-password - existingSecretUserKey: username - existingSecretPasswordKey: password - extraEnvVars: + ingressClassName: traefik + annotations: {} + # UPDATE: if you use cert-manager, enter your clusterIssuer may not match. + # cert-manager.io/cluster-issuer: letsencrypt-production + rules: [] + # - host: auth.app.example.com + # paths: + # - path: / + # pathType: Prefix + tls: [] + # - secretName: keycloak-tls + # hosts: + # - auth.app.example.com + + extraEnv: | + - name: KEYCLOAK_ADMIN + value: tmpadmin + - name: KEYCLOAK_ADMIN_PASSWORD + valueFrom: + secretKeyRef: + name: keycloak-admin + key: admin-password - name: KC_FEATURES value: token-exchange,admin-fine-grained-authz - name: KC_HTTP_ENABLED @@ -327,45 +357,11 @@ keycloak: value: "xforwarded" - name: KC_SPI_LOGIN_PROTOCOL_OPENID_CONNECT_LEGACY_LOGOUT_REDIRECT_URI value: "true" - image: - repository: bitnamilegacy/keycloak - waitForDb: - image: "bitnamilegacy/postgresql:latest" - initContainers: - - name: wait-for-db - # The Bitnami Keycloak subchart renders initContainers through common.tplvalues.render, - # so this template expression is evaluated at deploy time rather than being treated as - # a literal string. This lets us override just the image (e.g. for Replicated proxy) - # without duplicating the entire init container. - image: '{{ .Values.waitForDb.image }}' - command: ['sh', '-c'] - args: - - | - echo "Waiting for database \"$KEYCLOAK_DATABASE_NAME\" at $KEYCLOAK_DATABASE_HOST:$KEYCLOAK_DATABASE_PORT..." - until PGPASSWORD=$DB_PASS pg_isready -h "$KEYCLOAK_DATABASE_HOST" -p "$KEYCLOAK_DATABASE_PORT" -U "$DB_USER" -d "$KEYCLOAK_DATABASE_NAME" > /dev/null 2>&1; do - echo "PostgreSQL is unavailable - sleeping 5s" - sleep 5 - done - echo "PostgreSQL is ready, checking database exists..." - until PGPASSWORD=$DB_PASS psql -h "$KEYCLOAK_DATABASE_HOST" -p "$KEYCLOAK_DATABASE_PORT" -U "$DB_USER" -d "$KEYCLOAK_DATABASE_NAME" -c "SELECT 1" > /dev/null 2>&1; do - echo "Database \"$KEYCLOAK_DATABASE_NAME\" not ready - sleeping 5s" - sleep 5 - done - echo "Database \"$KEYCLOAK_DATABASE_NAME\" is ready!" - envFrom: - - configMapRef: - name: keycloak-env-vars - env: - - name: DB_USER - valueFrom: - secretKeyRef: - name: postgres-password - key: username - - name: DB_PASS - valueFrom: - secretKeyRef: - name: postgres-password - key: password + - name: KC_DB_USERNAME + valueFrom: + secretKeyRef: + name: postgres-password + key: username langfuse: # Enable this if you want to use langfuse for tracing diff --git a/replicated/application.yaml b/replicated/application.yaml index aede3aa2..10ba5047 100644 --- a/replicated/application.yaml +++ b/replicated/application.yaml @@ -27,7 +27,7 @@ spec: # keycloak - openhands/statefulset/keycloak - - openhands/service/keycloak + - openhands/service/keycloak-http # postgres - '{{repl if ConfigOptionEquals "postgres_type" "embedded_postgres"}}openhands/statefulset/openhands-postgresql{{repl end}}' diff --git a/replicated/openhands.yaml b/replicated/openhands.yaml index 7c5b2f3c..70999d84 100644 --- a/replicated/openhands.yaml +++ b/replicated/openhands.yaml @@ -57,8 +57,7 @@ spec: keycloak: enabled: true - url: 'http://keycloak' - resourcesPreset: none + url: 'http://keycloak-http' resources: requests: cpu: 500m @@ -71,29 +70,54 @@ spec: ingress: enabled: true ingressClassName: traefik - hostname: '{{repl if ConfigOptionEquals "hostname_mode" "derive"}}auth.app.{{repl ConfigOption "base_domain"}}{{repl else}}{{repl ConfigOption "auth_hostname"}}{{repl end}}' - tls: true annotations: nginx.ingress.kubernetes.io/upstream-vhost: '{{repl if ConfigOptionEquals "hostname_mode" "derive"}}auth.app.{{repl ConfigOption "base_domain"}}{{repl else}}{{repl ConfigOption "auth_hostname"}}{{repl end}}' nginx.ingress.kubernetes.io/backend-protocol: "HTTP" - secrets: - - name: '{{repl if ConfigOptionEquals "hostname_mode" "derive"}}auth.app.{{repl ConfigOption "base_domain"}}{{repl else}}{{repl ConfigOption "auth_hostname"}}{{repl end}}-tls' - key: | - {{repl ConfigOptionData "tls_private_key" | nindent 14}} - certificate: | - {{repl ConfigOptionData "tls_certificate" | nindent 14}} - externalDatabase: - host: '{{repl if ConfigOptionEquals "postgres_type" "external_postgres"}}{{repl ConfigOption "external_postgres_host"}}{{repl else}}oh-main-postgresql{{repl end}}' + rules: + - host: '{{repl if ConfigOptionEquals "hostname_mode" "derive"}}auth.app.{{repl ConfigOption "base_domain"}}{{repl else}}{{repl ConfigOption "auth_hostname"}}{{repl end}}' + paths: + - path: / + pathType: Prefix + tls: + - secretName: '{{repl if ConfigOptionEquals "hostname_mode" "derive"}}auth.app.{{repl ConfigOption "base_domain"}}{{repl else}}{{repl ConfigOption "auth_hostname"}}{{repl end}}-tls' + hosts: + - '{{repl if ConfigOptionEquals "hostname_mode" "derive"}}auth.app.{{repl ConfigOption "base_domain"}}{{repl else}}{{repl ConfigOption "auth_hostname"}}{{repl end}}' + database: + vendor: postgres + hostname: '{{repl if ConfigOptionEquals "postgres_type" "external_postgres"}}{{repl ConfigOption "external_postgres_host"}}{{repl else}}oh-main-postgresql{{repl end}}' + port: 5432 + database: bitnami_keycloak existingSecret: postgres-password - existingSecretUserKey: username - existingSecretPasswordKey: password - image: - repository: 'proxy/{{repl LicenseFieldValue "appSlug"}}/docker.io/bitnamilegacy/keycloak' - keycloakConfigCli: + existingSecretKey: password + dbchecker: + enabled: true image: - repository: 'proxy/{{repl LicenseFieldValue "appSlug"}}/docker.io/bitnamilegacy/keycloak-config-cli' - waitForDb: - image: 'images.r9.all-hands.dev/proxy/{{repl LicenseFieldValue "appSlug"}}/docker.io/bitnamilegacy/postgresql:latest' + repository: 'images.r9.all-hands.dev/proxy/{{repl LicenseFieldValue "appSlug"}}/docker.io/busybox' + tag: "1.37" + image: + repository: 'images.r9.all-hands.dev/proxy/{{repl LicenseFieldValue "appSlug"}}/quay.io/keycloak/keycloak' + tag: "26.5.5" + extraEnv: | + - name: KEYCLOAK_ADMIN + value: tmpadmin + - name: KEYCLOAK_ADMIN_PASSWORD + valueFrom: + secretKeyRef: + name: keycloak-admin + key: admin-password + - name: KC_FEATURES + value: token-exchange,admin-fine-grained-authz + - name: KC_HTTP_ENABLED + value: "true" + - name: KC_PROXY_HEADERS + value: "xforwarded" + - name: KC_SPI_LOGIN_PROTOCOL_OPENID_CONNECT_LEGACY_LOGOUT_REDIRECT_URI + value: "true" + - name: KC_DB_USERNAME + valueFrom: + secretKeyRef: + name: postgres-password + key: username litellm: enabled: true @@ -292,7 +316,7 @@ spec: # existingSecretUserKey: username # existingSecretPasswordKey: password keycloak: - externalDatabase: + database: database: '{{repl ConfigOption "external_postgres_keycloak_database"}}' litellm-helm: db: @@ -320,10 +344,6 @@ spec: postgresql: image: repository: 'proxy/{{repl LicenseFieldValue "appSlug"}}/docker.io/bitnamilegacy/postgresql' - keycloak: - postgresql: - image: - repository: 'proxy/{{repl LicenseFieldValue "appSlug"}}/docker.io/bitnamilegacy/postgresql' - when: '{{repl ConfigOptionEquals "langfuse_enabled" "1" }}' recursiveMerge: true values: @@ -388,12 +408,10 @@ spec: fs_group: 10001 keycloak: image: - repository: '{{repl LocalRegistryNamespace }}/keycloak' - keycloakConfigCli: + repository: '{{repl LocalRegistryHost }}/{{repl LocalRegistryNamespace }}/keycloak' + dbchecker: image: - repository: '{{repl LocalRegistryNamespace }}/keycloak-config-cli' - waitForDb: - image: '{{repl LocalRegistryHost }}/{{repl LocalRegistryNamespace }}/postgresql:latest' + repository: '{{repl LocalRegistryHost }}/{{repl LocalRegistryNamespace }}/busybox' postgresql: image: repository: '{{repl LocalRegistryNamespace }}/postgresql' @@ -441,7 +459,7 @@ spec: {{repl $llmDomain := $derive | ternary (printf "llm-proxy.%s" $bd) (ConfigOption "llm_proxy_hostname") }} {{repl $runtimeApiDomain := $derive | ternary (printf "runtime-api.%s" $bd) (ConfigOption "runtime_api_hostname") }} {{repl $runtimeBaseDomain := $derive | ternary (printf "runtime.%s" $bd) (ConfigOption "runtime_base_hostname") }} - {{repl $computedNoProxy := "127.0.0.1,cluster.local,keycloak,keycloak-headless,kubernetes,localhost,oh-main-clickhouse,oh-main-langfuse,oh-main-lite-llm,oh-main-runtime-api,openhands-integrations-service,openhands-litellm,openhands-mcp-service,openhands-minio,openhands-runtime-api,openhands-service,svc" }} + {{repl $computedNoProxy := "127.0.0.1,cluster.local,keycloak-http,keycloak-headless,kubernetes,localhost,oh-main-clickhouse,oh-main-langfuse,oh-main-lite-llm,oh-main-runtime-api,openhands-integrations-service,openhands-litellm,openhands-mcp-service,openhands-minio,openhands-runtime-api,openhands-service,svc" }} {{repl if ne $bd "" }}{{repl $computedNoProxy = printf "%s,%s" $computedNoProxy $bd }}{{repl end }} {{repl if ne $appDomain "" }}{{repl $computedNoProxy = printf "%s,%s" $computedNoProxy $appDomain }}{{repl end }} {{repl if ne $authDomain "" }}{{repl $computedNoProxy = printf "%s,%s" $computedNoProxy $authDomain }}{{repl end }} @@ -459,8 +477,15 @@ spec: SSL_VERIFY: '{{repl if ConfigOptionEquals "ssl_verify" "1"}}True{{repl else}}False{{repl end}}' OH_AGENT_SERVER_ENV: '{{repl printf "{\"HTTP_PROXY\": \"%s\", \"HTTPS_PROXY\": \"%s\", \"NO_PROXY\": \"%s\", \"SSL_VERIFY\": \"%s\", \"GIT_SSL_NO_VERIFY\": \"%s\"}" (ConfigOption "http_proxy") (ConfigOption "https_proxy") $computedNoProxy (ConfigOptionEquals "ssl_verify" "1" | ternary "True" "False") (ConfigOptionEquals "ssl_verify" "1" | ternary "False" "True") }}' keycloak: - extraEnvVars: - # Existing KC env vars (must be repeated because recursiveMerge replaces arrays) + # All extraEnv vars must be repeated because recursiveMerge replaces entire keys + extraEnv: | + - name: KEYCLOAK_ADMIN + value: tmpadmin + - name: KEYCLOAK_ADMIN_PASSWORD + valueFrom: + secretKeyRef: + name: keycloak-admin + key: admin-password - name: KC_FEATURES value: token-exchange,admin-fine-grained-authz - name: KC_HTTP_ENABLED @@ -469,7 +494,11 @@ spec: value: "xforwarded" - name: KC_SPI_LOGIN_PROTOCOL_OPENID_CONNECT_LEGACY_LOGOUT_REDIRECT_URI value: "true" - # Proxy env vars + - name: KC_DB_USERNAME + valueFrom: + secretKeyRef: + name: postgres-password + key: username - name: HTTP_PROXY value: '{{repl ConfigOption "http_proxy"}}' - name: HTTPS_PROXY From 39f28173dcec45d27e855ec68fdcd8cde0654258 Mon Sep 17 00:00:00 2001 From: CHANGE Date: Fri, 3 Apr 2026 18:39:23 -0400 Subject: [PATCH 02/13] Remove duplicate KC_HTTP_ENABLED and KC_PROXY_HEADERS env vars The codecentric KeycloakX chart already renders these from proxy.mode and http config values. Including them in extraEnv caused a StatefulSet patch failure due to duplicate env var keys. --- charts/openhands/values.yaml | 4 ---- replicated/openhands.yaml | 8 -------- 2 files changed, 12 deletions(-) diff --git a/charts/openhands/values.yaml b/charts/openhands/values.yaml index d01cfa4b..8aee5140 100644 --- a/charts/openhands/values.yaml +++ b/charts/openhands/values.yaml @@ -351,10 +351,6 @@ keycloak: key: admin-password - name: KC_FEATURES value: token-exchange,admin-fine-grained-authz - - name: KC_HTTP_ENABLED - value: "true" - - name: KC_PROXY_HEADERS - value: "xforwarded" - name: KC_SPI_LOGIN_PROTOCOL_OPENID_CONNECT_LEGACY_LOGOUT_REDIRECT_URI value: "true" - name: KC_DB_USERNAME diff --git a/replicated/openhands.yaml b/replicated/openhands.yaml index 70999d84..020c4245 100644 --- a/replicated/openhands.yaml +++ b/replicated/openhands.yaml @@ -107,10 +107,6 @@ spec: key: admin-password - name: KC_FEATURES value: token-exchange,admin-fine-grained-authz - - name: KC_HTTP_ENABLED - value: "true" - - name: KC_PROXY_HEADERS - value: "xforwarded" - name: KC_SPI_LOGIN_PROTOCOL_OPENID_CONNECT_LEGACY_LOGOUT_REDIRECT_URI value: "true" - name: KC_DB_USERNAME @@ -488,10 +484,6 @@ spec: key: admin-password - name: KC_FEATURES value: token-exchange,admin-fine-grained-authz - - name: KC_HTTP_ENABLED - value: "true" - - name: KC_PROXY_HEADERS - value: "xforwarded" - name: KC_SPI_LOGIN_PROTOCOL_OPENID_CONNECT_LEGACY_LOGOUT_REDIRECT_URI value: "true" - name: KC_DB_USERNAME From 8452b44e69286480992eff7f933a89e3018c0fa4 Mon Sep 17 00:00:00 2001 From: CHANGE Date: Sat, 4 Apr 2026 10:37:19 -0400 Subject: [PATCH 03/13] Disable keycloak dbchecker in Replicated deployments The busybox image used by the dbchecker init container fails to pull through the Replicated image proxy with a 400 error. Disable it since the external RDS instance is already available before Keycloak starts, and Keycloak has its own DB connectivity retry logic. --- replicated/openhands.yaml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/replicated/openhands.yaml b/replicated/openhands.yaml index 020c4245..dca32f5a 100644 --- a/replicated/openhands.yaml +++ b/replicated/openhands.yaml @@ -90,10 +90,7 @@ spec: existingSecret: postgres-password existingSecretKey: password dbchecker: - enabled: true - image: - repository: 'images.r9.all-hands.dev/proxy/{{repl LicenseFieldValue "appSlug"}}/docker.io/busybox' - tag: "1.37" + enabled: false image: repository: 'images.r9.all-hands.dev/proxy/{{repl LicenseFieldValue "appSlug"}}/quay.io/keycloak/keycloak' tag: "26.5.5" @@ -406,8 +403,7 @@ spec: image: repository: '{{repl LocalRegistryHost }}/{{repl LocalRegistryNamespace }}/keycloak' dbchecker: - image: - repository: '{{repl LocalRegistryHost }}/{{repl LocalRegistryNamespace }}/busybox' + enabled: false postgresql: image: repository: '{{repl LocalRegistryNamespace }}/postgresql' From 961a91e006524ac042f5caf36a95557f2dca85e9 Mon Sep 17 00:00:00 2001 From: CHANGE Date: Sat, 4 Apr 2026 10:50:25 -0400 Subject: [PATCH 04/13] Fix busybox image path for Replicated proxy Use docker.io/library/busybox (the correct official image namespace) instead of docker.io/busybox, which caused a 400 error from the Replicated image proxy when pulling the dbchecker init container image. --- charts/openhands/values.yaml | 2 +- replicated/openhands.yaml | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/charts/openhands/values.yaml b/charts/openhands/values.yaml index 8aee5140..62629169 100644 --- a/charts/openhands/values.yaml +++ b/charts/openhands/values.yaml @@ -307,7 +307,7 @@ keycloak: dbchecker: enabled: true image: - repository: docker.io/busybox + repository: docker.io/library/busybox tag: "1.37" proxy: diff --git a/replicated/openhands.yaml b/replicated/openhands.yaml index dca32f5a..b114c57d 100644 --- a/replicated/openhands.yaml +++ b/replicated/openhands.yaml @@ -90,7 +90,10 @@ spec: existingSecret: postgres-password existingSecretKey: password dbchecker: - enabled: false + enabled: true + image: + repository: 'images.r9.all-hands.dev/proxy/{{repl LicenseFieldValue "appSlug"}}/docker.io/library/busybox' + tag: "1.37" image: repository: 'images.r9.all-hands.dev/proxy/{{repl LicenseFieldValue "appSlug"}}/quay.io/keycloak/keycloak' tag: "26.5.5" @@ -403,7 +406,8 @@ spec: image: repository: '{{repl LocalRegistryHost }}/{{repl LocalRegistryNamespace }}/keycloak' dbchecker: - enabled: false + image: + repository: '{{repl LocalRegistryHost }}/{{repl LocalRegistryNamespace }}/busybox' postgresql: image: repository: '{{repl LocalRegistryNamespace }}/postgresql' From 0f3612e4c251d584d15f101bd98ddd4ff80d93b7 Mon Sep 17 00:00:00 2001 From: CHANGE Date: Sat, 4 Apr 2026 11:27:06 -0400 Subject: [PATCH 05/13] Use bitnamilegacy/os-shell for keycloak dbchecker init container MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit docker.io/library/busybox (official Docker Hub library image) cannot be pulled through the Replicated image proxy. Use bitnamilegacy/os-shell instead — it is already proxied and includes netcat-openbsd (nc), which the dbchecker script requires. --- replicated/openhands.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/replicated/openhands.yaml b/replicated/openhands.yaml index b114c57d..5a42dc9a 100644 --- a/replicated/openhands.yaml +++ b/replicated/openhands.yaml @@ -92,8 +92,8 @@ spec: dbchecker: enabled: true image: - repository: 'images.r9.all-hands.dev/proxy/{{repl LicenseFieldValue "appSlug"}}/docker.io/library/busybox' - tag: "1.37" + repository: 'images.r9.all-hands.dev/proxy/{{repl LicenseFieldValue "appSlug"}}/docker.io/bitnamilegacy/os-shell' + tag: "12" image: repository: 'images.r9.all-hands.dev/proxy/{{repl LicenseFieldValue "appSlug"}}/quay.io/keycloak/keycloak' tag: "26.5.5" @@ -407,7 +407,7 @@ spec: repository: '{{repl LocalRegistryHost }}/{{repl LocalRegistryNamespace }}/keycloak' dbchecker: image: - repository: '{{repl LocalRegistryHost }}/{{repl LocalRegistryNamespace }}/busybox' + repository: '{{repl LocalRegistryHost }}/{{repl LocalRegistryNamespace }}/os-shell' postgresql: image: repository: '{{repl LocalRegistryNamespace }}/postgresql' From d6a371fcda6d378cb42d55f1fb88adc7504aecb6 Mon Sep 17 00:00:00 2001 From: CHANGE Date: Sat, 4 Apr 2026 11:49:17 -0400 Subject: [PATCH 06/13] Replace dbchecker with pg_isready wait-for-db init container Disable the codecentric chart's built-in dbchecker (which requires busybox) and use extraInitContainers with the same pg_isready pattern already used by the openhands init containers. Uses the already-proxied bitnamilegacy/postgresql image. --- charts/openhands/values.yaml | 19 +++++++++++++++---- replicated/openhands.yaml | 10 +++------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/charts/openhands/values.yaml b/charts/openhands/values.yaml index 62629169..8e6db48e 100644 --- a/charts/openhands/values.yaml +++ b/charts/openhands/values.yaml @@ -305,10 +305,21 @@ keycloak: existingSecretKey: password dbchecker: - enabled: true - image: - repository: docker.io/library/busybox - tag: "1.37" + enabled: false + + waitForDbImage: "bitnamilegacy/postgresql:latest" + extraInitContainers: | + - name: wait-for-db + image: "{{ .Values.waitForDbImage }}" + command: ['sh', '-c'] + args: + - | + echo "Waiting for PostgreSQL at {{ .Values.database.hostname }}:{{ .Values.database.port }}..." + until pg_isready -h "{{ .Values.database.hostname }}" -p "{{ .Values.database.port }}" > /dev/null 2>&1; do + echo "PostgreSQL is unavailable - sleeping 2s" + sleep 2 + done + echo "PostgreSQL is ready!" proxy: enabled: true diff --git a/replicated/openhands.yaml b/replicated/openhands.yaml index 5a42dc9a..3b8a6577 100644 --- a/replicated/openhands.yaml +++ b/replicated/openhands.yaml @@ -90,10 +90,8 @@ spec: existingSecret: postgres-password existingSecretKey: password dbchecker: - enabled: true - image: - repository: 'images.r9.all-hands.dev/proxy/{{repl LicenseFieldValue "appSlug"}}/docker.io/bitnamilegacy/os-shell' - tag: "12" + enabled: false + waitForDbImage: 'images.r9.all-hands.dev/proxy/{{repl LicenseFieldValue "appSlug"}}/docker.io/bitnamilegacy/postgresql:latest' image: repository: 'images.r9.all-hands.dev/proxy/{{repl LicenseFieldValue "appSlug"}}/quay.io/keycloak/keycloak' tag: "26.5.5" @@ -405,9 +403,7 @@ spec: keycloak: image: repository: '{{repl LocalRegistryHost }}/{{repl LocalRegistryNamespace }}/keycloak' - dbchecker: - image: - repository: '{{repl LocalRegistryHost }}/{{repl LocalRegistryNamespace }}/os-shell' + waitForDbImage: '{{repl LocalRegistryHost }}/{{repl LocalRegistryNamespace }}/postgresql:latest' postgresql: image: repository: '{{repl LocalRegistryNamespace }}/postgresql' From bb8acccb1a111070e2d487704688917c38272e82 Mon Sep 17 00:00:00 2001 From: CHANGE Date: Sat, 4 Apr 2026 11:58:56 -0400 Subject: [PATCH 07/13] Fix dbchecker image proxy path: use index.docker.io for Docker Hub The Replicated image proxy requires index.docker.io (not docker.io) for Docker Hub images. Use index.docker.io/library/busybox:1.37 for the keycloak dbchecker init container. --- replicated/openhands.yaml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/replicated/openhands.yaml b/replicated/openhands.yaml index 3b8a6577..a7346530 100644 --- a/replicated/openhands.yaml +++ b/replicated/openhands.yaml @@ -90,8 +90,10 @@ spec: existingSecret: postgres-password existingSecretKey: password dbchecker: - enabled: false - waitForDbImage: 'images.r9.all-hands.dev/proxy/{{repl LicenseFieldValue "appSlug"}}/docker.io/bitnamilegacy/postgresql:latest' + enabled: true + image: + repository: 'images.r9.all-hands.dev/proxy/{{repl LicenseFieldValue "appSlug"}}/index.docker.io/library/busybox' + tag: "1.37" image: repository: 'images.r9.all-hands.dev/proxy/{{repl LicenseFieldValue "appSlug"}}/quay.io/keycloak/keycloak' tag: "26.5.5" @@ -403,7 +405,9 @@ spec: keycloak: image: repository: '{{repl LocalRegistryHost }}/{{repl LocalRegistryNamespace }}/keycloak' - waitForDbImage: '{{repl LocalRegistryHost }}/{{repl LocalRegistryNamespace }}/postgresql:latest' + dbchecker: + image: + repository: '{{repl LocalRegistryHost }}/{{repl LocalRegistryNamespace }}/busybox' postgresql: image: repository: '{{repl LocalRegistryNamespace }}/postgresql' From 8ade9295badad7347310f1a904390c3ad43d587a Mon Sep 17 00:00:00 2001 From: CHANGE Date: Sat, 4 Apr 2026 12:13:34 -0400 Subject: [PATCH 08/13] Add imagePullSecrets for keycloak and fix busybox proxy path The keycloak pod needs the Replicated pull secret to authenticate with the images.r9.all-hands.dev proxy. Also drop the library/ prefix from the busybox image path as it may not be needed for the proxy. --- replicated/openhands.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/replicated/openhands.yaml b/replicated/openhands.yaml index a7346530..710eadde 100644 --- a/replicated/openhands.yaml +++ b/replicated/openhands.yaml @@ -89,10 +89,12 @@ spec: database: bitnami_keycloak existingSecret: postgres-password existingSecretKey: password + imagePullSecrets: + - name: '{{repl ImagePullSecretName }}' dbchecker: enabled: true image: - repository: 'images.r9.all-hands.dev/proxy/{{repl LicenseFieldValue "appSlug"}}/index.docker.io/library/busybox' + repository: 'images.r9.all-hands.dev/proxy/{{repl LicenseFieldValue "appSlug"}}/index.docker.io/busybox' tag: "1.37" image: repository: 'images.r9.all-hands.dev/proxy/{{repl LicenseFieldValue "appSlug"}}/quay.io/keycloak/keycloak' From 8ff79e258be353753c29dd132c623e47989e57b4 Mon Sep 17 00:00:00 2001 From: CHANGE Date: Sat, 4 Apr 2026 13:37:16 -0400 Subject: [PATCH 09/13] Use bitnamilegacy/os-shell for dbchecker (already in proxy allow-list) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit busybox is not in the Replicated proxy's allowed image set. Use bitnamilegacy/os-shell instead — it's already proxied for clickhouse volume permissions and includes netcat (nc) for the DB readiness check. --- replicated/openhands.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/replicated/openhands.yaml b/replicated/openhands.yaml index 710eadde..779dd3f9 100644 --- a/replicated/openhands.yaml +++ b/replicated/openhands.yaml @@ -94,8 +94,8 @@ spec: dbchecker: enabled: true image: - repository: 'images.r9.all-hands.dev/proxy/{{repl LicenseFieldValue "appSlug"}}/index.docker.io/busybox' - tag: "1.37" + repository: 'images.r9.all-hands.dev/proxy/{{repl LicenseFieldValue "appSlug"}}/index.docker.io/bitnamilegacy/os-shell' + tag: "12" image: repository: 'images.r9.all-hands.dev/proxy/{{repl LicenseFieldValue "appSlug"}}/quay.io/keycloak/keycloak' tag: "26.5.5" @@ -409,7 +409,7 @@ spec: repository: '{{repl LocalRegistryHost }}/{{repl LocalRegistryNamespace }}/keycloak' dbchecker: image: - repository: '{{repl LocalRegistryHost }}/{{repl LocalRegistryNamespace }}/busybox' + repository: '{{repl LocalRegistryHost }}/{{repl LocalRegistryNamespace }}/os-shell' postgresql: image: repository: '{{repl LocalRegistryNamespace }}/postgresql' From 4eb83b6895efb7d10c4a50d83a06385179f231e7 Mon Sep 17 00:00:00 2001 From: CHANGE Date: Sat, 4 Apr 2026 14:08:36 -0400 Subject: [PATCH 10/13] Fix dbchecker busybox image: use docker.io/library/busybox with pull secret Docker Hub library images require the /library/ prefix when specifying a registry hostname through the Replicated proxy. The previous attempt omitted it. Tested on the cluster: docker.io/library/busybox:1.37 pulls successfully with the imagePullSecret. --- replicated/openhands.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/replicated/openhands.yaml b/replicated/openhands.yaml index 779dd3f9..3177ba59 100644 --- a/replicated/openhands.yaml +++ b/replicated/openhands.yaml @@ -94,8 +94,8 @@ spec: dbchecker: enabled: true image: - repository: 'images.r9.all-hands.dev/proxy/{{repl LicenseFieldValue "appSlug"}}/index.docker.io/bitnamilegacy/os-shell' - tag: "12" + repository: 'images.r9.all-hands.dev/proxy/{{repl LicenseFieldValue "appSlug"}}/docker.io/library/busybox' + tag: "1.37" image: repository: 'images.r9.all-hands.dev/proxy/{{repl LicenseFieldValue "appSlug"}}/quay.io/keycloak/keycloak' tag: "26.5.5" @@ -409,7 +409,7 @@ spec: repository: '{{repl LocalRegistryHost }}/{{repl LocalRegistryNamespace }}/keycloak' dbchecker: image: - repository: '{{repl LocalRegistryHost }}/{{repl LocalRegistryNamespace }}/os-shell' + repository: '{{repl LocalRegistryHost }}/{{repl LocalRegistryNamespace }}/busybox' postgresql: image: repository: '{{repl LocalRegistryNamespace }}/postgresql' From 312c899367d238e41e0d77e57f75035befcc94c6 Mon Sep 17 00:00:00 2001 From: CHANGE Date: Sat, 4 Apr 2026 14:48:13 -0400 Subject: [PATCH 11/13] Add 'kc.sh start' command for keycloak container The official keycloak image entrypoint (kc.sh) requires a subcommand. Without 'start', it prints CLI help and exits immediately, causing CrashLoopBackOff. --- charts/openhands/values.yaml | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/charts/openhands/values.yaml b/charts/openhands/values.yaml index 8e6db48e..a5666bca 100644 --- a/charts/openhands/values.yaml +++ b/charts/openhands/values.yaml @@ -291,6 +291,11 @@ keycloak: fullnameOverride: keycloak replicas: 1 + command: + - "/opt/keycloak/bin/kc.sh" + args: + - "start" + image: repository: quay.io/keycloak/keycloak tag: "26.5.5" @@ -305,21 +310,10 @@ keycloak: existingSecretKey: password dbchecker: - enabled: false - - waitForDbImage: "bitnamilegacy/postgresql:latest" - extraInitContainers: | - - name: wait-for-db - image: "{{ .Values.waitForDbImage }}" - command: ['sh', '-c'] - args: - - | - echo "Waiting for PostgreSQL at {{ .Values.database.hostname }}:{{ .Values.database.port }}..." - until pg_isready -h "{{ .Values.database.hostname }}" -p "{{ .Values.database.port }}" > /dev/null 2>&1; do - echo "PostgreSQL is unavailable - sleeping 2s" - sleep 2 - done - echo "PostgreSQL is ready!" + enabled: true + image: + repository: docker.io/library/busybox + tag: "1.37" proxy: enabled: true From 1974a69c13b9212cd0e5c25e0d69a4ab80ea4668 Mon Sep 17 00:00:00 2001 From: CHANGE Date: Sat, 4 Apr 2026 15:05:00 -0400 Subject: [PATCH 12/13] Add KC_HOSTNAME_STRICT=false for Keycloak 26.x Keycloak 26.x requires either KC_HOSTNAME or KC_HOSTNAME_STRICT=false. Since the hostname varies per deployment, disable strict hostname checking to allow Keycloak to accept requests on any hostname. --- charts/openhands/values.yaml | 2 ++ replicated/openhands.yaml | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/charts/openhands/values.yaml b/charts/openhands/values.yaml index a5666bca..886bb1bf 100644 --- a/charts/openhands/values.yaml +++ b/charts/openhands/values.yaml @@ -356,6 +356,8 @@ keycloak: key: admin-password - name: KC_FEATURES value: token-exchange,admin-fine-grained-authz + - name: KC_HOSTNAME_STRICT + value: "false" - name: KC_SPI_LOGIN_PROTOCOL_OPENID_CONNECT_LEGACY_LOGOUT_REDIRECT_URI value: "true" - name: KC_DB_USERNAME diff --git a/replicated/openhands.yaml b/replicated/openhands.yaml index 3177ba59..2bece184 100644 --- a/replicated/openhands.yaml +++ b/replicated/openhands.yaml @@ -109,6 +109,8 @@ spec: key: admin-password - name: KC_FEATURES value: token-exchange,admin-fine-grained-authz + - name: KC_HOSTNAME_STRICT + value: "false" - name: KC_SPI_LOGIN_PROTOCOL_OPENID_CONNECT_LEGACY_LOGOUT_REDIRECT_URI value: "true" - name: KC_DB_USERNAME @@ -486,6 +488,8 @@ spec: key: admin-password - name: KC_FEATURES value: token-exchange,admin-fine-grained-authz + - name: KC_HOSTNAME_STRICT + value: "false" - name: KC_SPI_LOGIN_PROTOCOL_OPENID_CONNECT_LEGACY_LOGOUT_REDIRECT_URI value: "true" - name: KC_DB_USERNAME From a02d2ea9e6131932619042354b5e3154cdae081d Mon Sep 17 00:00:00 2001 From: CHANGE Date: Mon, 6 Apr 2026 15:43:52 -0400 Subject: [PATCH 13/13] Remove fullnameOverride and update service/statefulset references fullnameOverride: keycloak caused a naming collision with the old Bitnami StatefulSet, requiring a manual delete on upgrade. Without it, the codecentric chart generates new resource names (openhands-keycloak, openhands-keycloak-http) that don't conflict with the old chart's resources. --- charts/openhands-secrets/templates/keycloak-realm.yaml | 2 +- charts/openhands/templates/troubleshoot/support-bundle.yaml | 2 +- charts/openhands/values.yaml | 3 +-- replicated/application.yaml | 4 ++-- replicated/openhands.yaml | 4 ++-- 5 files changed, 7 insertions(+), 8 deletions(-) diff --git a/charts/openhands-secrets/templates/keycloak-realm.yaml b/charts/openhands-secrets/templates/keycloak-realm.yaml index 79f68db5..0143bd3f 100644 --- a/charts/openhands-secrets/templates/keycloak-realm.yaml +++ b/charts/openhands-secrets/templates/keycloak-realm.yaml @@ -6,7 +6,7 @@ metadata: type: Opaque data: realm-name: {{ .Values.config.keycloak_realm_name | b64enc | quote }} - server-url: {{ "http://keycloak-http" | b64enc | quote }} + server-url: {{ "http://openhands-keycloak-http" | b64enc | quote }} client-id: {{ .Values.config.keycloak_client_id | b64enc | quote }} client-secret: {{ .Values.config.keycloak_client_secret | b64enc | quote }} smtp-password: {{ .Values.config.keycloak_smtp_password | b64enc | quote }} \ No newline at end of file diff --git a/charts/openhands/templates/troubleshoot/support-bundle.yaml b/charts/openhands/templates/troubleshoot/support-bundle.yaml index 9e04e4e6..7717b47d 100644 --- a/charts/openhands/templates/troubleshoot/support-bundle.yaml +++ b/charts/openhands/templates/troubleshoot/support-bundle.yaml @@ -243,7 +243,7 @@ spec: {{- end }} {{- if .Values.keycloak.enabled }} - statefulsetStatus: - name: keycloak + name: {{ .Release.Name }}-keycloak namespace: {{ .Release.Namespace }} outcomes: - fail: diff --git a/charts/openhands/values.yaml b/charts/openhands/values.yaml index 886bb1bf..14097189 100644 --- a/charts/openhands/values.yaml +++ b/charts/openhands/values.yaml @@ -287,8 +287,7 @@ clickhouse: keycloak: enabled: false - url: "http://keycloak-http" - fullnameOverride: keycloak + url: "http://openhands-keycloak-http" replicas: 1 command: diff --git a/replicated/application.yaml b/replicated/application.yaml index 10ba5047..966b6dd5 100644 --- a/replicated/application.yaml +++ b/replicated/application.yaml @@ -26,8 +26,8 @@ spec: - openhands/ingress/openhands-mcp-ingress # keycloak - - openhands/statefulset/keycloak - - openhands/service/keycloak-http + - openhands/statefulset/openhands-keycloak + - openhands/service/openhands-keycloak-http # postgres - '{{repl if ConfigOptionEquals "postgres_type" "embedded_postgres"}}openhands/statefulset/openhands-postgresql{{repl end}}' diff --git a/replicated/openhands.yaml b/replicated/openhands.yaml index 2bece184..606a6828 100644 --- a/replicated/openhands.yaml +++ b/replicated/openhands.yaml @@ -57,7 +57,7 @@ spec: keycloak: enabled: true - url: 'http://keycloak-http' + url: 'http://openhands-keycloak-http' resources: requests: cpu: 500m @@ -459,7 +459,7 @@ spec: {{repl $llmDomain := $derive | ternary (printf "llm-proxy.%s" $bd) (ConfigOption "llm_proxy_hostname") }} {{repl $runtimeApiDomain := $derive | ternary (printf "runtime-api.%s" $bd) (ConfigOption "runtime_api_hostname") }} {{repl $runtimeBaseDomain := $derive | ternary (printf "runtime.%s" $bd) (ConfigOption "runtime_base_hostname") }} - {{repl $computedNoProxy := "127.0.0.1,cluster.local,keycloak-http,keycloak-headless,kubernetes,localhost,oh-main-clickhouse,oh-main-langfuse,oh-main-lite-llm,oh-main-runtime-api,openhands-integrations-service,openhands-litellm,openhands-mcp-service,openhands-minio,openhands-runtime-api,openhands-service,svc" }} + {{repl $computedNoProxy := "127.0.0.1,cluster.local,openhands-keycloak-http,openhands-keycloak-headless,kubernetes,localhost,oh-main-clickhouse,oh-main-langfuse,oh-main-lite-llm,oh-main-runtime-api,openhands-integrations-service,openhands-litellm,openhands-mcp-service,openhands-minio,openhands-runtime-api,openhands-service,svc" }} {{repl if ne $bd "" }}{{repl $computedNoProxy = printf "%s,%s" $computedNoProxy $bd }}{{repl end }} {{repl if ne $appDomain "" }}{{repl $computedNoProxy = printf "%s,%s" $computedNoProxy $appDomain }}{{repl end }} {{repl if ne $authDomain "" }}{{repl $computedNoProxy = printf "%s,%s" $computedNoProxy $authDomain }}{{repl end }}