Deploying Keycloak
Responsibility Disclaimer:
This page is an example; you are responsible for what you deploy and maintain. Expand to read more.
This tutorial is validated by Scalingo and is provided as trusted guidance for the documented use case.
It shows one possible way to use Scalingo, but it is not part of the Scalingo product or managed service scope. Scalingo provides limited support for the Scalingo-controlled scope only.
This tutorial may reference software, repositories, dependencies or configuration choices that are not fully maintained or supported by Scalingo, customers remain responsible for:
- Validating the code and configuration they deploy against applicable security, compliance and operational requirements before any production use.
- Maintaining the applications and components they control during runtime, including monitoring releases, deploying new versions, monitoring application health and scaling as needed.
This notice does not modify or limit the applicable Agreement or Scalingo’s contractual commitments.
Keycloak is an open-source identity and access management solution designed to secure modern applications and services. It provides features such as single sign-on (SSO), user federation, and social login integration. It supports standard protocols like OAuth 2.0, OpenID Connect, and SAML. It also offers centralized user management, role-based access control, and fine-grained permissions. Overall, Keycloak helps organizations improve security while simplifying the work of developers with authentication and authorization.
Planning your Deployment
-
Even if Keycloak provides quite precise recommendations in terms of CPU, RAM and databases, sizing still mostly depends on the foreseen usage and expected performances.
-
Keycloak requires a rough minimum of 1.5GB of RAM to run, and quite a lot of CPU (Keycloak spends a lot of time hashing, opening TLS connections, etc.). Consequently, we advise provisioning at least one XL container, and possibly change for a more powerful plan later.
-
Keycloak requires its own database. Considering the key role Keycloak is generally playing, we advise to always deploy with at least a Business service class, mainly to benefit from the higher SLA and redundancy. In this tutorial, we deploy a Scalingo for PostgreSQL® Business 1G.
-
Keycloak is designed for multi-node clustered setups. In production mode, it uses a distributed cache (implemented via Infinispan) to share some resources between nodes. To be able to benefit from this cache, which is highly recommended, we deploy multiple Keycloak containers in a Private Network:
-
Infinispan uses several TCP ports to run. Running inside a Private Network gives us the freedom to bind any port we want.
-
Infinispan also features some auto-discovery mechanism to automatically update the list of available nodes in the cluster. By relying on it, we can ensure the cache is always well distributed between the available nodes.
-
To do so, we have to make sure Keycloak listens on the Private Network IP addresses for everything related to the Infinispan distributed cache.
-
-
For greater control and security, we suggest to deploy behind a reverse proxy:
- It prevents Keycloak from being directly exposed to the Internet.
- It allows to set up features such as rate-limiting and IP allow/deny lists.
- It allows to scale the reverse proxy so that it can handle traffic peaks or sudden load.
-
To do so, we deploy two applications, grouped in the same Project: one hosting Keycloak and the second one hosting the reverse proxy.
-
Keycloak provides an exhaustive list of configuration options, recommendations to run in production, and documentation for administrating Keycloak.
We strongly suggest to read these pages before entering prodution.
Creating the Project
Using the Command Line
- From the command line, create a new Project to host the
applications:
scalingo projects-add keycloak - Retrieve the project ID:
scalingo projectsThe output should look like this:
/!\ This command only displays projects where you are the owner ┌──────────┬─────────┬──────────────────────────────────────────┬─────────────────┐ │ NAME │ DEFAULT │ ID │ PRIVATE NETWORK │ ├──────────┼─────────┼──────────────────────────────────────────┼─────────────────┤ │ keycloak │ false │ prj-32232e93-8c8a-4898-8a68-d10ff1e63f7a │ true │ - Identify the project you just created and keep its ID aside.
Using the Terraform Provider
- Place the following
scalingo_projectresource block in your Terraform file:resource "scalingo_project" "keycloak-prj" { name = "keycloak" default = false }
Deploying Keycloak
Using the Command Line
-
Fork our Keycloak repository
- Create a new application in the project:
scalingo create my-keycloak --project-id <project_id>With
project_idbeing the ID of the newly created project. - Provision a Scalingo for PostgreSQL® Business 1G database:
scalingo --app my-keycloak addons-add postgresql postgresql-business-1024 - Let the platform know what buildpack it must use:
scalingo --app my-keycloak env-set BUILDPACK_URL=https://github.com/Scalingo/keycloak-buildpack -
Create a few mandatory environment variables:
- These make sure Keycloak runs properly on Scalingo:
scalingo --app my-keycloak env-set KC_PROXY_HEADERS=xforwarded scalingo --app my-keycloak env-set KC_HTTP_ENABLED=true scalingo --app my-keycloak env-set KC_HTTP_PORT=8080 scalingo --app my-keycloak env-set KC_HOSTNAME=<hostname>With
hostnamebeing the publicly exposed address at which Keycloak is available
(e.g.my-keycloak.osc-fr1.scalingo.io).Using port 8080 is an example, you can choose any port number.
- This one restricts the cache communications to the Private Network only:
scalingo --app my-keycloak env-set KC_CACHE_EMBEDDED_NETWORK_BIND_ADDRESS="match-address:10.240.\*" - These configure the cache:
scalingo --app my-keycloak env-set KC_CACHE_STACK="jdbc-ping" scalingo --app my-keycloak env-set KC_CACHE_CONFIG_MUTATE="true" - These are used to create the initial credentials for the administrator
user (remember to use a strong password):
scalingo --app my-keycloak env-set KC_BOOTSTRAP_ADMIN_USERNAME=<admin_username> scalingo --app my-keycloak env-set KC_BOOTSTRAP_ADMIN_PASSWORD=<admin_password>
- These make sure Keycloak runs properly on Scalingo:
-
Create a few more recommended environment variables:
- This one pins the version of Keycloak. This prevents against unintentional
updates:
scalingo --app my-keycloak env-set KEYCLOAK_VERSION="<version>"With
versionbeing the version number to deploy. - This one restricts the communication with the reverse proxy to the Private
Network only:
scalingo --app my-keycloak env-set KC_PROXY_TRUSTED_ADDRESSES="10.240.0.0/22" - This one allows to limit the number of queued requests, which is important
to protect the cluster against overload situations:
scalingo --app my-keycloak env-set KC_HTTP_MAX_QUEUED_REQUESTS=<number>With
numberbeing the number of requests Keycloak can keep in queue before dropping additional ones. Set this to a number matching your environment.
- This one pins the version of Keycloak. This prevents against unintentional
updates:
- Add a
Procfileto your git repository, with the following content:kc: /app/keycloak/bin/kc.sh start --optimizedThis instructs the platform to start Keycloak in a process type named
kc, which, unlikeweb, can not be publicly exposed. - (optional) Instruct the platform to run the
kcprocess type in three XL containers:scalingo --app my-keycloak scale kc:3:XL - Everything’s ready, deploy to Scalingo:
git push scalingo master
From this point, you should have a working Keycloak cluster running in its own Private Network on Scalingo.
Using the Terraform Provider
-
Fork our Keycloak repository
- Place the following
scalingo_appresource block in your Terraform file:resource "scalingo_app" "my-keycloak" { name = "my-keycloak" project_id = scalingo_project.keycloak-prj.id stack_id = "scalingo-26" force_https = true environment = { BUILDPACK_URL = "https://github.com/Scalingo/keycloak-buildpack", KEYCLOAK_VERSION = "<version>", KC_PROXY_HEADERS = "xforwarded", KC_HTTP_ENABLED = true, KC_HTTP_PORT = 8080, KC_HOSTNAME = "<hostname>", KC_CACHE_STACK = "jdbc-ping", KC_CACHE_CONFIG_MUTATE = true, KC_CACHE_EMBEDDED_NETWORK_BIND_ADDRESS = "match-address:10.240.\*", KC_PROXY_TRUSTED_ADDRESSES = "10.240.0.0/24", KC_HTTP_MAX_QUEUED_REQUESTS = <number>, KC_BOOTSTRAP_ADMIN_USERNAME = "<admin_username>", KC_BOOTSTRAP_ADMIN_PASSWORD = "<admin_password>" } } - Link the app to your forked repository:
data "scalingo_scm_integration" "github" { scm_type = "github" } resource "scalingo_scm_repo_link" "my-keycloak-repo" { auth_integration_uuid = data.scalingo_scm_integration.github.id app = scalingo_app.my-keycloak.id source = "https://github.com/<username>/keycloak-scalingo" branch = "master" } - Place the following
scalingo_addonresource block in your Terraform file to provision a Scalingo for PostgreSQL® Business 1G database and attach it to your app:resource "scalingo_addon" "my-keycloak-db" { app = scalingo_app.my-keycloak.id provider_id = "postgresql" plan = "postgresql-business-1024" } - Add a
Procfileto your git repository, with the following content:kc: /app/keycloak/bin/kc.sh start --optimizedThis instructs the platform to start Keycloak in a process type named
kc, which, unlikeweb, can not be publicly exposed. - (optional) Instruct the platform to run the
kcprocess type in three XL containers:resource "scalingo_container_type" "kc" { app = scalingo_app.my-keycloak.id name = "kc" size = "XL" amount = 3 } -
Run
terraform planand check if the result looks good -
If so, run
terraform apply - Once Terraform is done, your Keycloak instance is ready to be deployed:
- Head to your dashboard
- Click on your Keycloak application
- Click on the Deploy tab
- Click on Manual deployment in the left menu
- Click the Trigger deployment button
- After a few seconds, your Keycloak cluster is finally up and running!
Deploying the Reverse Proxy
Here is a very basic working example of nginx configuration that can be used as a starting point. It only proxies the strictly required endpoints, which has the advantage of drastically lowering the attack surface of Keycloak:
resolver 10.255.0.10 10.255.0.11 valid=3s ipv6=off;
resolver_timeout 2s;
upstream keycloak {
zone kc 64k;
ip_hash;
server <%= ENV["KEYCLOAK_PRIVATE_DOMAIN"] %>:8080 resolve max_fails=2;
}
server {
server_name localhost;
listen <%= ENV["PORT"] %>;
charset utf-8;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;
proxy_set_header X-Forwarded-Port $http_x_forwarded_port;
# Optional hardening:
proxy_hide_header X-Powered-By;
location ^~ /realms/ {
proxy_pass http://keycloak/realms/;
proxy_redirect default;
}
location ^~ /resources/ {
proxy_pass http://keycloak/resources/;
proxy_redirect default;
}
location ^~ /.well-known/ {
proxy_pass http://keycloak/.well-known/;
proxy_redirect default;
}
}
Using the Command Line
- Create a new application in the same Project:
scalingo create my-nginx --project-id <project_id> -
Follow our documentation to deploy nginx.
- Create an environment variable to store the
kcprocess type’s private domain name:scalingo --app my-nginx env-set KEYCLOAK_PRIVATE_DOMAIN=kc.<private_network_fqdn>With
<private_network_fqdn>being the private domain name of the application hosting the Keycloak cluster. -
Create a
servers.conf.erbfile for nginx, using the sample suggested above, or your own. - Deploy:
git push scalingo master
Using the Terraform Provider
-
Create a new git repository dedicated to nginx.
-
In this repository, create a
servers.conf.erbfile for nginx, using the sample suggested above, or your own. - Place the following
scalingo_private_network_domaindata block in your Terraform file:data "scalingo_private_network_domain" "pndn" { app = scalingo_app.my-nginx.name } - Place the following
scalingo_appresource block in your Terraform file:resource "scalingo_app" "my-nginx" { name = "my-nginx" project_id = scalingo_project.keycloak-prj.id stack_id = "scalingo-26" environment = { KEYCLOAK_PRIVATE_DOMAIN = "kc.${substr(data.scalingo_private_network_domain.pndn.domains[0], 0, -1)}" } } - Link the app to your repository:
resource "scalingo_scm_repo_link" "my-nginx-repo" { auth_integration_uuid = data.scalingo_scm_integration.github.id app = scalingo_app.my-nginx.id source = "https://github.com/<username>/my-nginx" branch = "master" } - (optional) Instruct the platform to run the
webprocess type in a single L container:resource "scalingo_container_type" "web" { app = scalingo_app.my-nginx.id name = "web" size = "L" amount = 1 } -
Run
terraform planand check if the result looks good -
If so, run
terraform apply - Once Terraform is done, your nginx instance is ready to be deployed:
- Head to your dashboard
- Click on your nginx application
- Click on the Deploy tab
- Click on Manual deployment in the left menu
- Click the Trigger deployment button
- After a few seconds, your nginx reverse proxy instance is finally up and running, making your Keycloak cluster reachable!
Exposing the Admin
Keycloak’s /admin endpoint provides access to the administration console and
management APIs used to configure and operate a Keycloak cluster. Through this
endpoint, administrators can manage realms, users, groups, roles, identity
providers, clients, and authentication flows.
Using the Command Line or the Terraform Provider
- Add a
limit_req_zonedirective to set up rate limiting at the top of theservers.conf.erbfile:# Rate limiting for /admin: limit_req_zone $binary_remote_addr zone=keycloak_admin:10m rate=20r/m; - Add two new
locationin theservers.conf.erbfile:location = /admin { return 301 /admin/; } location ^~ /admin/ { # IP allow list: allow 192.0.2.10; allow 198.51.100.0/24; deny all; # Rate limiting: limit_req zone=keycloak_admin burst=20 nodelay; proxy_pass http://keycloak/admin/; proxy_redirect default; } - Trigger a new deployment
Exposing Health and Metrics
Keycloak allows to track instances status, health, and performances, thanks to several health checks and metrics endpoints.
When enabled, these endpoints are exposed on the management port, which
defaults to TCP 9000. By default, they are not available, and they must
be explicitely enabled.
- Add a new
upstreamdedicated to management in theservers.conf.erbfile:upstream mgmt { zone mgmt 64k; ip_hash; server <%= ENV["KEYCLOAK_PRIVATE_DOMAIN"] %>:<%= ENV["KC_HTTP_MANAGEMENT_PORT"] or 9000 %> resolve max_fails=2; } - Add two new
limit_req_zonedirectives for health and metrics:limit_req_zone $binary_remote_addr zone=keycloak_health:10m rate=60r/m; limit_req_zone $binary_remote_addr zone=keycloak_metrics:10m rate=30r/m; - Add two new
locationin theservers.conf.erbfile:<% if ENV["KC_HEALTH_ENABLED"] %> location ^~ /health/ { # IP allow list: allow 192.0.2.20; allow 198.51.100.0/24; deny all; limit_req zone=keycloak_health burst=20 nodelay; proxy_pass http://mgmt/health/; proxy_redirect default; } <% end %> <% if ENV["KC_METRICS_ENABLED"] %> location = /metrics { # IP allow list: allow 192.0.2.20; allow 198.51.100.0/24; deny all; # Rate limiting: limit_req zone=keycloak_metrics burst=10 nodelay; proxy_pass http://mgmt/metrics; proxy_redirect default; }
Using the Command Line
Make sure you have followed the first steps
- Enable the health and/or metrics endpoints:
scalingo --app my-keycloak env-set KC_HEALTH_ENABLED=true scalingo --app my-keycloak env-set KC_METRICS_ENABLED=true - (optional) Choose a port for the management interface:
scalingo --app my-keycloak env-set KC_HTTP_MANAGEMENT_PORT=9000 - Trigger a new deployment
Using the Terraform Provider
Make sure you have followed the first steps
- Update the
scalingo_appresource block of the Keycloak app to include the appropriate environment variables:resource "scalingo_app" "my-keycloak" { name = "my-keycloak" project_id = scalingo_project.keycloak_project.id stack_id = "scalingo-26" environment = { # [...] KC_HEALTH_ENABLED = true, KC_METRICS_ENABLED = true, # Optional: KC_HTTP_MANAGEMENT_PORT = 9000 } } - Trigger a new deployment
Managing Logs
By default, Keycloak runs with the INFO log level. This provides general
operational information such as Keycloak lifecycle events, authentication
flows, and warnings, without being overly verbose.
Logging can be configured either globally or per component. The log level can
be changed using environment variables. For example, to set the general log
level to DEBUG, set KC_LOG_LEVEL to DEBUG.
It is also possible to enable logging for specific components only, allowing
for troubleshooting specific issues without flooding the logs. To do so, use an
environment variable named after the component. For example, to specify a
different log level for the component org.hibernate, you could set
KC_LOG_LEVEL_ORG_HIBERNATE to DEBUG.
Logs are written to standard output by default, making them fully compatible with every logging features Scalingo provides.
For further guidance related to Keycloak logging, please refer to the official documentation.
Managing Vulnerabilities
Keycloak official vulnerabilities and security issues are documented here by the editor.
Updating
While updating Keycloak is generally safe, we still advise to take extra care, especially before updating a production instance:
- Review the official changelog that is published with each release. Breaking and notable changes should catch your attention.
- Ensure your SPIs and themes are compatible with the new version.
- Keep a recent backup of your production database aside. The update process sometimes involves database updates, which can unfortunately fail. Having a backup allows to rollback to a working version in case of failures.
- Test the exact update path on a testing instance.
Using the Command Line
- Update the version to the desired number:
scalingo --app my-keycloak env-set KEYCLOAK_VERSION=<new_version> - In your Keycloak repository, create a new empty commit:
git commit -m "Deploy version <new_version>" --allow-empty - Trigger a new deployment
Using the Terraform Provider
- Update the
scalingo_appresource block of the Keycloak app to include theKEYCLOAK_VERSIONenvironment variables:resource "scalingo_app" "my-keycloak" { name = "my-keycloak" project_id = scalingo_project.keycloak_project.id stack_id = "scalingo-26" environment = { # [...] KEYCLOAK_VERSION = "<new_version>" } } - Trigger a new deployment
Customizing
Service Provider Interfaces
Keycloak is designed to be extensible. It provides multiple Service Provider Interfaces (SPIs), each responsible for providing a specific capability to the server.
To add SPIs to your Keycloak cluster:
- Create a directory named
providersat the root of your project - Put the
.jarfiles in this directory (don’t forget to also add them to your git repository):my-keycloak ├── Procfile ├── providers │ └── spi.jar └── system.properties - Configure each SPI using the available environment variables (please refer to the SPI documentation for available variables)
- Trigger a new deployment
Themes
Keycloak also supports custom themes, which allow to personalize the look and feel of end-user facing pages. This allows to further integrate Keycloak with your applications or company.
To add themes to your Keycloak cluster:
- Create a directory named
providersat the root of your project - Put the
.jarfiles in this directory (don’t forget to also add them to your git repository):my-keycloak ├── Procfile ├── providers │ └── theme.jar └── system.properties - Configure each theme using the available environment variables (please refer to the theme documentation for available variables)
- Trigger a new deployment
Environment
Keycloak supports many environment variables.
Moreover, the buildpack makes use of the following environment variables. They can be leveraged to customize your deployment:
-
KEYCLOAK_VERSION
Allows to specify the version of Keycloak to deploy.
Defaults to not being set, which falls back on the default version set in the buildpack. -
KEYCLOAK_PRIVATE_DOMAIN_NAME
Private domain name of the Keycloak process type. Allows the reverse proxy to know where the requests must be forwarded.
Default to not being set.