Customization
This page will describe how you can customize your Keycloak instance.
Themes, providers and custom files
VSHNKeycloak allows you to use custom providers, themes and files.
Create a custom container image and use it to customize your Keycloak instance as described below.
apiVersion: vshn.appcat.vshn.io/v1
kind: VSHNKeycloak
metadata:
  name: keycloak-app1-prod
  namespace: prod-app
spec:
  parameters:
    service:
      version: "26"
      customizationImage:
        image: https://registry/user/image:tag (1)
        imagePullSecretRef:
          name: secret-name (2)
          namespace: secret-namespace (3)
      customFiles:
        - source: path/in/customization/image (4)
          destination: path/in/keycloak/instance (5)
    size:
      plan: standard-2
  writeConnectionSecretToRef:
    name: keycloak-creds| 1 | Container image url | 
| 2 | Secret name containing credentials for the registry with key .dockerconfigjson | 
| 3 | Secret namespace | 
| 4 | File in your customization image (can also be a folder) | 
| 5 | Destination path in Keycloak instance, relative to /opt/keycloak | 
| Themes and providers will be implicitly copied if found in your image.
You do not need to add them separately in customFiles. | 
Configuration
You can provide a custom configuration to Keycloak using a ConfigMap.
The content of the ConfigMap is a JSON file based on the Keycloak export files.
You can logically separate different configurations inside the ConfigMap by using different keys. However, each key must end in .json.
Changes in the underlying ConfigMap or Secret referenced by customConfigurationRef or customEnvVariablesRef will trigger an external Job to configure the Keycloak instance via its API. The configuration will be applied dynamically and loaded into the database without requiring a restart of the Keycloak instance.
To speed up the propagation of changes, refer to the section Propagating Changes to Referenced Resources.
An extensive set of configuration examples can be found here.
The configuration also supports variable substitution.
Variables exposed by Quarkus (through <quarkus.io/guides/config-reference>) can be accessed by $(property.name)
In additional, the string substitution support multiple prefixes for different approaches:
Base64 Decoder:        $(base64Decoder:SGVsbG9Xb3JsZCE=)
Base64 Encoder:        $(base64Encoder:HelloWorld!)
Java Constant:         $(const:java.awt.event.KeyEvent.VK_ESCAPE)
Date:                  $(date:yyyy-MM-dd)
DNS:                   $(dns:address|apache.org)
Environment Variable:  $(env:USERNAME)
File Content:          $(file:UTF-8:src/test/resources/document.properties)
Java:                  $(java:version)
Localhost:             $(localhost:canonical-name)
Properties File:       $(properties:src/test/resources/document.properties::mykey)
Resource Bundle:       $(resourceBundle:org.example.testResourceBundleLookup:mykey)
Script:                $(script:javascript:3 + 4)
System Property:       $(sys:user.dir)
URL Decoder:           $(urlDecoder:Hello%20World%21)
URL Encoder:           $(urlEncoder:Hello World!)
URL Content (HTTP):    $(url:UTF-8:http://www.apache.org)
URL Content (HTTPS):   $(url:UTF-8:https://www.apache.org)
URL Content (File):    $(url:UTF-8:file:///$(sys:user.dir)/src/test/resources/document.properties)
XML XPath:             $(xml:src/test/resources/document.xml:/root/path/to/node)Example
The following example demonstrates how a custom configuration that creates a new realm looks like:
apiVersion: v1
kind: ConfigMap
metadata:
  name: keycloak-app1-prod-config
  namespace: prod-app
data:
  keycloak-config.json: |
    {
      "enabled": true,
      "realm": "prod-app"
    }apiVersion: vshn.appcat.vshn.io/v1
kind: VSHNKeycloak
metadata:
  name: keycloak-app1-prod
  namespace: prod-app
spec:
  parameters:
    service:
      customConfigurationRef: keycloak-app1-prod-config (1)| 1 | The name of the ConfigMap. Must be in the same namespace as the Keycloak claim. | 
Environment variables
You can pass custom environment variables to your Keycloak instance. These variables can be used by your custom providers or custom configuration.
| The customEnvVariablesRefparameter has been deprecated in favor of the newenvFromparameter. WhilecustomEnvVariablesRefis still supported for backward compatibility, it is recommended to useenvFromfor defining environment variables, as it allows specifying multiple sources, including bothConfigMapandSecret. | 
Using envFrom
The envFrom parameter allows you to define multiple sources for environment variables. Each source can be a ConfigMap or a Secret.
Example
apiVersion: vshn.appcat.vshn.io/v1
kind: VSHNKeycloak
metadata:
  name: keycloak-app-envfrom
  namespace: prod-app
spec:
  parameters:
    service:
      envFrom:
        - configMapRef:
            name: env-from-cm (1)
        - secretRef:
            name: env-from-secret (2)| 1 | Reference to a ConfigMapcontaining environment variables. | 
| 2 | Reference to a Secretcontaining environment variables. | 
Each referenced ConfigMap or Secret must be in the same namespace as the Keycloak claim.
Using customEnvVariablesRef (Deprecated)
The customEnvVariablesRef parameter allows you to reference a single Secret containing environment variables. While this method is deprecated, it is still supported for backward compatibility.
Example
apiVersion: v1
kind: Secret
metadata:
  name: keycloak-app2-prod-env
  namespace: prod-app
stringData:
  REALM_NAME: prod-app
type: OpaqueapiVersion: v1
kind: ConfigMap
metadata:
  name: keycloak-app2-prod-config
  namespace: prod-app
data:
  keycloak-config.json: |
    {
      "enabled": true,
      "$(env:REALM_NAME)"
    }apiVersion: vshn.appcat.vshn.io/v1
kind: VSHNKeycloak
metadata:
  name: keycloak-app2-prod
  namespace: prod-app
spec:
  parameters:
    service:
      customConfigurationRef: keycloak-app2-prod-config (1)
      customEnvVariablesRef: keycloak-app2-prod-env (2)| 1 | The name of the ConfigMap. Must be in the same namespace as the Keycloak claim. | 
| 2 | The name of the Secretthat contains the environment variables. Must be in the same namespace as the Keycloak claim. | 
Custom Mounts
You can mount additional Kubernetes Secrets and ConfigMaps directly into the Keycloak container’s filesystem by using the customMounts parameter under spec.parameters.service.
apiVersion: vshn.appcat.vshn.io/v1
kind: VSHNKeycloak
metadata:
  name: keycloak-app3-prod
  namespace: prod-app
spec:
  parameters:
    service:
      customMounts:
        - name: custom-secret1
          type: secret
        - name: custom-secret2
          type: secret
        - name: custom-configmap
          type: configMapBy default, mounts follow this folder structure:
- 
Secrets ( type: secret) are mounted under/custom/secrets/{name}/…
- 
ConfigMaps ( type: configMap) are mounted under/custom/configs/{name}/…
For example, a Secret named custom-secret1 will be available under /custom/secrets/custom-secret1/, and a ConfigMap named custom-configmap under /custom/configs/custom-configmap/.
Mount names must be unique within the customMounts list to avoid folder name collisions inside the container.
Propagating Changes to Referenced Resources
When you make changes to a ConfigMap or Secret that is referenced by your VSHNKeycloak instance (e.g., via customConfigurationRef, customEnvVariablesRef or customMounts), these changes are not immediately propagated to the running Keycloak instance.
To force a reconciliation and apply the updated configuration or environment variables, you need to annotate the VSHNKeycloak resource. This tells Crossplane to re-evaluate the resource and apply any changes from its external references.
kubectl annotate vshnkeycloak [NAME] -n [NAMESPACE] crossplane.io/touch="$(date +%s)" --overwriteReplace [NAME] with the name of your VSHNKeycloak instance (e.g., keycloak-app1-prod or keycloak-app2-prod), and [NAMESPACE] with the namespace where the claim was created.