Subscribe to our news letter to get the latest on Google Cloud Platform and more!
Best Practices for Cost Optimization in Google Cloud: A GCP Guide 2025
As organizations increasingly migrate to cloud platforms, understanding the intricacies of cost…
As organizations increasingly migrate to cloud platforms, understanding the intricacies of cost…
In the evolving landscape of cloud computing, organizations are increasingly turning to…
Introduction: The Journey from Keys to Federation In the early days of…
Subscribe to our news letter to get the latest on Google Cloud Platform and more!
n today’s digital landscape, where applications are increasingly exposed to the vastness of the internet, the importance of secure and efficient user authentication and authorization cannot be overstated. With the surge in data breaches and identity theft, organizations are seeking solutions that not only protect sensitive information but also enhance user experience. This is where Keycloak on Google Cloud Platform (GCP) shines, offering a robust open-source identity and access management solution tailored for modern applications.
Keycloak acts as a gatekeeper, managing user identities, handling authentication protocols, and ensuring that only authorized users gain access to your resources. Whether you’re building a simple web application or a complex microservices architecture, Keycloak provides the tools necessary to streamline user management and bolster security. By deploying Keycloak in GCP, particularly in Google Kubernetes Engine (GKE), you can harness the power of cloud scalability and resilience while benefiting from GCP’s robust infrastructure.
In this post, I will take you on a journey through the process of setting up a production-ready Keycloak server on Kubernetes within GCP. This setup will not only allow you to leverage the scalability and resilience of GKE but will also include SSL certificates to secure communications between users and the Keycloak server, ensuring that sensitive data remains protected in transit.
Imagine launching your application with the confidence that user credentials are handled securely and seamlessly, powered by GCP’s high-performance capabilities. Picture your users enjoying a smooth login experience, effortlessly navigating through single sign-on features while you maintain control over their identities. This is not just a dream; it’s a reality that can be achieved with Keycloak and GCP.
As we dive into the setup process, I will share insights and best practices based on my experience deploying Keycloak in a production environment on GCP. From selecting the right infrastructure on Google Cloud Platform to configuring networking and scaling for high availability, each step will be guided by real-world scenarios and challenges.
So, grab your favorite beverage, and let’s embark on this exciting journey to transform your user authentication strategy with Keycloak on GCP. By the end of this post, you’ll be equipped with the knowledge and tools to implement a secure, efficient, and scalable identity management solution that meets the demands of today’s applications.
Keycloak is a powerful open-source identity and access management solution designed to secure applications and services with little to no coding. It offers features such as single sign-on (SSO), user federation, identity brokering, and social login, making it an excellent choice for modern applications.
Before deploying the Keycloak server, ensure you have the following:
To ensure a scalable and reliable deployment of Keycloak in GCP, I designed an architecture that aligns with modern best practices and leverages the full potential of Google Cloud Platform’s infrastructure. Central to this design is the use of Kustomize for managing deployments across multiple environments. Kustomize enables a declarative configuration approach, allowing tailored configurations for development, staging, and production without duplicating code. This flexibility ensures that updates can be consistently and efficiently propagated across all environments, simplifying maintenance and improving version control.
For the deployment strategy, I chose StatefulSets over traditional Deployments, which is particularly advantageous for Keycloak in GCP. StatefulSets provide stable, unique network identifiers and persistent storage, essential for Keycloak’s operations that involve managing user sessions and retaining consistent data. This approach ensures that each instance of Keycloak maintains its identity and access to persistent data even across restarts, a critical factor for session management and enhancing user experience.
To provide seamless access to the Keycloak server in GCP, I implemented a LoadBalancer service. This choice streamlines network configuration by abstracting its complexity and exposing Keycloak to external clients effectively. The LoadBalancer also supports scalability; as user traffic increases, I can easily scale the StatefulSet replicas to maintain high availability and distribute the load evenly. This setup enhances the performance and resilience of the authentication service, ensuring that user requests are managed efficiently.
This architecture emphasizes both security and scalability, preparing the deployment for future growth and adaptability within GCP. By using Kustomize for consistent configuration management, StatefulSets for data stability, and LoadBalancer services for high availability, I have constructed a solid foundation for a production-ready Keycloak server in GCP on Kubernetes. This thoughtful design enables me to focus on delivering exceptional user experiences while maintaining robust identity and access management.
With our architectural framework established, it’s time to explore the code that powers our Keycloak deployment in GCP. Think of this code as our navigational chart through the Kubernetes landscape, guiding us through the intricacies of deployment and configuration within Google Cloud Platform.
We’ll start by looking at the Kustomize configurations, which serve as blueprints for tailoring deployments to meet the unique requirements of each environment. These configurations highlight how we can create a flexible and cohesive strategy that ensures consistent deployment practices across development, staging, and production within GCP.
Next, we’ll dive into the StatefulSet definitions. Here, the code showcases the mechanisms that guarantee our Keycloak in GCP instances remain resilient and reliable. The StatefulSet ensures that each Keycloak instance maintains state and identity, which is crucial for user session management and providing a seamless authentication experience.
Finally, we’ll examine the service configurations that expose Keycloak in GCP to the outside world. By configuring a LoadBalancer service, we make sure that our authentication service is accessible, scalable, and secure. This setup is vital for managing user traffic effectively and enhancing the overall availability and performance of Keycloak.
Let’s turn the page and dive into the code, where our architectural vision for Keycloak in GCP comes to life!
Let’s take a moment to explore how we manage our Kubernetes manifests with Kustomize. The directory structure for our Kustomize setup is organized into two main folders: k8s/base/ and k8s/overlays/.
By utilizing Kustomize, we streamline our deployment process, enabling us to maintain clean and organized configurations. Now that we have a solid understanding of our Kustomize setup, let’s dive into the specifics of the StatefulSet configuration.
A StatefulSet is a Kubernetes resource that manages the deployment and scaling of a set of Pods, providing guarantees about the ordering and uniqueness of these Pods. It is particularly useful for applications that require persistent storage and stable network identities, such as databases or other stateful applications.
The Keycloak server is deployed using a StatefulSet on Kubernetes, ensuring stable network identities and persistent storage. This approach not only guarantees that our Keycloak instances maintain their state across restarts but also provides the flexibility to scale as needed. Moreover, our setup employs TLS for secure communications, adding an essential layer of security to protect sensitive user data.
Here’s a glimpse of the YAML configuration used for deploying Keycloak:
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: keycloak
namespace: default
labels:
app: keycloak
spec:
replicas: 1
selector:
matchLabels:
app: keycloak
serviceName: "keycloak-service"
template:
metadata:
labels:
app: keycloak
spec:
serviceAccountName: keycloak-sa
securityContext:
fsGroup: 1000
containers:
- name: keycloak
image: quay.io/keycloak/keycloak:latest
args: ["start","--cache-stack=kubernetes", "--spi-login-protocol-openid-connect-legacy-logout-redirect-uri=true"]
ports:
- containerPort: 8080
name: http
- containerPort: 8443
name: https
- name: jgroups
containerPort: 7600
securityContext:
runAsUser: 1000
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
volumeMounts:
- name: keycloak-tls-volume
mountPath: /etc/x509/https
readOnly: true
env:
- name: KC_BOOTSTRAP_ADMIN_USERNAME
valueFrom:
secretKeyRef:
name: keycloak-secrets
key: KC_BOOTSTRAP_ADMIN_USERNAME
- name: KC_BOOTSTRAP_ADMIN_PASSWORD
valueFrom:
secretKeyRef:
name: keycloak-secrets
key: KC_BOOTSTRAP_ADMIN_PASSWORD
- name: KC_DB
valueFrom:
configMapKeyRef:
name: keycloak-configmap
key: KC_DB
- name: KC_DB_URL
valueFrom:
configMapKeyRef:
name: keycloak-configmap
key: KC_DB_URL
- name: KC_HOSTNAME
valueFrom:
configMapKeyRef:
name: keycloak-configmap
key: KC_HOSTNAME
- name: KC_HEALTH_ENABLED
valueFrom:
configMapKeyRef:
name: keycloak-configmap
key: KC_HEALTH_ENABLED
- name: KC_DB_USERNAME
valueFrom:
secretKeyRef:
name: keycloak-secrets
key: KC_DB_USERNAME
- name: KC_DB_PASSWORD
valueFrom:
secretKeyRef:
name: keycloak-secrets
key: KC_DB_PASSWORD
- name: KC_HTTPS_CERTIFICATE_FILE
valueFrom:
secretKeyRef:
name: keycloak-secrets
key: KC_HTTPS_CERTIFICATE_FILE
- name: KC_HTTPS_CERTIFICATE_KEY_FILE
valueFrom:
secretKeyRef:
name: keycloak-secrets
key: KC_HTTPS_CERTIFICATE_KEY_FILE
- name: KC_PROXY
valueFrom:
configMapKeyRef:
name: keycloak-configmap
key: KC_PROXY
- name: jgroups.dns.query
valueFrom:
configMapKeyRef:
name: keycloak-configmap
key: JGROUPS_DNS_QUERY
- name: PROXY_ADDRESS_FORWARDING
valueFrom:
configMapKeyRef:
name: keycloak-configmap
key: PROXY_ADDRESS_FORWARDING
- name: KC_METRICS_ENABLED
valueFrom:
configMapKeyRef:
name: keycloak-configmap
key: KC_METRICS_ENABLED
- name: KC_HTTP_ENABLED
valueFrom:
configMapKeyRef:
name: keycloak-configmap
key: KC_HTTP_ENABLED
- name: KC_HTTP_RELATIVE_PATH
valueFrom:
configMapKeyRef:
name: keycloak-configmap
key: KC_HTTP_RELATIVE_PATH
- name: KC_HOSTNAME_URL
valueFrom:
configMapKeyRef:
name: keycloak-configmap
key: KC_HOSTNAME_URL
- name: KC_HOSTNAME_ADMIN_URL
valueFrom:
configMapKeyRef:
name: keycloak-configmap
key: KC_HOSTNAME_URL
- name: JAVA_OPTS_APPEND
valueFrom:
configMapKeyRef:
name: keycloak-configmap
key: JAVA_OPTS_APPEND
- name: KC_LOG_LEVEL
valueFrom:
configMapKeyRef:
name: keycloak-configmap
key: KC_LOG_LEVEL
resources:
requests:
memory: "2Gi"
cpu: "1000m"
limits:
memory: "4Gi"
cpu: "2000m"
readinessProbe:
httpGet:
path: /auth/health/ready
port: 9000
scheme: HTTPS
initialDelaySeconds: 60
periodSeconds: 10
livenessProbe:
httpGet:
path: /auth/health/live
port: 9000
scheme: HTTPS
initialDelaySeconds: 60
periodSeconds: 30
volumes:
- name: keycloak-tls-volume
secret:
secretName: keycloak-tls
tolerations:
- key: "keycloak"
operator: "Exists"
effect: "NoSchedule"
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: keycloak
namespace: default
labels:
app: keycloak
apiVersion: Specifies the version of the Kubernetes API that you are using to create this object. Here, apps/v1indicates that this is an application-related object.
kind: This denotes the type of Kubernetes object. In this case, it is a StatefulSet, which is used to manage stateful applications.
metadata: Contains data that helps uniquely identify the object, including:
spec:
replicas: 1
selector:
matchLabels:
app: keycloak
serviceName: "keycloak-service"
spec: This section defines the desired state of the StatefulSet.
replicas: Specifies the number of pod replicas to run. Here, it is set to 1, meaning only one instance of Keycloak will be deployed.
selector: This field is crucial for StatefulSet as it identifies the pods managed by the StatefulSet. The matchLabelsshould match the labels specified in the pod template.
serviceName: Specifies the name of the service that will be used to manage network routing for the StatefulSet pods.
template:
metadata:
labels:
app: keycloak
spec:
serviceAccountName: keycloak-sa
securityContext:
fsGroup: 1000
template: This defines the pod template used by the StatefulSet.
metadata: Contains labels for the pods created from this template. These labels should match the selector defined earlier.
spec: Details the pod specification, including:
containers:
- name: keycloak
image: quay.io/keycloak/keycloak:latest
args: ["start", "--cache-stack=kubernetes", "--spi-login-protocol-openid-connect-legacy-logout-redirect-uri=true"]
containers: Defines the containers that will be created in the pod.
name: The name of the container (here, it is keycloak).
image: The container image to use, in this case, the latest Keycloak image from the Quay.io registry.
args: Specifies the command-line arguments to pass to the container at startup. The provided arguments configure the Keycloak server, enabling Kubernetes cache and specifying settings related to OpenID Connect.
ports:
- containerPort: 8080
name: http
- containerPort: 8443
name: https
- name: jgroups
containerPort: 7600
ports: Lists the ports that the container will expose.
securityContext:
runAsUser: 1000
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
securityContext: Defines security settings for the container:
volumeMounts:
- name: keycloak-tls-volume
mountPath: /etc/x509/https
readOnly: true
volumeMounts: Defines volumes that will be mounted into the container.
env:
- name: KC_BOOTSTRAP_ADMIN_USERNAME
valueFrom:
secretKeyRef:
name: keycloak-secrets
key: KC_BOOTSTRAP_ADMIN_USERNAME
env: Defines environment variables to be used in the container.
resources:
requests:
memory: "2Gi"
cpu: "1000m"
limits:
memory: "4Gi"
cpu: "2000m"
resources: Specifies resource requests and limits for the container.
readinessProbe:
httpGet:
path: /auth/health/ready
port: 9000
scheme: HTTPS
initialDelaySeconds: 60
periodSeconds: 10
readinessProbe: Configures a probe to check if the application is ready to handle traffic.
livenessProbe:
httpGet:
path: /auth/health/live
port: 9000
scheme: HTTPS
initialDelaySeconds: 60
periodSeconds: 30
livenessProbe: Configures a probe to check if the application is still alive.
volumes:
- name: keycloak-tls-volume
secret:
secretName: keycloak-tls
volumes: Defines the volumes that can be used by the pods.
tolerations:
- key: "keycloak"
operator: "Exists"
effect: "NoSchedule"
tolerations: Allows the pods to be scheduled onto nodes with specific taints.
In Kubernetes, a Service is an abstraction that defines a logical set of Pods and a policy to access them. Services enable communication between different parts of your application, whether they are internal components (like microservices) or external users (clients accessing your application). Here’s a breakdown of what a Service is and why it’s essential in Kubernetes:
apiVersion: v1
kind: Service
metadata:
name: keycloak-service
labels:
app: keycloak
spec:
ports:
- name: http
port: 8080
targetPort: 8080
- name: https
port: 8443
targetPort: 8443
- name: jgroup
port: 7600
targetPort: 7600
selector:
app: keycloak
type: LoadBalancer
apiVersion: v1
kind: Service
metadata:
name: keycloak-service
namespace: default
labels:
app: keycloak
apiVersion: Specifies the version of the Kubernetes API used for this configuration. Here it is v1, indicating a stable API version.
kind: This indicates that the resource being created is a Service, which is essential for networking in Kubernetes.
metadata: Contains metadata about the Service, including:
spec:
ports:
- name: http
port: 8080
targetPort: 8080
- name: https
port: 8443
targetPort: 8443
- name: jgroup
port: 7600
targetPort: 7600
spec: This section defines the desired state of the Service.
ports: Defines the ports that the Service will expose. Each port specification includes:
selector:
app: keycloak
selector: This field specifies the criteria used to determine which pods the Service targets. In this case, it matches pods labeled with app: keycloak. This ensures that traffic sent to keycloak-service is routed to the appropriate Keycloak pods.
type: LoadBalancer
type: This field defines how the Service is exposed. By setting the type to LoadBalancer, Kubernetes provisions an external load balancer (typically in a cloud environment) that distributes incoming traffic across the Keycloak pods. This enables external clients to access the Keycloak server using a public IP address assigned by the cloud provider, facilitating seamless integration with other services or applications that require authentication.
To bring your Keycloak server to life, the first step is to clone the GitHub repository containing the configuration files and deployment scripts. This repository serves as a foundation for your setup, allowing you to customize and extend the configuration as needed.
First, clone the repository containing the Keycloak setup:
git clone https://github.com/DataArize/keycloak-identity-management.git
cd keycloak-identity-management
Keycloak requires several secrets for secure operation. These secrets should not be stored in version control for security reasons. Below are the secrets that need to be created:
Use the following command to generate the secrets:
kubectl create secret generic keycloak-secrets \
--namespace default \
--from-literal=KC_BOOTSTRAP_ADMIN_USERNAME=<base64-encoded-admin-username> \
--from-literal=KC_BOOTSTRAP_ADMIN_PASSWORD=<base64-encoded-admin-password> \
--from-literal=KC_DB_USERNAME=<base64-encoded-db-username> \
--from-literal=KC_DB_PASSWORD=<base64-encoded-db-password> \
--from-literal=KC_HTTPS_CERTIFICATE_FILE=<base64-encoded-certificate-file-path> \
--from-literal=KC_HTTPS_CERTIFICATE_KEY_FILE=<base64-encoded-certificate-key-file-path>
Additionally, create a TLS secret for the SSL certificates:
kubectl create secret tls keycloak-tls \
--cert=path/to/certificate.crt \
--key=path/to/private.key
Ensure that the database for Keycloak is set up and accessible. Update the KC_DB_URL in the keycloak-configmapaccordingly.
Deploy the Keycloak setup using Kustomize:
kubectl apply -k k8s/overlay/development
This command will apply the configurations defined in the kustomization.yaml file located in the k8s/overlay/development directory.
This setup requires SSL certificates for secure communication. You will need to generate these certificates and create secrets in Kubernetes as outlined in the “Create Required Secrets” section above. Consider using tools like Let’s Encrypt for generating certificates.
Once deployed, you can access the Keycloak admin console at:
https://<your-domain>:8443/auth/admin
Use the admin credentials you specified in the secrets to log in.

You can find the complete configuration and deployment scripts for this Keycloak setup in my GitHub repository:
Keycloak Identity Management Repository
Feel free to clone the repository, explore the code, and adapt it for your own needs!
Setting up a production-ready Keycloak in GCP on Kubernetes is a pivotal step toward ensuring secure user authentication and authorization for your applications. Throughout this guide, we’ve explored the architecture design, Kustomize configurations, and the YAML breakdown that collectively contribute to a robust deployment. By leveraging StatefulSets and LoadBalancer services, we’ve ensured high availability and resilience for our Keycloak instance.
With the hands-on steps provided, you now have a solid foundation to deploy Keycloak seamlessly. As you implement this solution, consider the scalability and security features that Keycloak offers, allowing you to adapt to your growing needs. Embrace the journey of enhancing your application’s security and user experience, and feel free to revisit this guide as you refine your deployment process.
Happy deploying!