Skip to content

Installation

This page covers installing the cluster prerequisites and the bnerd-keycloak-operator itself.

Cluster prerequisites

The operator renders raw Kubernetes resources directly — there is no Flux or Helm indirection to satisfy at reconcile time. The following are optional depending on which features you use.

1. Percona PostgreSQL Operator (for managed Postgres)

Required only when a KeycloakInstance uses spec.postgres.managed: true. Install the Percona PG Operator into its own namespace:

helm repo add percona https://percona.github.io/percona-helm-charts/
helm install pg-operator percona/pg-operator \
  --namespace pgo \
  --create-namespace

Verify:

kubectl get crd perconapgclusters.pgv2.percona.com

No silent fallback

If spec.postgres.managed: true is set but this CRD is not installed, the operator sets the instance to phase: Failed with reason PerconaCRDMissing rather than falling back to an in-cluster or BYO database.

2. Ingress controller (for Ingress objects)

Required only when spec.ingress.enabled is true (the default). The operator's Ingress rules default to class nginx (public) and nginx-internal (admin) — install ingress-nginx or another controller and set spec.ingress.class / spec.ingress.adminClass to match.

kubectl get ingressclass

For dev/kind clusters without an ingress controller, set spec.ingress.enabled: false to get Services only.

3. cert-manager (for TLS)

Required only when spec.ingress.clusterIssuer is set. Without it, both Ingresses are plain HTTP with no TLS annotation.

kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.14.4/cert-manager.yaml

Verify:

kubectl get crd certificates.cert-manager.io

Provision a ClusterIssuer (e.g. Let's Encrypt) at the cluster level and reference its name in spec.ingress.clusterIssuer — see the KeycloakInstance Guide.

4. Prometheus Operator (optional, for metrics)

Required only when a KeycloakInstance sets spec.metrics.serviceMonitor: true.

kubectl get crd servicemonitors.monitoring.coreos.com

If absent, the operator still deploys the instance — it sets MetricsExporterReady=False/ServiceMonitorCRDMissing as a warning condition rather than failing.


Installing the operator

helm install bnerd-keycloak-operator charts/bnerd-keycloak-operator \
  --namespace bnerd-keycloak-system \
  --create-namespace \
  --set image.tag=0.1.0

The Helm chart installs:

  • The operator Deployment with leader election enabled
  • A ServiceAccount and ClusterRole/Role with the minimum RBAC permissions
  • Both CRDs (KeycloakInstance, KeycloakVersionMap) from charts/bnerd-keycloak-operator/crds/
  • The default KeycloakVersionMap (versionMap.installDefault: true, the chart default) — see Applying the default KeycloakVersionMap below

Verify the operator is running:

kubectl get pods -n bnerd-keycloak-system
# NAME                                       READY   STATUS    RESTARTS   AGE
# bnerd-keycloak-operator-7d9f6c8b4-xk2lp   1/1     Running   0          30s
make deploy IMG=git.bnerd.net/cloud/operators/bnerd-keycloak-operator:0.1.0

This applies the manifests in config/manager/ using kustomize and sets the operator image. CRDs from config/crd/bases/ are applied first with make install.

Useful for development or when you want to run directly from a repository checkout without packaging a Helm chart. With kustomize the default KeycloakVersionMap is not installed automatically — apply it explicitly (next section).


Applying the default KeycloakVersionMap

The KeycloakVersionMap named default is required for version resolution — without it, a KeycloakInstance stays Pending with reason VersionResolutionPending (this is transient, not a failure: the map may simply not be applied yet).

Installed automatically as part of the chart (versionMap.installDefault: true, the default). No extra step needed. Verify it's there:

kubectl get kcvm default

Set versionMap.installDefault: false if you manage your own KeycloakVersionMap/default out-of-band, then apply your own copy.

Apply it once per cluster after installing the operator:

kubectl apply -f examples/versionmap-default.yaml

See Upgrades & Version Management for the ladder this default map covers.


Verifying the installation

# Operator pod
kubectl get pods -n bnerd-keycloak-system

# CRDs registered
kubectl get crd | grep k8s.bnerd.com

# Default version map
kubectl get kcvm default

# Operator logs (no errors expected)
kubectl logs -n bnerd-keycloak-system deploy/bnerd-keycloak-operator --tail=50

RBAC summary

The operator requires cluster-level read access to CRDs (for capability gating), namespace-scoped create/update/delete access for Secrets, Services, StatefulSets, Ingresses, PodDisruptionBudgets, PerconaPGCluster, and ServiceMonitor resources, and patch/create on Events. The full role is in config/rbac/role.yaml.

The operator runs with a non-root UID (65532), a read-only root filesystem, and allowPrivilegeEscalation: false by default.