DEV Community

Lars van der Niet
Lars van der Niet

Posted on • Originally published at larsniet.com

Deploying Keycloak on GKE with a custom Helm Chart

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

  1. GKE cluster
  • You'll need a functional GKE cluster. Ensure kubectl is pointing to it:
   kubectl config get-contexts
Enter fullscreen mode Exit fullscreen mode
  1. Helm (v3+)
  • Verify Helm is installed:
   helm version
Enter fullscreen mode Exit fullscreen mode
  1. Bastion host (if needed)
  • If your cluster is private, confirm you can SSH into a bastion host with internal cluster access.
  1. Argo CD (optional)
  • If you wish to automate deployments, have Argo CD installed in the cluster.
  1. 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)
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode

Key points:

  • apiVersion: v2 is required for Helm 3.
  • dependencies: References the Bitnami Keycloak chart and its version.
  • name, version, and appVersion can 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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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 }}
Enter fullscreen mode Exit fullscreen mode

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: "git@github.com:my-org/keycloak.git"
    path: "."
    targetRevision: HEAD
  destination:
    server: "https://kubernetes.default.svc"
    namespace: keycloak
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
Enter fullscreen mode Exit fullscreen mode

Key points:

  • repoURL: Set to your repository.
  • path: If your chart is in a subdirectory (e.g., keycloak/), adjust accordingly.
  • prune and selfHeal: 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/
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Helm will render Chart.yaml, values.yaml, and everything under templates/.

8.3 Using Argo CD

  1. Push changes to Git Commit and push your chart files (Chart.yaml, values.yaml, templates/, etc.) along with keycloak-argo.yaml.
  2. Argo CD sync

    • Forward Argo CD if needed:
     kubectl port-forward svc/argocd-server -n argocd 8081:443
    
  • Go to https://localhost:8081 and look for the keycloak application. Argo CD will either auto-sync or you can press "Sync" to deploy.

9. Post-deployment checks

  1. Pods:
   kubectl get pods -n keycloak
Enter fullscreen mode Exit fullscreen mode
  • Look for Keycloak and PostgreSQL pods in a "Running" state.
  1. Ingress:
   kubectl get ingress -n keycloak
Enter fullscreen mode Exit fullscreen mode
  • An external IP should appear once the LB is set up.
  1. Managed Certificate:
   kubectl describe managedcertificate -n keycloak
Enter fullscreen mode Exit fullscreen mode
  • Wait for "Active" before HTTPS is fully functional.
  1. Browser test:
    • Visit https://keycloak.mydomain.com (or whichever domain you set).
    • You should see Keycloak's login screen.

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
Enter fullscreen mode Exit fullscreen mode
  • 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, and Chart.yaml references 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

  1. Use secure passwords
    • Update keycloak.auth.adminPassword and the database credentials before production.
  2. Production mode
    • Leave production: true to enforce stricter security settings.
  3. TLS
    • Use GKE-managed certificates externally and auto-generated self-signed certs internally to ensure end-to-end encryption.
  4. 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.

Top comments (0)