Deploying Keycloak on GKE with a custom Helm Chart
In this guide, you'll learn how to deploy Keycloak on Google Kubernetes Engine (GKE) using:
- A Chart.yaml that specifies dependencies (Bitnami Keycloak),
- values.yaml for configuration details,
- ingress.yaml to define how traffic enters your cluster,
- managed-cert.yaml for an SSL certificate managed by GKE,
- keycloak-argo.yaml for automated GitOps via Argo CD (optional).
1. Prerequisites
-
GKE cluster
- You'll need a functional GKE cluster. Ensure
kubectlis pointing to it:
kubectl config get-contexts - You'll need a functional GKE cluster. Ensure
-
Helm (v3+)
- Verify Helm is installed:
helm version -
Bastion host (if needed)
- If your cluster is private, confirm you can SSH into a bastion host with internal cluster access.
-
Argo CD (optional)
- If you wish to automate deployments, have Argo CD installed in the cluster.
-
Reserved static IP (optional)
- Consider reserving a global static IP in GCP for a consistent external IP.
2. Chart structure
Below is a common structure for your custom Keycloak chart. Put these files in a directory (e.g., keycloak/) in your Git repository:
keycloak/
├── Chart.yaml
├── values.yaml
├── templates/
│ ├── ingress.yaml
│ ├── managed-cert.yaml
│ └── _other_templates_.yaml
├── keycloak-argo.yaml
└── README.md (optional)
You can place keycloak-argo.yaml either in the root of your repo or alongside your chart files.
3. Chart.yaml
Purpose: Defines your Helm chart's name, version, description, and dependencies.
View Chart.yaml configuration
apiVersion: v2
name: keycloak
description: A wrapper chart for Bitnami Keycloak
version: 0.1.0
appVersion: "24.4.10"
dependencies:
- name: keycloak
version: "24.4.10"
repository: "https://charts.bitnami.com/bitnami"
Key points:
apiVersion: v2is required for Helm 3.dependencies: References the Bitnami Keycloak chart and its version.name,version, andappVersioncan be customized to fit your needs.
When you run helm dependency update in this chart's directory, Helm will fetch the Bitnami Keycloak chart from the specified repository.
4. values.yaml
Purpose: Holds the main configuration for Keycloak, its database, and resources. These values override or supplement the Bitnami chart's defaults.
View values.yaml configuration
keycloak:
# Enable production mode
production: true
# TLS Configuration
tls:
enabled: true
autoGenerated: true
# Configure proxy settings for edge termination
proxy: edge
httpRelativePath: /
# Pod configuration
replicaCount: 1
resources:
requests:
memory: 256Mi
cpu: 250m
limits:
memory: 512Mi
cpu: 500m
persistence:
enabled: true
storageClass: "standard"
accessModes:
- ReadWriteOnce
size: 8Gi
# Configure authentication
auth:
adminUser: admin
adminPassword: "mySecureAdminPassword"
# Extra environment variables
extraEnvVars:
- name: KC_HOSTNAME
value: "keycloak.mydomain.com"
- name: KC_HOSTNAME_STRICT
value: "true"
- name: KC_HOSTNAME_STRICT_HTTPS
value: "true"
- name: KC_PROXY
value: "edge"
- name: KEYCLOAK_LOG_LEVEL
value: DEBUG
# Ingress configuration (used by our custom template)
ingress:
enabled: false # Disabled because we use a custom Ingress template
hostname: keycloak.mydomain.com
postgresql:
enabled: true
auth:
postgresPassword: "postgrespassword"
username: keycloak
password: "keycloakpassword"
database: keycloak_db
persistence:
enabled: true
storageClass: "standard"
accessModes:
- ReadWriteOnce
size: 8Gi
Key points:
production: true: Enables stricter security settings.proxy: edge: Configures Keycloak for edge-terminated SSL.auth.adminPassword: Must be changed before production use.postgresql.enabled: true: Deploys an internal PostgreSQL instance with persistent storage.ingress.enabled: false: Because we define an Ingress resource in a separate template file (ingress.yaml), we disable the default Bitnami Keycloak ingress.
5. ingress.yaml
In a typical Helm chart, you'd place this file under templates/ingress.yaml.
View ingress.yaml configuration
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ .Release.Name }}-ingress
namespace: {{ .Release.Namespace }}
annotations:
kubernetes.io/ingress.class: "gce"
kubernetes.io/ingress.global-static-ip-name: "keycloak-global-ip"
networking.gke.io/managed-certificates: {{ .Release.Name }}-cert
# Temporarily allow HTTP while certificate is provisioning
kubernetes.io/ingress.allow-http: "true"
spec:
ingressClassName: gce
rules:
- host: {{ .Values.keycloak.ingress.hostname }}
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: {{ .Release.Name }}
port:
number: 80
Key points:
kubernetes.io/ingress.class: "gce": Uses Google Cloud Load Balancing.kubernetes.io/ingress.global-static-ip-name: Assign your static IP name here (if you reserved one).allow-http: "true": Lets HTTP traffic pass while the SSL certificate is pending. You can remove or change it later.
6. managed-cert.yaml
Also stored in templates/ (e.g., templates/managed-cert.yaml). This resource requests and manages an SSL certificate for your domain.
View managed-cert.yaml configuration
apiVersion: networking.gke.io/v1
kind: ManagedCertificate
metadata:
name: {{ .Release.Name }}-cert
namespace: {{ .Release.Namespace }}
spec:
domains:
- {{ .Values.keycloak.ingress.hostname }}
Key points:
domains: Should match the domain in the Ingress host.- It may take up to 30 minutes for GCP to issue and activate the certificate.
7. keycloak-argo.yaml (Optional)
If you want a GitOps approach with Argo CD, you can define an Argo CD Application pointing to this Helm chart. You can place it at your repo's root or in the keycloak/ folder.
View keycloak-argo.yaml configuration
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: keycloak
namespace: argocd
spec:
project: default
source:
repoURL: "[email protected]:my-org/keycloak.git"
path: "."
targetRevision: HEAD
destination:
server: "https://kubernetes.default.svc"
namespace: keycloak
syncPolicy:
automated:
prune: true
selfHeal: true
Key points:
repoURL: Set to your repository.path: If your chart is in a subdirectory (e.g.,keycloak/), adjust accordingly.pruneandselfHeal: Ensures your cluster stays in sync with Git at all times.
8. Deployment steps
8.1 (optional) Prepare dependencies
If you have defined dependencies in Chart.yaml (like Bitnami Keycloak), run:
helm dependency update path/to/keycloak/
This downloads the Bitnami Keycloak chart locally so Helm can merge it with your wrapper.
8.2 Using Helm CLI
If you're deploying manually:
helm upgrade --install keycloak path/to/keycloak/ \
--namespace keycloak \
--create-namespace
Helm will render Chart.yaml, values.yaml, and everything under templates/.
8.3 Using Argo CD
- Push changes to Git
Commit and push your chart files (Chart.yaml,values.yaml,templates/, etc.) along withkeycloak-argo.yaml. - Argo CD sync
- Forward Argo CD if needed:
kubectl port-forward svc/argocd-server -n argocd 8081:443 - Go to
https://localhost:8081and look for thekeycloakapplication. Argo CD will either auto-sync or you can press "Sync" to deploy.
- Forward Argo CD if needed:
9. Post-deployment checks
-
Pods:
kubectl get pods -n keycloak- Look for Keycloak and PostgreSQL pods in a "Running" state.
-
Ingress:
kubectl get ingress -n keycloak- An external IP should appear once the LB is set up.
-
Managed Certificate:
kubectl describe managedcertificate -n keycloak- Wait for "Active" before HTTPS is fully functional.
-
Browser test:
- Visit
https://keycloak.mydomain.com(or whichever domain you set). - You should see Keycloak's login screen.
- Visit
10. Troubleshooting tips
- Certificate provisioning
- It can take up to 30 minutes. Ensure your DNS record points to the external IP.
- Pod crashes or failures
kubectl logs -n keycloak -l app.kubernetes.io/name=keycloak- Check for issues like incorrect database credentials or insufficient memory.
- Argo CD errors
- Inspect the "Events" or "Application" details in the Argo CD UI.
- Make sure your
repoURL,path, andChart.yamlreferences are correct.
- Ingress not resolving
- Confirm your domain DNS points to the Ingress external IP.
- If using a static IP, ensure the name matches
kubernetes.io/ingress.global-static-ip-name.
11. Security best practices
- Use secure passwords
- Update
keycloak.auth.adminPasswordand the database credentials before production.
- Update
- Production mode
- Leave
production: trueto enforce stricter security settings.
- Leave
- TLS
- Use GKE-managed certificates externally and auto-generated self-signed certs internally to ensure end-to-end encryption.
- Argo CD prune & self-heal
- Test changes in a staging environment first; these features can remove resources or revert manual changes unexpectedly if you're not prepared.