apiVersion: apps/v1 kind: Deployment metadata: name: keycloak namespace: keycloak labels: app.kubernetes.io/name: keycloak app.kubernetes.io/component: idp app.kubernetes.io/part-of: keycloak spec: replicas: 1 selector: matchLabels: app: keycloak template: metadata: labels: app: keycloak app.kubernetes.io/name: keycloak app.kubernetes.io/component: idp app.kubernetes.io/part-of: keycloak annotations: # Discovered by the Prometheus in the observability namespace. # Endpoint exposed by KC_METRICS_ENABLED=true, on the management # port (9000), never on the application port. prometheus.io/scrape: "true" prometheus.io/port: "9000" prometheus.io/path: "/metrics" spec: initContainers: # Re-imports the realm from configmap-realm.yaml on every start, so that # file is the source of truth rather than a first-install seed. # # It has to be a separate command: `start --import-realm` skips a realm # that already exists and there is no option to change that — the # --override flag exists only on the standalone `import` command. With # the default behaviour, editing the ConfigMap changed nothing on a # restart and the truth drifted into the database. It already had: four # scope grants existed only in the running realm. # # What this costs, deliberately: the realm is replaced at every start, # so anything created at runtime and not written back to the file is # lost, and active sessions are dropped. Users survive only because they # are declared in the file. Create nothing through the admin console # expecting it to last. - name: import-realm image: quay.io/keycloak/keycloak:26.6.0 args: - "import" - "--dir" - "/opt/keycloak/data/import" - "--override" - "true" env: - name: KC_DB value: "postgres" - name: KC_DB_URL_HOST value: "keycloak-postgresql" - name: KC_DB_URL_PORT value: "5432" - name: KC_DB_URL_DATABASE valueFrom: secretKeyRef: name: keycloak-secrets key: POSTGRES_DB - name: KC_DB_USERNAME valueFrom: secretKeyRef: name: keycloak-secrets key: KC_DB_USERNAME - name: KC_DB_PASSWORD valueFrom: secretKeyRef: name: keycloak-secrets key: KC_DB_PASSWORD - name: KC_BOOTSTRAP_ADMIN_USERNAME valueFrom: secretKeyRef: name: keycloak-secrets key: KC_BOOTSTRAP_ADMIN_USERNAME - name: KC_BOOTSTRAP_ADMIN_PASSWORD valueFrom: secretKeyRef: name: keycloak-secrets key: KC_BOOTSTRAP_ADMIN_PASSWORD - name: KEYCLOAK_ADMIN valueFrom: secretKeyRef: name: keycloak-secrets key: KEYCLOAK_ADMIN - name: KEYCLOAK_ADMIN_PASSWORD valueFrom: secretKeyRef: name: keycloak-secrets key: KEYCLOAK_ADMIN_PASSWORD - name: KC_HTTP_ENABLED value: "true" - name: KC_PROXY_HEADERS value: "xforwarded" - name: KC_HOSTNAME value: "https://keycloak.sttlab.local" - name: KC_HOSTNAME_STRICT value: "false" - name: KC_HOSTNAME_BACKCHANNEL_DYNAMIC value: "false" - name: KC_FEATURES value: "token-exchange,admin-fine-grained-authz,log-mdc" # Emptied for this container only: it inherits the main container's env, # which loads the OpenTelemetry agent from a volume the other init # container downloads into and this one does not mount. Inheriting it # verbatim fails the JVM before the import runs. - name: JAVA_OPTS_APPEND value: "" - name: OTEL_SERVICE_NAME value: "keycloak" - name: OTEL_EXPORTER_OTLP_ENDPOINT value: "http://tempo.observability.svc.cluster.local:4318" - name: OTEL_EXPORTER_OTLP_PROTOCOL value: "http/protobuf" - name: OTEL_TRACES_EXPORTER value: "otlp" - name: OTEL_METRICS_EXPORTER value: "none" - name: OTEL_LOGS_EXPORTER value: "none" - name: KC_HTTP_ACCESS_LOG_ENABLED value: "true" - name: KC_LOG_LEVEL value: "INFO,org.keycloak.events:DEBUG" - name: KC_LOG_CONSOLE_OUTPUT value: "json" - name: KC_LOG_MDC_ENABLED value: "true" - name: KC_LOG_SERVICE_NAME value: "keycloak" - name: KC_LOG_SERVICE_ENVIRONMENT value: "local" - name: KC_HEALTH_ENABLED value: "true" - name: KC_METRICS_ENABLED value: "true" volumeMounts: - name: realm-import mountPath: /opt/keycloak/data/import envFrom: # Supplies the ${KC_SECRET_*} and ${KC_USER_*} placeholders the realm # file carries in place of the values it used to hold in clear. # Keycloak resolves them from the environment at import time. - secretRef: name: keycloak-realm-secrets - name: download-otel-agent image: curlimages/curl:8.10.0 command: - "sh" - "-c" - "curl -sSL -o /otel/opentelemetry-javaagent.jar https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v2.31.0/opentelemetry-javaagent.jar" volumeMounts: - name: otel-agent mountPath: /otel containers: - name: keycloak image: quay.io/keycloak/keycloak:26.6.0 args: - "start" ports: - name: http containerPort: 8080 - name: https containerPort: 8443 - name: management containerPort: 9000 env: - name: KC_DB value: "postgres" - name: KC_DB_URL_HOST value: "keycloak-postgresql" - name: KC_DB_URL_PORT value: "5432" - name: KC_DB_URL_DATABASE valueFrom: secretKeyRef: name: keycloak-secrets key: POSTGRES_DB - name: KC_DB_USERNAME valueFrom: secretKeyRef: name: keycloak-secrets key: KC_DB_USERNAME - name: KC_DB_PASSWORD valueFrom: secretKeyRef: name: keycloak-secrets key: KC_DB_PASSWORD - name: KC_BOOTSTRAP_ADMIN_USERNAME valueFrom: secretKeyRef: name: keycloak-secrets key: KC_BOOTSTRAP_ADMIN_USERNAME - name: KC_BOOTSTRAP_ADMIN_PASSWORD valueFrom: secretKeyRef: name: keycloak-secrets key: KC_BOOTSTRAP_ADMIN_PASSWORD - name: KEYCLOAK_ADMIN valueFrom: secretKeyRef: name: keycloak-secrets key: KEYCLOAK_ADMIN - name: KEYCLOAK_ADMIN_PASSWORD valueFrom: secretKeyRef: name: keycloak-secrets key: KEYCLOAK_ADMIN_PASSWORD - name: KC_HTTP_ENABLED value: "true" - name: KC_PROXY_HEADERS value: "xforwarded" - name: KC_HOSTNAME value: "https://keycloak.sttlab.local" - name: KC_HOSTNAME_STRICT value: "false" - name: KC_HOSTNAME_BACKCHANNEL_DYNAMIC value: "false" - name: KC_FEATURES value: "token-exchange,admin-fine-grained-authz,log-mdc" - name: JAVA_OPTS_APPEND value: "-javaagent:/otel/opentelemetry-javaagent.jar" - name: OTEL_SERVICE_NAME value: "keycloak" - name: OTEL_EXPORTER_OTLP_ENDPOINT value: "http://tempo.observability.svc.cluster.local:4318" - name: OTEL_EXPORTER_OTLP_PROTOCOL value: "http/protobuf" - name: OTEL_TRACES_EXPORTER value: "otlp" - name: OTEL_METRICS_EXPORTER value: "none" - name: OTEL_LOGS_EXPORTER value: "none" - name: KC_HTTP_ACCESS_LOG_ENABLED value: "true" - name: KC_LOG_LEVEL value: "INFO,org.keycloak.events:DEBUG" - name: KC_LOG_CONSOLE_OUTPUT value: "json" - name: KC_LOG_MDC_ENABLED value: "true" - name: KC_LOG_SERVICE_NAME value: "keycloak" - name: KC_LOG_SERVICE_ENVIRONMENT value: "local" - name: KC_HEALTH_ENABLED value: "true" - name: KC_METRICS_ENABLED value: "true" readinessProbe: httpGet: path: /health/ready port: 9000 initialDelaySeconds: 15 periodSeconds: 10 failureThreshold: 5 livenessProbe: httpGet: path: /health/live port: 9000 initialDelaySeconds: 30 periodSeconds: 15 failureThreshold: 5 resources: requests: cpu: 500m memory: 1024Mi limits: cpu: 2000m memory: 2048Mi volumeMounts: - name: realm-import mountPath: /opt/keycloak/data/import readOnly: true - name: otel-agent mountPath: /otel volumes: - name: realm-import configMap: name: keycloak-realm-classic-models - name: otel-agent emptyDir: {}