diff --git a/apps/base/keycloak/database.yaml b/apps/base/keycloak/database.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..b9be4d9989166da77676117248f8a4046a7a0135
--- /dev/null
+++ b/apps/base/keycloak/database.yaml
@@ -0,0 +1,18 @@
+apiVersion: "acid.zalan.do/v1"
+kind: postgresql
+metadata:
+  name: keycloak-postgres
+  namespace: keycloak
+spec:
+  teamId: "keycloak"
+  volume:
+    size: 5Gi
+  numberOfInstances: 1
+  users:
+    keycloak:
+    - superuser
+    - createdb
+  databases:
+    keycloak: keycloak
+  postgresql:
+    version: "14"
diff --git a/apps/base/keycloak/kustomization.yaml b/apps/base/keycloak/kustomization.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..b5fb71bb547c45cb0b89e72abd30582434eb4b8b
--- /dev/null
+++ b/apps/base/keycloak/kustomization.yaml
@@ -0,0 +1,13 @@
+apiVersion: kustomize.config.k8s.io/v1beta1
+kind: Kustomization
+namespace: keycloak
+resources:
+  - namespace.yaml
+  - database.yaml
+  - release.yaml
+  - ../../../shared/networkpolicies/allow-from-same-namespace.yaml
+  - ../../../shared/networkpolicies/allow-from-ingress.yaml
+  - ../../../shared/networkpolicies/allow-from-monitoring.yaml
+  - ../../../shared/networkpolicies/allow-from-database.yaml
+patchesStrategicMerge:
+  - networkpolicy.yaml
diff --git a/apps/base/keycloak/namespace.yaml b/apps/base/keycloak/namespace.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..0deeb378527bbfe892742e3dc902d75bd730b161
--- /dev/null
+++ b/apps/base/keycloak/namespace.yaml
@@ -0,0 +1,34 @@
+apiVersion: v1
+kind: Namespace
+metadata:
+  name: keycloak
+---
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+  name: keycloak-reconciler
+  namespace: keycloak
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+  name: keycloak-reconciler
+  namespace: keycloak
+rules:
+  - apiGroups: ["*"]
+    resources: ["*"]
+    verbs: ["*"]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+  name: keycloak-reconciler
+  namespace: keycloak
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: Role
+  name: keycloak-reconciler
+subjects:
+  - kind: ServiceAccount
+    name: keycloak-reconciler
+    namespace: keycloak
diff --git a/apps/base/keycloak/networkpolicy.yaml b/apps/base/keycloak/networkpolicy.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..3c4598347a88eb0644fb548f5031fb2ee59913da
--- /dev/null
+++ b/apps/base/keycloak/networkpolicy.yaml
@@ -0,0 +1,20 @@
+---
+apiVersion: networking.k8s.io/v1
+kind: NetworkPolicy
+metadata:
+  name: allow-from-ingress
+spec:
+  podSelector:
+    matchLabels:
+      app.kubernetes.io/name: keycloak
+      app.kubernetes.io/instance: keycloak
+---
+apiVersion: networking.k8s.io/v1
+kind: NetworkPolicy
+metadata:
+  name: allow-from-monitoring
+spec:
+  podSelector:
+    matchLabels:
+      app.kubernetes.io/name: keycloak
+      app.kubernetes.io/instance: keycloak
diff --git a/apps/base/keycloak/release.yaml b/apps/base/keycloak/release.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..693b4184c38b08773f2424ad5c1aa662dc08e986
--- /dev/null
+++ b/apps/base/keycloak/release.yaml
@@ -0,0 +1,52 @@
+apiVersion: helm.toolkit.fluxcd.io/v2beta1
+kind: HelmRelease
+metadata:
+  name: keycloak
+  namespace: keycloak
+spec:
+  serviceAccountName: keycloak-reconciler
+  releaseName: keycloak
+  chart:
+    spec:
+      chart: ./charts/keycloak
+      sourceRef:
+        kind: GitRepository
+        name: flux-system
+        namespace: flux-system
+  interval: 5m
+  valuesFrom:
+    - kind: ConfigMap
+      name: keycloak-base-values
+      valuesKey: values.yaml
+    - kind: Secret
+      name: keycloak-override-values
+      valuesKey: values-overrides.yaml
+      optional: false
+    - kind: Secret
+      name: keycloak.keycloak-postgres.credentials.postgresql.acid.zalan.do
+      valuesKey: username
+      targetPath: keycloak.database.username
+    - kind: Secret
+      name: keycloak.keycloak-postgres.credentials.postgresql.acid.zalan.do
+      valuesKey: password
+      targetPath: keycloak.database.password
+  install:
+    remediation:
+      retries: -1
+  upgrade:
+    remediation:
+      retries: -1
+---
+apiVersion: v1
+kind: ConfigMap
+metadata:
+  name: keycloak-base-values
+  namespace: keycloak
+data:
+  values.yaml: |
+    keycloak:
+      database:
+        type: postgres
+        url: jdbc:postgresql://keycloak-postgres.keycloak.svc.cluster.local/keycloak
+    metrics:
+      enabled: true
diff --git a/apps/k8s01/keycloak/certificate.yaml b/apps/k8s01/keycloak/certificate.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..65c648c2c0d1be487a75aea4ac9a5616db4e0c3a
--- /dev/null
+++ b/apps/k8s01/keycloak/certificate.yaml
@@ -0,0 +1,64 @@
+apiVersion: cert-manager.io/v1
+kind: Certificate
+metadata:
+    name: keycloak-tls
+    namespace: keycloak
+spec:
+    dnsNames:
+        - ENC[AES256_GCM,data:XdXAdukLEN9OPdyIcRMIxRpGrH4rSs2D,iv:pKKkpZfYRQ5OUS8+jT3rFSNNroPPx2ikCFIZ6vGUIs8=,tag:jBs8pHjryEZ8qn1w9rigeA==,type:str]
+    issuerRef:
+        name: letsencrypt
+        kind: ClusterIssuer
+    secretName: ingress-keycloak-tls
+sops:
+    kms: []
+    gcp_kms: []
+    azure_kv: []
+    hc_vault: []
+    age: []
+    lastmodified: "2022-09-24T10:58:24Z"
+    mac: ENC[AES256_GCM,data:5kTbbCl4a3AnXJMsPJVaS9XoitjBnfCnbSPIgZ5thP16ElMJ148UAvM3np2ZKSCiPxqXhV35MPrHRNeR7jva4olq6di5JBDX8fLaqmoRDB3o/EJGtODBGTm29DJJ+iClnVpeXwuUeF6/9c9Hs1SG8450uAijxw5jFzpnpr6IkN4=,iv:Znt8tstoB9+czwWdu1uQ2/XMD2iJJwzqXujoZWGUKEE=,tag:WhT8GPosxxRQlZpZTi2Ykg==,type:str]
+    pgp:
+        - created_at: "2022-01-21T18:13:48Z"
+          enc: |-
+            -----BEGIN PGP MESSAGE-----
+
+            wcFMA7kpg2bgzVHcARAAHhDshl1OJqNRUolNvbIXzOuDzssJnvyi6cIZuMmVMsxf
+            a6wAWAtYOehvtn1ODL7/h4fIpBtfp7d8VuwfJSrh3ghUeiOl3zRzQbmaFA2L5/iG
+            Jd94tFAVwIl30qjcYqGVB2RF27VF1RElzgDLQh3hiXn1hDC+WmNSnBF5hwnwCFOL
+            wM4BHuE2AB4TX3PlYSo1n71VSzcCqRzbIxelZasYLnJQVL0VE6AjEd/fHS468R8N
+            aZ3mhmHW3sWzuLHNREMD2Q3ghkguLhau0VoETlYRI9103I4k7/khFrhAj5l2/PUr
+            2SWgpXyRqXVaKPeTiQs3QR8B5jNq3BlZj6Celw5Ig/wx3LY0EhI9e9WFgtSlZxM+
+            2yk65HQGvTIgsbys/z/0skA9vqik9csFRsH9iK42E/+XLvoAT6yxyl0cv1kBEyAS
+            ggPmKOq8+CT+voHzuh8kZHq9Sa8kH5xL1DQLzX2yIruV3OhTPSK+VlDpjUbycmI2
+            qR1oCo/snOJwwwvfl9vu0B8FCwhrz8554ZQBErFfJl6GFiUV8LElRlZh5S9Jiysr
+            nYJS5gxrcvjF/0Y6EHEfWDRDxvCHoWQpWhl2hRkh5UlQKH0ab+QWLYpISyNJxjfl
+            orQJdaVX3BQwhqMLwiMLGoaNGrSpmxXveLOZmsdK0obXC67lyE6ZM/Wy6gx2dFnS
+            5gFdXCLzQmmjYK8gIlsejQdnxZI2qWavZIN9T70OZQGaDE/S+U1uxKjuGBM7HTcP
+            7f1nUa6z96A9ydWs1xHjtm7k172V16PMSrvjQ8KLhFJd9eJDq3ksAA==
+            =XgF6
+            -----END PGP MESSAGE-----
+          fp: 286791FB6648539775DB31B8FCB98C2A3EC6F601
+        - created_at: "2022-01-21T18:13:48Z"
+          enc: |
+            -----BEGIN PGP MESSAGE-----
+
+            hQIMA4oYbIHZIrAPAQ//S/9rOkbd3beNH20dxgZ7VuZxgnjiV3Hd3om717njcMm2
+            kCfTJ3AmpLtQsT2s1W221tIyCwtHOobj82ANP9KzNi4e6v3LlNTIVHTQiHXk9KJP
+            AX6JoCOLu3bAI0xcdApNBU2wAlHBVC+T4BUfhPqD5AdHpW++e1qUIsM/6TViunHj
+            BWoIA0bpXqyOhTm1GbkJrHMgczJn2qgR5lBf8wgGmASd8jlNyfA7SxoKHj8sl/Ji
+            nucP/90dmyD2eBIJYdYS3anJYa2uP96oioG5xxIyfppnL5dwozDAit3Z5vvnBZNb
+            1rrpUnN8H0cCcaj7tmDEmjGfjGwxLKegQRZX7Pg5hwaaOOPGheXf8Ip/DpDf6T0n
+            Sq24X6DC5gD1RBU+YY6ZayMt/OKpVVVwRlY4BTDIUe4M+ecK/fve5vpDW2M+KWMc
+            pOkO1B09/prsX0w5XjFh8hb/6HlDDhomiB+BszcRCUDzocRzSEIFwMf7/iTaExe8
+            2fKCCHB4kHo6GHpydlQOpnGMOvDmiNKopXxTkFQUFQjyRmHGXf/u79JNXBjHkniv
+            ZiokjTEarwMp68dyiaL4L/5Uk+4NG3MetobqSaeW2TbeBwif3G2eFleYscz7QPIR
+            5ZBBhU/CoUEz2Xge6t8rlp8PNcQ1yq/R+tZjaeqIIT4++ZxCErhA0lsxyFrgLefU
+            aAEJAhD7hR3IMDGN2zOZSiw1IBz9P8Jss/oERQiuVpe/eTv5Vqj9vuL+koKftwnF
+            vSVkNo0fLwNLtnU659Mkoj9utoUL9tAhcCMpP3NehKkBG5RjF9crnIP6zT3lvVU0
+            GYyW4Lsfrt/a
+            =FfV+
+            -----END PGP MESSAGE-----
+          fp: B137EE1549DFAF960DD1E2B15147025FB9F09E07
+    encrypted_regex: ^(data|stringData|email|dnsZones?|dnsNames?|hosts?|tang|externalURL)$
+    version: 3.7.1
diff --git a/apps/k8s01/keycloak/keycloak-values.yaml b/apps/k8s01/keycloak/keycloak-values.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..8196fd6f4ef388029b0d2ee67856c0cd48287f4e
--- /dev/null
+++ b/apps/k8s01/keycloak/keycloak-values.yaml
@@ -0,0 +1,60 @@
+apiVersion: v1
+kind: Secret
+metadata:
+    name: keycloak-override-values
+    namespace: keycloak
+type: Opaque
+stringData:
+    values-overrides.yaml: ENC[AES256_GCM,data:g5WB8aX37Lls+G1VpxZQx4pJbEPcHlU9Ys1p3MA+1lY/5m9Ilh27QVgJQNvxMoJBqdVWDBMHy0o/rVNdU2ycCkBLRZrobtQn1WdAFYbi/nnEX3aLYVF2qu93aX2XC7GGFN2NsQ8LndvkobeOQKOCjpMvpHkv7LXaT1eKgqorVP3bSINd8c1MLYGP8Vq89ZT3YgJ1/KxPpQZ2/Jgw+S0HqNDheAPYLU9cFUxLw93b+UcFNZ79TTbOZ3M1G3SyAu4Vf5HjN6F5lX4gTs0Z+7gheI1TOpTYKlVylN68nYgUMSFCQBDp1u+/kTRJ0dARK9o4moQv/ZrEOk/lSoSybQJC2A7O4pIRxH+7QXYMg0m9U6qDf6NDChVKbieCf+kijbtIIOW1fDk2DZbi+uzOT4E8ae3XK/YnqkbhKAO7WHTfXo8wU2EhOj+yG4YAVdc8uWtuf63xvaxdzv9xo/ipI1Pc8FX0tZxuq5g=,iv:7hmGz34d+Z7XTWnZs72R0d/uBNj9YL4HEV8WLtvwIdY=,tag:YsFqq2usWQINzyLnIALRTQ==,type:str]
+sops:
+    kms: []
+    gcp_kms: []
+    azure_kv: []
+    hc_vault: []
+    age: []
+    lastmodified: "2022-09-24T11:48:29Z"
+    mac: ENC[AES256_GCM,data:xJFwarD96hBvFglyXPXS1Eo6LHq8MwsdA6XsFrEoErFP3AWg7DC2ag+mQAn/d9wem6dO9gH0jP0NncmKp3pSz0dR6/dr3YHHzdElvbZzSzGFEPX7NkO3eIPt8P0gXT0hzpoh9p3aY0YhAc32MmMrt/ejFzqq7vv2Sq5DqU/FJbg=,iv:LNRUMSWIGE48fNh04mcVjtezzUkHdjta288TOPO9t3I=,tag:9t/gfepo8y5SaLQfSD16RA==,type:str]
+    pgp:
+        - created_at: "2022-03-22T22:26:35Z"
+          enc: |-
+            -----BEGIN PGP MESSAGE-----
+
+            wcFMA7kpg2bgzVHcARAApcdDAfEgx93xGtkm4f7xTuRhvUyl8lw85rIHbWcAveYU
+            ayU88OLaoQyeZDZkOXbtxMHpry8GbId6vPAJ7KflT2eMP0A4uQGSSCQO6+5QcaYg
+            sbO/zT4vdprN7icLbvmmoK2Dh+hOo5Z7/7YGmdJfaaATzT2BGL/cVS1bonI83vXR
+            lzlW/DglIe7oNEKGVT5vWR5uGvq/dJwSRe/34eutEnJuV30imxHOcpxy3uXJFFXJ
+            3eKTk8dNLz3UE3IeUjbFdPFZYU+grOAOOCZRK0IOYFn+SF7E3dewgiwEdaXzz3gK
+            /6aEMEmf5vyVqn9jOaqZhKRqE7tW5HnhwIIlxcMPhkLVZvYf4F2EDA5f12C2hdp0
+            s7fFhU7v5GgFaHMJuaWVPxDnWTrNIst9bgeJv/N4RVfrLifrZJcqa9lE8ou0iCr5
+            dLi9d6UjsgWAREIViz+Uz7dJQ9QeJ6PGYgg/xgf0ihJFG7sx+TBG58DKb3G3tyUV
+            8hfK8Ou9m+zYnd13mJ2mV3rY0rmXusT+NcqTG2G4bBG5NimGpJS3rO7tAjjp/8sN
+            hMM46ay0vVTUXx1FwmjUFDG1e4sc7fKxTaCBizMjeUfZpAOiy/10YQmrFHBsftpo
+            K5j0nFMoG9NeO+2ffEmLhRtxvMe3WpINk7du3F624rYIGCB0aNUP69FCeJKuUQHS
+            5gH5AwnxOAtQakDksfLxJhUG1NlaS0iAFkZkTTibvOJwsY9L/scDDQlseb5zBKaZ
+            sOPwmn6hL4KavxF9BPG33ILkZKbkcvlaTlAMMY3iBs+MZeIB4+i/AA==
+            =SQqg
+            -----END PGP MESSAGE-----
+          fp: 286791FB6648539775DB31B8FCB98C2A3EC6F601
+        - created_at: "2022-03-22T22:26:35Z"
+          enc: |
+            -----BEGIN PGP MESSAGE-----
+
+            hQIMA4oYbIHZIrAPAQ//fGGoDT5KfsG/o6r5xhDHSc0IFH6zT2TFIB6TuA5SwHfV
+            8t3IeKD0bE//4f8AxGAVocw+AetolwrQL/Tl+n0UV9P44Jeh5VlCAGltHcowR53o
+            zdjS3+i9K3OOvQFhF+aYrPcnc+aTn9KbptHCam0w+Lr2UkYSAPAZHsBcoMp24mHX
+            6A+5kP1kaRzFzEn4TCNeTt13W1AsJIoSagkBWfYRBkRPk1OzGOuYqX6yeqj7a0kM
+            8uiloTQgWOiBSOyRtxUJi87CTrMXyb0F2E9HMyhgRnzF0YX0ZU0UVG8MNdRL8eFD
+            WYY68OK7DQw3zlJubscYQ2jltxKcq5g9qUCw/sXaNurtohIx9UeaHtfp036EMb22
+            5StgGEnBirUzfSrQGT3kuj20lcMtQAr/d1UsmQNjB36eOZSrx0m80pO8JVYL62/O
+            HLYnAHU52aAPtE7brNEVg4yRLCbWyVY3Z3H9OaTVXwNIMFoMEgkHHnNlsb+1ZnhV
+            cStKMO3H6W8eXQi3VGIVNhuC1ltsxHQL1I22Kr41JEnuaB9Jy5bsEbrO4XGyDdte
+            hMI8Gx+0KZAMlKuZKLS6sMa4oVnQTy8w20PtVrrS0zDrQRPpxBrOgzjrNeMj9FpS
+            q/efiCAOBc8eVd8N/7j66UItwrysfmIfsHWfoPotS7F6WmUHeAyoWjfcvTZyd4bU
+            ZgEJAhAtdCnHNvUSl5O9XZuSu51pRwj+O72kZXRSJWv7GTT9dsRfuM5Dy9A/tuVI
+            BuZraI4JyAWb2KbkM6onp3Rh9IcLuzqEYm/ETktxTtO1HlcVPJ2NMcFgTCzaIGX9
+            +rtkG7tPbA==
+            =tvBa
+            -----END PGP MESSAGE-----
+          fp: B137EE1549DFAF960DD1E2B15147025FB9F09E07
+    encrypted_regex: ^(data|stringData|email|dnsZones?|dnsNames?|hosts?|tang|externalURL|.*-secret|.*-url|.*Secrets?|.*-domain|password|subjects|node|apiURL|.*(S|s)erverNames?|.*SecretKey)$
+    version: 3.7.1
diff --git a/apps/k8s01/keycloak/kustomization.yaml b/apps/k8s01/keycloak/kustomization.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..55fc4d2b6c7ee69c8aaa241d202cdce9b1c55ca3
--- /dev/null
+++ b/apps/k8s01/keycloak/kustomization.yaml
@@ -0,0 +1,8 @@
+apiVersion: kustomize.config.k8s.io/v1beta1
+kind: Kustomization
+namespace: keycloak
+resources:
+  - ../../base/keycloak
+  - keycloak-values.yaml
+  - certificate.yaml
+  - ../../../shared/resourcequotas/default.yaml
diff --git a/charts/keycloak/.gitlab-ci.yaml b/charts/keycloak/.gitlab-ci.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..ec5975b8b01ef9ec0dbcc6e8b23bba4759e7ebeb
--- /dev/null
+++ b/charts/keycloak/.gitlab-ci.yaml
@@ -0,0 +1,11 @@
+build:
+  stage: build
+  image: quay.io/helmpack/chart-testing:v3.6.0
+  script:
+    - export TMP_DIR=$(mktemp -d)
+    - helm package charts/keycloak -d "$TMP_DIR"
+    - for i in $(ls $TMP_DIR/*.tgz); do curl --request POST --user "gitlab-ci-token:$CI_JOB_TOKEN" --form "chart=@${i}" "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/helm/api/${CI_COMMIT_REF_SLUG}/charts"; done
+  rules:
+    - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE == "push"'
+      changes:
+        - charts/keycloak/Chart.yaml
diff --git a/charts/keycloak/.helmignore b/charts/keycloak/.helmignore
new file mode 100644
index 0000000000000000000000000000000000000000..0e8a0eb36f4ca2c939201c0d54b5d82a1ea34778
--- /dev/null
+++ b/charts/keycloak/.helmignore
@@ -0,0 +1,23 @@
+# Patterns to ignore when building packages.
+# This supports shell glob matching, relative path matching, and
+# negation (prefixed with !). Only one pattern per line.
+.DS_Store
+# Common VCS dirs
+.git/
+.gitignore
+.bzr/
+.bzrignore
+.hg/
+.hgignore
+.svn/
+# Common backup files
+*.swp
+*.bak
+*.tmp
+*.orig
+*~
+# Various IDEs
+.project
+.idea/
+*.tmproj
+.vscode/
diff --git a/charts/keycloak/Chart.yaml b/charts/keycloak/Chart.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..875442d9cd3543d6d87157828623b1e02a6748dd
--- /dev/null
+++ b/charts/keycloak/Chart.yaml
@@ -0,0 +1,11 @@
+apiVersion: v2
+name: keycloak
+description: (Alpha) A Helm chart for Keycloak on Kubernetes
+type: application
+home: https://www.keycloak.org/
+icon: https://www.keycloak.org/resources/images/keycloak_icon_512px.svg
+sources:
+  - https://git.shivering-isles.com/shivering-isles/infrastructure-gitops
+  - https://github.com/keycloak/keycloak
+version: 0.1.3
+appVersion: "19.0.1"
diff --git a/charts/keycloak/Makefile b/charts/keycloak/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..d996e967de7fa83d3b34bbb062c7fffd8cb940f8
--- /dev/null
+++ b/charts/keycloak/Makefile
@@ -0,0 +1,10 @@
+lint:
+	koolbox helm lint
+
+test: lint helm-unittest
+
+helm-unittest:
+	podman run -ti --rm -v $$(pwd):/apps docker.io/quintush/helm-unittest:3.7.1-0.2.8 -3 .
+
+docs:
+	koolbox helm-docs
diff --git a/charts/keycloak/README.md b/charts/keycloak/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..121b78714d33ccb97169e85338776f5a68d525a5
--- /dev/null
+++ b/charts/keycloak/README.md
@@ -0,0 +1,64 @@
+# keycloak
+
+![Version: 0.1.0](https://img.shields.io/badge/Version-0.1.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 19.0.1](https://img.shields.io/badge/AppVersion-19.0.1-informational?style=flat-square)
+
+A Helm chart for Keycloak on Kubernetes
+
+**Homepage:** <https://www.keycloak.org/>
+
+## Source Code
+
+* <https://git.shivering-isles.com/shivering-isles/infrastructure-gitops>
+* <https://github.com/keycloak/keycloak>
+
+## Values
+
+| Key | Type | Default | Description |
+|-----|------|---------|-------------|
+| affinity | object | `{}` |  |
+| autoscaling.enabled | bool | `false` |  |
+| autoscaling.maxReplicas | int | `100` |  |
+| autoscaling.minReplicas | int | `1` |  |
+| autoscaling.targetCPUUtilizationPercentage | int | `80` |  |
+| fullnameOverride | string | `""` |  |
+| image.pullPolicy | string | `"IfNotPresent"` |  |
+| image.repository | string | `"quay.io/keycloak/keycloak"` |  |
+| image.tag | string | `""` |  |
+| imagePullSecrets | list | `[]` |  |
+| ingress.annotations | object | `{}` |  |
+| ingress.className | string | `""` |  |
+| ingress.enabled | bool | `false` |  |
+| ingress.hosts[0].host | string | `"chart-example.local"` |  |
+| ingress.hosts[0].paths[0].path | string | `"/"` |  |
+| ingress.hosts[0].paths[0].pathType | string | `"ImplementationSpecific"` |  |
+| ingress.tls | list | `[]` |  |
+| keycloak.database.password | string | `nil` | password of the database user |
+| keycloak.database.type | string | `"dev-file"` | Type of the database, see `db` at https://www.keycloak.org/server/db#_configuring_a_database |
+| keycloak.database.url | string | `nil` | database URL, see `db-url` at https://www.keycloak.org/server/db#_configuring_a_database jdbc:postgresql://localhost/keycloak |
+| keycloak.database.username | string | `nil` | username of the database user |
+| keycloak.features | list | `[]` | list of features that should be enabled on the keycloak instance. See `features` at https://www.keycloak.org/server/containers#_relevant_options |
+| keycloak.hostname | string | `nil` |  |
+| keycloak.proxy | string | `"edge"` | proxy configuration, See https://www.keycloak.org/server/reverseproxy |
+| metrics.enabled | bool | `false` |  |
+| metrics.interval | string | `nil` |  |
+| metrics.scrapeTimeout | string | `nil` |  |
+| nameOverride | string | `""` |  |
+| nodeSelector | object | `{}` |  |
+| podAnnotations | object | `{}` |  |
+| podSecurityContext | object | `{}` |  |
+| replicaCount | int | `1` |  |
+| resources.limits.cpu | string | `"1"` |  |
+| resources.limits.memory | string | `"1.5Gi"` |  |
+| resources.requests.cpu | string | `"100m"` |  |
+| resources.requests.memory | string | `"1Gi"` |  |
+| securityContext.capabilities.drop[0] | string | `"ALL"` |  |
+| securityContext.runAsNonRoot | bool | `true` |  |
+| service.port | int | `80` |  |
+| service.type | string | `"ClusterIP"` |  |
+| serviceAccount.annotations | object | `{}` | Annotations to add to the service account |
+| serviceAccount.create | bool | `true` | Specifies whether a service account should be created |
+| serviceAccount.name | string | `""` | The name of the service account to use. If not set and create is true, a name is generated using the fullname template |
+| tolerations | list | `[]` |  |
+
+----------------------------------------------
+Autogenerated from chart metadata using [helm-docs v1.11.0](https://github.com/norwoodj/helm-docs/releases/v1.11.0)
diff --git a/charts/keycloak/templates/NOTES.txt b/charts/keycloak/templates/NOTES.txt
new file mode 100644
index 0000000000000000000000000000000000000000..fb8d0cef3dac9ee8e65fe93eb518e0bbf7d79740
--- /dev/null
+++ b/charts/keycloak/templates/NOTES.txt
@@ -0,0 +1,22 @@
+1. Get the application URL by running these commands:
+{{- if .Values.ingress.enabled }}
+{{- range $host := .Values.ingress.hosts }}
+  {{- range .paths }}
+  http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }}
+  {{- end }}
+{{- end }}
+{{- else if contains "NodePort" .Values.service.type }}
+  export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "keycloak.fullname" . }})
+  export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
+  echo http://$NODE_IP:$NODE_PORT
+{{- else if contains "LoadBalancer" .Values.service.type }}
+     NOTE: It may take a few minutes for the LoadBalancer IP to be available.
+           You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "keycloak.fullname" . }}'
+  export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "keycloak.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
+  echo http://$SERVICE_IP:{{ .Values.service.port }}
+{{- else if contains "ClusterIP" .Values.service.type }}
+  export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "keycloak.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
+  export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
+  echo "Visit http://127.0.0.1:8080 to use your application"
+  kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT
+{{- end }}
diff --git a/charts/keycloak/templates/_helpers.tpl b/charts/keycloak/templates/_helpers.tpl
new file mode 100644
index 0000000000000000000000000000000000000000..5ae124b1bafa62cefef3779a6b9d8e4642cb167e
--- /dev/null
+++ b/charts/keycloak/templates/_helpers.tpl
@@ -0,0 +1,62 @@
+{{/*
+Expand the name of the chart.
+*/}}
+{{- define "keycloak.name" -}}
+{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Create a default fully qualified app name.
+We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
+If release name contains chart name it will be used as a full name.
+*/}}
+{{- define "keycloak.fullname" -}}
+{{- if .Values.fullnameOverride }}
+{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- $name := default .Chart.Name .Values.nameOverride }}
+{{- if contains $name .Release.Name }}
+{{- .Release.Name | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
+{{- end }}
+{{- end }}
+{{- end }}
+
+{{/*
+Create chart name and version as used by the chart label.
+*/}}
+{{- define "keycloak.chart" -}}
+{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Common labels
+*/}}
+{{- define "keycloak.labels" -}}
+helm.sh/chart: {{ include "keycloak.chart" . }}
+{{ include "keycloak.selectorLabels" . }}
+{{- if .Chart.AppVersion }}
+app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
+{{- end }}
+app.kubernetes.io/managed-by: {{ .Release.Service }}
+{{- end }}
+
+{{/*
+Selector labels
+*/}}
+{{- define "keycloak.selectorLabels" -}}
+app.kubernetes.io/name: {{ include "keycloak.name" . }}
+app.kubernetes.io/instance: {{ .Release.Name }}
+{{- end }}
+
+{{/*
+Create the name of the service account to use
+*/}}
+{{- define "keycloak.serviceAccountName" -}}
+{{- if .Values.serviceAccount.create }}
+{{- default (include "keycloak.fullname" .) .Values.serviceAccount.name }}
+{{- else }}
+{{- default "default" .Values.serviceAccount.name }}
+{{- end }}
+{{- end }}
diff --git a/charts/keycloak/templates/deployment.yaml b/charts/keycloak/templates/deployment.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..e7cf34037e3e60b800d090ee0756a4fb743f23a9
--- /dev/null
+++ b/charts/keycloak/templates/deployment.yaml
@@ -0,0 +1,109 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: {{ include "keycloak.fullname" . }}
+  labels:
+    {{- include "keycloak.labels" . | nindent 4 }}
+spec:
+  strategy:
+    type: Recreate
+  {{- if not .Values.autoscaling.enabled }}
+  replicas: {{ .Values.replicaCount }}
+  {{- end }}
+  selector:
+    matchLabels:
+      {{- include "keycloak.selectorLabels" . | nindent 6 }}
+  template:
+    metadata:
+      annotations:
+        checksum/config: {{ include (print $.Template.BasePath "/secret.yaml") . | sha256sum }}
+        {{- with .Values.podAnnotations }}
+        {{- toYaml . | nindent 8 }}
+        {{- end }}
+      labels:
+        {{- include "keycloak.selectorLabels" . | nindent 8 }}
+    spec:
+      {{- with .Values.imagePullSecrets }}
+      imagePullSecrets:
+        {{- toYaml . | nindent 8 }}
+      {{- end }}
+      serviceAccountName: {{ include "keycloak.serviceAccountName" . }}
+      securityContext:
+        {{- toYaml .Values.podSecurityContext | nindent 8 }}
+      containers:
+        - name: {{ .Chart.Name }}
+          securityContext:
+            {{- toYaml .Values.securityContext | nindent 12 }}
+          image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
+          imagePullPolicy: {{ .Values.image.pullPolicy }}
+          args:
+            - start
+            - --proxy
+            - '{{ .Values.keycloak.proxy }}'
+            {{- if gt (len .Values.keycloak.features) 0 }}
+            - --features
+            - '{{ join "," .Values.keycloak.features }}'
+            {{- end }}
+          env:
+            - name: KC_HEALTH_ENABLED
+              value: "true"
+            {{- if .Values.metrics.enabled }}
+            - name: KC_METRICS_ENABLED
+              value: "true"
+            {{- end }}
+            - name: KC_HOSTNAME
+              value: "{{ required "Setting a hostname is required" .Values.keycloak.hostname }}"
+            {{- if .Values.keycloak.database.type}}
+            - name: KC_DB
+              value: "{{ .Values.keycloak.database.type }}"
+            {{- end }}
+            {{- if .Values.keycloak.database.username }}
+            - name: KC_DB_USERNAME
+              valueFrom:
+                secretKeyRef:
+                  name: {{ include "keycloak.fullname" . }}
+                  key:  database-username
+                  optional: false
+            {{- end }}
+            {{- if .Values.keycloak.database.password }}
+            - name: KC_DB_PASSWORD
+              valueFrom:
+                secretKeyRef:
+                  name: {{ include "keycloak.fullname" . }}
+                  key:  database-password
+                  optional: false
+            {{- end }}
+            {{- if .Values.keycloak.database.url }}
+            - name: KC_DB_URL
+              valueFrom:
+                secretKeyRef:
+                  name: {{ include "keycloak.fullname" . }}
+                  key:  database-url
+                  optional: false
+            {{- end }}
+          ports:
+            - name: http
+              containerPort: 8080
+              protocol: TCP
+          livenessProbe:
+            httpGet:
+              path: /health/live
+              port: http
+          readinessProbe:
+            httpGet:
+              path: /health/ready
+              port: http
+          resources:
+            {{- toYaml .Values.resources | nindent 12 }}
+      {{- with .Values.nodeSelector }}
+      nodeSelector:
+        {{- toYaml . | nindent 8 }}
+      {{- end }}
+      {{- with .Values.affinity }}
+      affinity:
+        {{- toYaml . | nindent 8 }}
+      {{- end }}
+      {{- with .Values.tolerations }}
+      tolerations:
+        {{- toYaml . | nindent 8 }}
+      {{- end }}
diff --git a/charts/keycloak/templates/hpa.yaml b/charts/keycloak/templates/hpa.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..a7660b5082b73f8ff67516d8b2d5afe9463107da
--- /dev/null
+++ b/charts/keycloak/templates/hpa.yaml
@@ -0,0 +1,28 @@
+{{- if .Values.autoscaling.enabled }}
+apiVersion: autoscaling/v2beta1
+kind: HorizontalPodAutoscaler
+metadata:
+  name: {{ include "keycloak.fullname" . }}
+  labels:
+    {{- include "keycloak.labels" . | nindent 4 }}
+spec:
+  scaleTargetRef:
+    apiVersion: apps/v1
+    kind: Deployment
+    name: {{ include "keycloak.fullname" . }}
+  minReplicas: {{ .Values.autoscaling.minReplicas }}
+  maxReplicas: {{ .Values.autoscaling.maxReplicas }}
+  metrics:
+    {{- if .Values.autoscaling.targetCPUUtilizationPercentage }}
+    - type: Resource
+      resource:
+        name: cpu
+        targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }}
+    {{- end }}
+    {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }}
+    - type: Resource
+      resource:
+        name: memory
+        targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }}
+    {{- end }}
+{{- end }}
diff --git a/charts/keycloak/templates/ingress.yaml b/charts/keycloak/templates/ingress.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..d2e79e396dc35a243ad6249da425cabb96d62c15
--- /dev/null
+++ b/charts/keycloak/templates/ingress.yaml
@@ -0,0 +1,61 @@
+{{- if .Values.ingress.enabled -}}
+{{- $fullName := include "keycloak.fullname" . -}}
+{{- $svcPort := .Values.service.port -}}
+{{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }}
+  {{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }}
+  {{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}}
+  {{- end }}
+{{- end }}
+{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}}
+apiVersion: networking.k8s.io/v1
+{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}}
+apiVersion: networking.k8s.io/v1beta1
+{{- else -}}
+apiVersion: extensions/v1beta1
+{{- end }}
+kind: Ingress
+metadata:
+  name: {{ $fullName }}
+  labels:
+    {{- include "keycloak.labels" . | nindent 4 }}
+  {{- with .Values.ingress.annotations }}
+  annotations:
+    {{- toYaml . | nindent 4 }}
+  {{- end }}
+spec:
+  {{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }}
+  ingressClassName: {{ .Values.ingress.className }}
+  {{- end }}
+  {{- if .Values.ingress.tls }}
+  tls:
+    {{- range .Values.ingress.tls }}
+    - hosts:
+        {{- range .hosts }}
+        - {{ . | quote }}
+        {{- end }}
+      secretName: {{ .secretName }}
+    {{- end }}
+  {{- end }}
+  rules:
+    {{- range .Values.ingress.hosts }}
+    - host: {{ .host | quote }}
+      http:
+        paths:
+          {{- range .paths }}
+          - path: {{ .path }}
+            {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }}
+            pathType: {{ .pathType }}
+            {{- end }}
+            backend:
+              {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }}
+              service:
+                name: {{ $fullName }}
+                port:
+                  number: {{ $svcPort }}
+              {{- else }}
+              serviceName: {{ $fullName }}
+              servicePort: {{ $svcPort }}
+              {{- end }}
+          {{- end }}
+    {{- end }}
+{{- end }}
diff --git a/charts/keycloak/templates/poddisruptionbudget.yaml b/charts/keycloak/templates/poddisruptionbudget.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/charts/keycloak/templates/secret.yaml b/charts/keycloak/templates/secret.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..46d46b9da22a349aafb7eb3896a7159cdfb9aa07
--- /dev/null
+++ b/charts/keycloak/templates/secret.yaml
@@ -0,0 +1,11 @@
+apiVersion: v1
+kind: Secret
+metadata:
+  name: {{ include "keycloak.fullname" . }}
+  labels:
+    {{- include "keycloak.labels" . | nindent 4 }}
+type: Opaque
+stringData:
+  database-username: {{ required "An external database username is required." .Values.keycloak.database.username }}
+  database-password: {{ required "An external database user password is required." .Values.keycloak.database.password }}
+  database-url:      {{ required "An external database URL is required." .Values.keycloak.database.url }}
diff --git a/charts/keycloak/templates/service.yaml b/charts/keycloak/templates/service.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..14ce8c7c666b48dd29cd2ff4710fbed35fb0383c
--- /dev/null
+++ b/charts/keycloak/templates/service.yaml
@@ -0,0 +1,15 @@
+apiVersion: v1
+kind: Service
+metadata:
+  name: {{ include "keycloak.fullname" . }}
+  labels:
+    {{- include "keycloak.labels" . | nindent 4 }}
+spec:
+  type: {{ .Values.service.type }}
+  ports:
+    - port: {{ .Values.service.port }}
+      targetPort: http
+      protocol: TCP
+      name: http
+  selector:
+    {{- include "keycloak.selectorLabels" . | nindent 4 }}
diff --git a/charts/keycloak/templates/serviceaccount.yaml b/charts/keycloak/templates/serviceaccount.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..ef5153b77b9621d0e4a54e759f45d4382bc20878
--- /dev/null
+++ b/charts/keycloak/templates/serviceaccount.yaml
@@ -0,0 +1,12 @@
+{{- if .Values.serviceAccount.create -}}
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+  name: {{ include "keycloak.serviceAccountName" . }}
+  labels:
+    {{- include "keycloak.labels" . | nindent 4 }}
+  {{- with .Values.serviceAccount.annotations }}
+  annotations:
+    {{- toYaml . | nindent 4 }}
+  {{- end }}
+{{- end }}
diff --git a/charts/keycloak/templates/servicemonitor.yaml b/charts/keycloak/templates/servicemonitor.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..a425ffddb7e6650e195b83f7973f49640506368b
--- /dev/null
+++ b/charts/keycloak/templates/servicemonitor.yaml
@@ -0,0 +1,24 @@
+{{- if .Values.metrics.enabled }}
+apiVersion: monitoring.coreos.com/v1
+kind: ServiceMonitor
+metadata:
+  name: {{ template "keycloak.fullname" . }}
+  labels:
+    {{- include "keycloak.labels" . | nindent 4 }}
+spec:
+  endpoints:
+    - port: http
+      {{- if .Values.metrics.interval }}
+      interval: {{ .Values.metrics.interval }}
+      {{- end }}
+      {{- if .Values.metrics.scrapeTimeout }}
+      scrapeTimeout: {{ .Values.metrics.scrapeTimeout }}
+      {{- end }}
+      honorLabels: true
+  namespaceSelector:
+    matchNames:
+      - {{ .Release.Namespace }}
+  selector:
+    matchLabels:
+      {{- include "keycloak.selectorLabels" . | nindent 6 }}
+{{- end }}
diff --git a/charts/keycloak/templates/tests/test-connection.yaml b/charts/keycloak/templates/tests/test-connection.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..a44cb460e85c6c70309c80a2a6e9a399ffa4f113
--- /dev/null
+++ b/charts/keycloak/templates/tests/test-connection.yaml
@@ -0,0 +1,15 @@
+apiVersion: v1
+kind: Pod
+metadata:
+  name: "{{ include "keycloak.fullname" . }}-test-connection"
+  labels:
+    {{- include "keycloak.labels" . | nindent 4 }}
+  annotations:
+    "helm.sh/hook": test
+spec:
+  containers:
+    - name: wget
+      image: busybox
+      command: ['wget']
+      args: ['{{ include "keycloak.fullname" . }}:{{ .Values.service.port }}']
+  restartPolicy: Never
diff --git a/charts/keycloak/tests/__snapshot__/snapshot_test.yaml.snap b/charts/keycloak/tests/__snapshot__/snapshot_test.yaml.snap
new file mode 100644
index 0000000000000000000000000000000000000000..cec153ea73afcff90e7503d4613634534f9e6e67
--- /dev/null
+++ b/charts/keycloak/tests/__snapshot__/snapshot_test.yaml.snap
@@ -0,0 +1,113 @@
+should match snapshot:
+  1: |
+    apiVersion: apps/v1
+    kind: Deployment
+    metadata:
+      labels:
+        app.kubernetes.io/instance: RELEASE-NAME
+        app.kubernetes.io/managed-by: Helm
+        app.kubernetes.io/name: keycloak
+        app.kubernetes.io/version: 4.5.6
+        helm.sh/chart: keycloak-1.2.3
+      name: RELEASE-NAME-keycloak
+    spec:
+      replicas: 1
+      selector:
+        matchLabels:
+          app.kubernetes.io/instance: RELEASE-NAME
+          app.kubernetes.io/name: keycloak
+      template:
+        metadata:
+          labels:
+            app.kubernetes.io/instance: RELEASE-NAME
+            app.kubernetes.io/name: keycloak
+        spec:
+          containers:
+          - args:
+            - start
+            - --cache-stack
+            - kubernetes
+            - --proxy
+            - edge
+            - --features
+            - ""
+            env:
+            - name: KC_HEALTH_ENABLED
+              value: "true"
+            image: quay.io/keycloak/keycloak:4.5.6
+            imagePullPolicy: IfNotPresent
+            livenessProbe:
+              httpGet:
+                path: /health/live
+                port: http
+            name: keycloak
+            ports:
+            - containerPort: 8080
+              name: http
+              protocol: TCP
+            readinessProbe:
+              httpGet:
+                path: /health/ready
+                port: http
+            resources:
+              limits:
+                cpu: "1"
+                memory: 1.5Gi
+              requests:
+                cpu: 100m
+                memory: 1Gi
+            securityContext:
+              capabilities:
+                drop:
+                - ALL
+              runAsNonRoot: true
+          securityContext: {}
+          serviceAccountName: RELEASE-NAME-keycloak
+  2: |
+    apiVersion: v1
+    kind: Secret
+    metadata:
+      app.kubernetes.io/instance: RELEASE-NAME
+      app.kubernetes.io/managed-by: Helm
+      app.kubernetes.io/name: keycloak
+      app.kubernetes.io/version: 4.5.6
+      helm.sh/chart: keycloak-1.2.3
+      labels: null
+      name: RELEASE-NAME-keycloak
+    stringData:
+      database-password: null
+      database-url: null
+      database-username: null
+    type: Opaque
+  3: |
+    apiVersion: v1
+    kind: Service
+    metadata:
+      labels:
+        app.kubernetes.io/instance: RELEASE-NAME
+        app.kubernetes.io/managed-by: Helm
+        app.kubernetes.io/name: keycloak
+        app.kubernetes.io/version: 4.5.6
+        helm.sh/chart: keycloak-1.2.3
+      name: RELEASE-NAME-keycloak
+    spec:
+      ports:
+      - name: http
+        port: 80
+        protocol: TCP
+        targetPort: http
+      selector:
+        app.kubernetes.io/instance: RELEASE-NAME
+        app.kubernetes.io/name: keycloak
+      type: ClusterIP
+  4: |
+    apiVersion: v1
+    kind: ServiceAccount
+    metadata:
+      labels:
+        app.kubernetes.io/instance: RELEASE-NAME
+        app.kubernetes.io/managed-by: Helm
+        app.kubernetes.io/name: keycloak
+        app.kubernetes.io/version: 4.5.6
+        helm.sh/chart: keycloak-1.2.3
+      name: RELEASE-NAME-keycloak
diff --git a/charts/keycloak/tests/snapshot_test.yaml b/charts/keycloak/tests/snapshot_test.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..f126d02f84a52411502c45f8c099f73f5bd5a5f5
--- /dev/null
+++ b/charts/keycloak/tests/snapshot_test.yaml
@@ -0,0 +1,13 @@
+suite: Keycloak
+templates:
+  - deployment.yaml
+  - secret.yaml
+  - service.yaml
+  - serviceaccount.yaml
+tests:
+  - it: should match snapshot
+    chart:
+      version: 1.2.3
+      appVersion: 4.5.6
+    asserts:
+      - matchSnapshot: {}
diff --git a/charts/keycloak/values.yaml b/charts/keycloak/values.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..31a45e82ebd61deb9671a28b4a4f0235fe26f19e
--- /dev/null
+++ b/charts/keycloak/values.yaml
@@ -0,0 +1,102 @@
+# Default values for keycloak.
+# This is a YAML-formatted file.
+# Declare variables to be passed into your templates.
+
+# Number of keycloak replicas running
+replicaCount: 1
+
+image:
+  # -- Keycloak image to be used
+  repository: quay.io/keycloak/keycloak
+  # -- pull policy used for the keycloak container
+  pullPolicy: IfNotPresent
+  # -- Overrides the image tag whose default is the chart appVersion.
+  tag: ""
+
+keycloak:
+  # -- Hostname used for the keycloak installation
+  hostname: keycloak.example.com
+  database:
+    # -- Type of the database, see `db` at https://www.keycloak.org/server/db#_configuring_a_database
+    type: postgres
+    # -- database URL, see `db-url` at https://www.keycloak.org/server/db#_configuring_a_database
+    # jdbc:postgresql://localhost/keycloak
+    url: null
+    # -- username of the database user
+    username: null
+    # -- password of the database user
+    password: null
+  # -- proxy configuration, See https://www.keycloak.org/server/reverseproxy
+  proxy: edge
+  # -- list of features that should be enabled on the keycloak instance. See `features` at https://www.keycloak.org/server/containers#_relevant_options
+  features: []
+
+imagePullSecrets: []
+nameOverride: ""
+fullnameOverride: ""
+
+serviceAccount:
+  # -- Specifies whether a service account should be created
+  create: true
+  # -- Annotations to add to the service account
+  annotations: {}
+  # -- The name of the service account to use. If not set and create is true, a name is generated using the fullname template
+  name: ""
+
+podAnnotations: {}
+
+podSecurityContext: {}
+  # fsGroup: 2000
+
+securityContext:
+  capabilities:
+    drop:
+      - ALL
+  allowPrivilegeEscalation: false
+  runAsNonRoot: true
+
+service:
+  type: ClusterIP
+  port: 80
+
+ingress:
+  enabled: false
+  className: ""
+  annotations: {}
+    # kubernetes.io/ingress.class: nginx
+    # kubernetes.io/tls-acme: "true"
+  hosts:
+    - host: chart-example.local
+      paths:
+        - path: /
+          pathType: ImplementationSpecific
+  tls: []
+  #  - secretName: chart-example-tls
+  #    hosts:
+  #      - chart-example.local
+
+resources:
+  limits:
+    cpu: "1"
+    memory: 1.5Gi
+  requests:
+    cpu: 100m
+    memory: 1Gi
+
+autoscaling:
+  enabled: false
+  minReplicas: 1
+  maxReplicas: 100
+  targetCPUUtilizationPercentage: 80
+  # targetMemoryUtilizationPercentage: 80
+
+metrics:
+  enabled: false
+  scrapeTimeout: null
+  interval: null
+
+nodeSelector: {}
+
+tolerations: []
+
+affinity: {}