{"id":13821570,"url":"https://github.com/ivangfr/keycloak-clustered","last_synced_at":"2025-04-13T00:46:48.725Z","repository":{"id":38351973,"uuid":"137933116","full_name":"ivangfr/keycloak-clustered","owner":"ivangfr","description":"Keycloak-Clustered extends quay.io/keycloak/keycloak official Keycloak Docker image by adding JDBC_PING discovery protocol.","archived":false,"fork":false,"pushed_at":"2025-01-15T09:18:27.000Z","size":301,"stargazers_count":186,"open_issues_count":9,"forks_count":57,"subscribers_count":18,"default_branch":"master","last_synced_at":"2025-04-13T00:46:38.509Z","etag":null,"topics":["docker","jdbc-ping","keycloak","keycloak-cluster","mariadb","mssqlserver","mysql","postgresql","vagrant","virtualbox"],"latest_commit_sha":null,"homepage":"","language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ivangfr.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"ivangfr"}},"created_at":"2018-06-19T18:57:28.000Z","updated_at":"2025-04-08T06:22:18.000Z","dependencies_parsed_at":"2023-01-21T00:47:21.920Z","dependency_job_id":"decaeba3-a841-4fa9-8da4-ada47bb4e22b","html_url":"https://github.com/ivangfr/keycloak-clustered","commit_stats":{"total_commits":114,"total_committers":6,"mean_commits":19.0,"dds":0.06140350877192979,"last_synced_commit":"24f363436a11b0aa74fe2710302fbaff4bebedd3"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ivangfr%2Fkeycloak-clustered","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ivangfr%2Fkeycloak-clustered/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ivangfr%2Fkeycloak-clustered/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ivangfr%2Fkeycloak-clustered/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ivangfr","download_url":"https://codeload.github.com/ivangfr/keycloak-clustered/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248650434,"owners_count":21139672,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["docker","jdbc-ping","keycloak","keycloak-cluster","mariadb","mssqlserver","mysql","postgresql","vagrant","virtualbox"],"created_at":"2024-08-04T08:01:24.065Z","updated_at":"2025-04-13T00:46:48.704Z","avatar_url":"https://github.com/ivangfr.png","language":"Shell","funding_links":["https://github.com/sponsors/ivangfr"],"categories":["Shell","Example Projects"],"sub_categories":[],"readme":"# keycloak-clustered\n\n**Keycloak-Clustered** extends `quay.io/keycloak/keycloak` official **Keycloak Docker image** by adding **JDBC_PING** discovery protocol\n\n## Proof-of-Concepts \u0026 Articles\n\nOn [ivangfr.github.io](https://ivangfr.github.io), I have compiled my Proof-of-Concepts (PoCs) and articles. You can easily search for the technology you are interested in by using the filter. Who knows, perhaps I have already implemented a PoC or written an article about what you are looking for.\n\n## Additional Readings\n\n- \\[**Medium**\\] [**Keycloak Cluster using JDBC-PING for Distributed Caching**](https://medium.com/@ivangfr/keycloak-cluster-using-jdbc-ping-for-distributed-caching-8ba5c09cc206)\n- \\[**Medium**\\] [**Keycloak Cluster Setup with Vagrant, Virtual Machines, and JDBC-PING for Distributed Caching**](https://medium.com/@ivangfr/keycloak-cluster-setup-with-vagrant-virtual-machines-and-jdbc-ping-for-distributed-caching-bd09708219d1)\n- \\[**Medium**\\] [**Keycloak Cluster Setup with Docker Compose and JDBC-PING for Distributed Caching**](https://medium.com/@ivangfr/keycloak-cluster-setup-with-docker-compose-and-jdbc-ping-for-distributed-caching-3623fb6ee513)\n- \\[**Medium**\\] [**Keycloak Cluster Setup with Docker Compose and UDP for Distributed Caching**](https://medium.com/@ivangfr/keycloak-cluster-setup-with-docker-compose-and-udp-for-distributed-caching-9123be1de12d)\n\n## Supported tags and respective Dockerfile links\n\n- `26.0.8`, `latest` ([Dockerfile](https://github.com/ivangfr/keycloak-clustered/blob/master/26.0.8/Dockerfile))\n- `26.0.7` ([Dockerfile](https://github.com/ivangfr/keycloak-clustered/blob/master/26.0.7/Dockerfile))\n- `26.0.6` ([Dockerfile](https://github.com/ivangfr/keycloak-clustered/blob/master/26.0.6/Dockerfile))\n- `26.0.5` ([Dockerfile](https://github.com/ivangfr/keycloak-clustered/blob/master/26.0.5/Dockerfile))\n- `26.0.4` ([Dockerfile](https://github.com/ivangfr/keycloak-clustered/blob/master/26.0.4/Dockerfile))\n- `26.0.2` ([Dockerfile](https://github.com/ivangfr/keycloak-clustered/blob/master/26.0.2/Dockerfile))\n- `26.0.1` ([Dockerfile](https://github.com/ivangfr/keycloak-clustered/blob/master/26.0.1/Dockerfile))\n- `26.0.0` ([Dockerfile](https://github.com/ivangfr/keycloak-clustered/blob/master/26.0.0/Dockerfile))\n- `25.0.6` ([Dockerfile](https://github.com/ivangfr/keycloak-clustered/blob/master/25.0.6/Dockerfile))\n- `25.0.5` ([Dockerfile](https://github.com/ivangfr/keycloak-clustered/blob/master/25.0.5/Dockerfile))\n- `25.0.4` ([Dockerfile](https://github.com/ivangfr/keycloak-clustered/blob/master/25.0.4/Dockerfile))\n- `25.0.2` ([Dockerfile](https://github.com/ivangfr/keycloak-clustered/blob/master/25.0.2/Dockerfile))\n- `25.0.1` ([Dockerfile](https://github.com/ivangfr/keycloak-clustered/blob/master/25.0.1/Dockerfile))\n- `25.0.0` ([Dockerfile](https://github.com/ivangfr/keycloak-clustered/blob/master/25.0.0/Dockerfile))\n\n## Author\n\nIvan Franchin ([**LinkedIn**](https://www.linkedin.com/in/ivanfranchin)) ([**Github**](https://github.com/ivangfr)) ([**Medium**](https://medium.com/@ivangfr)) ([**X**](https://x.com/ivangfr))\n\n## Environment Variables\n\nPlease, refer to the official **Keycloak** documentation at https://www.keycloak.org/server/all-config\n\n## How to build locally a development Docker image\n\nNavigate into one of the version folders and run the following command\n```\ndocker build -t ivanfranchin/keycloak-clustered:latest .\n```\n\n## How to check if Keycloak instances are sharing user sessions\n\n1. Open two different browsers, for instance `Chrome` and `Safari` or `Chrome` and `Incognito Chrome`.\n\n2. In one access http://localhost:8080 and, in another, http://localhost:8081\n\n3. Login with the following credentials\n   ```\n   username: admin\n   password: admin\n   ```\n\n4. Once logged in\n  - Click `Sessions` present on the menu on the left;\n  - You should see that `admin` has two sessions.\n\n## Running a Keycloak Cluster using JDBC_PING\n\n### Prerequisites\n\n[`Docker`](https://www.docker.com/)\n\n### Using MySQL\n\n#### Startup\n\nOpen a terminal and create a Docker network\n```\ndocker network create keycloak-net\n```\n\nRun [MySQL](https://hub.docker.com/_/mysql) Docker container\n```\ndocker run --rm --name mysql -p 3306:3306 \\\n  -e MYSQL_DATABASE=keycloak \\\n  -e MYSQL_USER=keycloak \\\n  -e MYSQL_PASSWORD=password \\\n  -e MYSQL_ROOT_PASSWORD=root_password \\\n  --network keycloak-net \\\n  mysql:9.1.0\n```\n\nOpen another terminal and run `keycloak-clustered-1` Docker container\n```\ndocker run --rm --name keycloak-clustered-1 -p 8080:8080 \\\n  -e KEYCLOAK_ADMIN=admin \\\n  -e KEYCLOAK_ADMIN_PASSWORD=admin \\\n  -e KC_DB=mysql \\\n  -e KC_DB_URL_HOST=mysql \\\n  -e KC_DB_URL_DATABASE=keycloak \\\n  -e KC_DB_USERNAME=keycloak \\\n  -e KC_DB_PASSWORD=password \\\n  -e KC_LOG_LEVEL=INFO,org.infinispan:DEBUG,org.jgroups:DEBUG \\\n  -e JGROUPS_DISCOVERY_EXTERNAL_IP=keycloak-clustered-1 \\\n  --network keycloak-net \\\n  ivanfranchin/keycloak-clustered:latest start-dev\n```\n\nFinally, open another terminal and run `keycloak-clustered-2` Docker container\n```\ndocker run --rm --name keycloak-clustered-2 -p 8081:8080 \\\n  -e KEYCLOAK_ADMIN=admin \\\n  -e KEYCLOAK_ADMIN_PASSWORD=admin \\\n  -e KC_DB=mysql \\\n  -e KC_DB_URL_HOST=mysql \\\n  -e KC_DB_URL_DATABASE=keycloak \\\n  -e KC_DB_USERNAME=keycloak \\\n  -e KC_DB_PASSWORD=password \\\n  -e KC_LOG_LEVEL=INFO,org.infinispan:DEBUG,org.jgroups:DEBUG \\\n  -e JGROUPS_DISCOVERY_EXTERNAL_IP=keycloak-clustered-2 \\\n  --network keycloak-net \\\n  ivanfranchin/keycloak-clustered:latest start-dev\n```\n\n#### Testing\n\nIn order to test it, have a look at [How to check if keycloak-clustered instances are sharing user sessions](#how-to-check-if-keycloak-instances-are-sharing-user-sessions)\n\n#### Check database\n\nAccess `MySQL monitor` terminal inside `mysql` Docker container\n```\ndocker exec -it -e MYSQL_PWD=password mysql mysql -ukeycloak --database keycloak\n```\n\nList tables\n```\nmysql\u003e show tables;\n```\n\nSelect entries in `JGROUPSPING` table\n```\nmysql\u003e SELECT * FROM JGROUPSPING;\n```\n\nTo exit `MySQL monitor` terminal type `exit`\n\n#### Teardown\n\nTo stop `keycloak-clustered-1` and `keycloak-clustered-2` Docker containers, press `Ctrl+C` in their terminals;\n\nTo stop `mysql` Docker container, press `Ctrl+\\` in its terminal;\n\nTo remove Docker network, run in a terminal\n```\ndocker network rm keycloak-net\n```\n\n### Using MariaDB\n\nOpen a terminal and create a Docker network\n```\ndocker network create keycloak-net\n```\n\nRun [MariaDB](https://hub.docker.com/_/mariadb) Docker container\n```\ndocker run --rm --name mariadb -p 3306:3306 \\\n  -e MARIADB_DATABASE=keycloak \\\n  -e MARIADB_USER=keycloak \\\n  -e MARIADB_PASSWORD=password \\\n  -e MARIADB_ROOT_PASSWORD=root_password \\\n  --network keycloak-net \\\n  mariadb:10.11.10\n```\n\nOpen another terminal and run `keycloak-clustered-1` Docker container\n```\ndocker run --rm --name keycloak-clustered-1 -p 8080:8080 \\\n  -e KEYCLOAK_ADMIN=admin \\\n  -e KEYCLOAK_ADMIN_PASSWORD=admin \\\n  -e KC_DB=mariadb \\\n  -e KC_DB_URL_HOST=mariadb \\\n  -e KC_DB_URL_DATABASE=keycloak \\\n  -e KC_DB_USERNAME=keycloak \\\n  -e KC_DB_PASSWORD=password \\\n  -e KC_LOG_LEVEL=INFO,org.infinispan:DEBUG,org.jgroups:DEBUG \\\n  -e JGROUPS_DISCOVERY_EXTERNAL_IP=keycloak-clustered-1 \\\n  --network keycloak-net \\\n  ivanfranchin/keycloak-clustered:latest start-dev\n```\n\nFinally, open another terminal and run `keycloak-clustered-2` Docker container\n```\ndocker run --rm --name keycloak-clustered-2 -p 8081:8080 \\\n  -e KEYCLOAK_ADMIN=admin \\\n  -e KEYCLOAK_ADMIN_PASSWORD=admin \\\n  -e KC_DB=mariadb \\\n  -e KC_DB_URL_HOST=mariadb \\\n  -e KC_DB_URL_DATABASE=keycloak \\\n  -e KC_DB_USERNAME=keycloak \\\n  -e KC_DB_PASSWORD=password \\\n  -e KC_LOG_LEVEL=INFO,org.infinispan:DEBUG,org.jgroups:DEBUG \\\n  -e JGROUPS_DISCOVERY_EXTERNAL_IP=keycloak-clustered-2 \\\n  --network keycloak-net \\\n  ivanfranchin/keycloak-clustered:latest start-dev\n```\n\n#### Testing\n\nIn order to test it, have a look at [How to check if keycloak-clustered instances are sharing user sessions](#how-to-check-if-keycloak-instances-are-sharing-user-sessions)\n\n#### Check database\n\nAccess `MariaDB monitor` terminal inside `mariadb` Docker container\n```\ndocker exec -it mariadb mariadb -ukeycloak -ppassword --database keycloak\n```\n\nList tables\n```\nMariaDB [keycloak]\u003e show tables;\n```\n\nSelect entries in `JGROUPSPING` table\n```\nMariaDB [keycloak]\u003e SELECT * FROM JGROUPSPING;\n```\n\nTo exit `MariaDB monitor` terminal type `exit\n\n#### Teardown\n\nTo stop `keycloak-clustered-1` and `keycloak-clustered-2` Docker containers, press `Ctrl+C` in their terminals;\n\nTo stop `mariadb` Docker container, press `Ctrl+\\` in its terminal;\n\nTo remove Docker network, run in a terminal\n```\ndocker network rm keycloak-net\n```\n\n### Using Postgres\n\n#### Startup\n\nOpen a terminal and create a Docker network\n```\ndocker network create keycloak-net\n```\n\nRun [Postgres](https://hub.docker.com/_/postgres) Docker container\n```\ndocker run --rm --name postgres -p 5432:5432 \\\n  -e POSTGRES_DB=keycloak \\\n  -e POSTGRES_USER=keycloak \\\n  -e POSTGRES_PASSWORD=password \\\n  --network keycloak-net \\\n  postgres:17.2\n```\n\nOpen another terminal and run `keycloak-clustered-1` Docker container\n```\ndocker run --rm --name keycloak-clustered-1 -p 8080:8080 \\\n  -e KEYCLOAK_ADMIN=admin \\\n  -e KEYCLOAK_ADMIN_PASSWORD=admin \\\n  -e KC_DB=postgres \\\n  -e KC_DB_URL_HOST=postgres \\\n  -e KC_DB_URL_DATABASE=keycloak \\\n  -e KC_DB_SCHEMA=myschema \\\n  -e KC_DB_USERNAME=keycloak \\\n  -e KC_DB_PASSWORD=password \\\n  -e KC_LOG_LEVEL=INFO,org.infinispan:DEBUG,org.jgroups:DEBUG \\\n  -e JGROUPS_DISCOVERY_EXTERNAL_IP=keycloak-clustered-1 \\\n  --network keycloak-net \\\n  ivanfranchin/keycloak-clustered:latest start-dev\n```\n\nFinally, open another terminal and run `keycloak-clustered-2` Docker container\n```\ndocker run --rm --name keycloak-clustered-2 -p 8081:8080 \\\n  -e KEYCLOAK_ADMIN=admin \\\n  -e KEYCLOAK_ADMIN_PASSWORD=admin \\\n  -e KC_DB=postgres \\\n  -e KC_DB_URL_HOST=postgres \\\n  -e KC_DB_URL_DATABASE=keycloak \\\n  -e KC_DB_SCHEMA=myschema \\\n  -e KC_DB_USERNAME=keycloak \\\n  -e KC_DB_PASSWORD=password \\\n  -e KC_LOG_LEVEL=INFO,org.infinispan:DEBUG,org.jgroups:DEBUG \\\n  -e JGROUPS_DISCOVERY_EXTERNAL_IP=keycloak-clustered-2 \\\n  --network keycloak-net \\\n  ivanfranchin/keycloak-clustered:latest start-dev\n```\n\n#### Testing\n\nIn order to test it, have a look at [How to check if keycloak-clustered instances are sharing user sessions](#how-to-check-if-keycloak-instances-are-sharing-user-sessions)\n\n#### Check database\n\nAccess `psql` terminal inside `postgres` Docker container\n```\ndocker exec -it postgres psql -U keycloak\n```\n\nList tables in `myschema` schema\n```\nkeycloak=# \\dt myschema.*\n```\n\nSelect entries in `JGROUPSPING` table\n```\nkeycloak=# SELECT * FROM myschema.JGROUPSPING;\n```\n\nTo exit `psql` terminal type `\\q`\n\n#### Teardown\n\nTo stop `postgres`, `keycloak-clustered-1` and `keycloak-clustered-2` Docker containers, press `Ctrl+C` in their terminals;\n\nTo remove Docker network, run in a terminal\n```\ndocker network rm keycloak-net\n```\n\n### Using Microsoft SQL Server\n\n\u003e **Warning**: It is not working! See [Issues](#issues) section\n\n#### Startup\n\nOpen a terminal and create a Docker network\n```\ndocker network create keycloak-net\n```\n\nRun [Microsoft SQL Server](https://hub.docker.com/_/microsoft-mssql-server) Docker container\n```\ndocker run --rm --name mssql -p 1433:1433 \\\n  -e ACCEPT_EULA=Y \\\n  -e MSSQL_SA_PASSWORD=my_Password \\\n  --network keycloak-net \\\n  mcr.microsoft.com/mssql/server:2022-CU11-ubuntu-22.04\n```\n\nOpen another terminal and run the following command to create `keycloak` database\n```\ndocker exec -i mssql /opt/mssql-tools/bin/sqlcmd -S localhost -U SA -P my_Password -Q 'CREATE DATABASE keycloak'\n```\n\nIn a terminal, run `keycloak-clustered-1` Docker container\n```\ndocker run --rm --name keycloak-clustered-1 -p 8080:8080 \\\n  -e KEYCLOAK_ADMIN=admin \\\n  -e KEYCLOAK_ADMIN_PASSWORD=admin \\\n  -e KC_DB=mssql \\\n  -e KC_DB_URL_HOST=mssql \\\n  -e KC_DB_URL_DATABASE=keycloak \\\n  -e KC_DB_URL_PROPERTIES=\";trustServerCertificate=false;encrypt=false\" \\\n  -e KC_DB_USERNAME=SA \\\n  -e KC_DB_PASSWORD=my_Password \\\n  -e KC_LOG_LEVEL=INFO,org.infinispan:DEBUG,org.jgroups:DEBUG \\\n  -e JGROUPS_DISCOVERY_EXTERNAL_IP=keycloak-clustered-1 \\\n  --network keycloak-net \\\n  ivanfranchin/keycloak-clustered:latest start-dev\n```\n\nFinally, open another terminal and run `keycloak-clustered-2` Docker container\n```\ndocker run --rm --name keycloak-clustered-2 -p 8081:8080 \\\n  -e KEYCLOAK_ADMIN=admin \\\n  -e KEYCLOAK_ADMIN_PASSWORD=admin \\\n  -e KC_DB=mssql \\\n  -e KC_DB_URL_HOST=mssql \\\n  -e KC_DB_URL_DATABASE=keycloak \\\n  -e KC_DB_URL_PROPERTIES=\";trustServerCertificate=false;encrypt=false\" \\\n  -e KC_DB_USERNAME=SA \\\n  -e KC_DB_PASSWORD=my_Password \\\n  -e KC_LOG_LEVEL=INFO,org.infinispan:DEBUG,org.jgroups:DEBUG \\\n  -e JGROUPS_DISCOVERY_EXTERNAL_IP=keycloak-clustered-2 \\\n  --network keycloak-net \\\n  ivanfranchin/keycloak-clustered:latest start-dev\n```\n\n#### Testing\n\nIn order to test it, have a look at [How to check if keycloak-clustered instances are sharing user sessions](#how-to-check-if-keycloak-instances-are-sharing-user-sessions)\n\n#### Check database\n\nAccess `sqlcmd` terminal inside `mssql` Docker container\n```\ndocker exec -it mssql /opt/mssql-tools/bin/sqlcmd -S localhost -U SA -P my_Password\n```\n\nSelect entries in `JGROUPSPING` table\n```\n1\u003e select * from keycloak.myschema.JGROUPSPING\n2\u003e go\n```\n\nTo exit `sqlcmd` terminal type `exit` or press `Ctrl+C`\n\n#### Teardown\n\nTo stop `keycloak-clustered-1` and `keycloak-clustered-2` Docker containers, press `Ctrl+C` in their terminals;\n\nTo remove Docker network, run in a terminal\n```\ndocker network rm keycloak-net\n```\n\n## Running a Keycloak Cluster using JDBC_PING in Virtual Machines\n\n### Prerequisites\n\n[`VirtualBox`](https://www.virtualbox.org/) and [`Vagrant`](https://developer.hashicorp.com/vagrant/docs/installation)\n\n### Startup\n\nOpen a terminal and make sure you are in `keycloak-clustered` root folder\n\nYou can edit `Vagrantfile` and set the database and/or the discovery protocol to be used\n\nStart the virtual machines by running the command below\n```\nvagrant up\n```\n\n\u003e **Mac Users**\n\u003e\n\u003e If you have an error like the one below\n\u003e ```\n\u003e The IP address configured for the host-only network is not within the\n\u003e allowed ranges. Please update the address used to be within the allowed\n\u003e ranges and run the command again.\n\u003e\n\u003e   Address: 10.0.0.1\n\u003e   Ranges: 123.456.78.0/21\n\u003e\n\u003e Valid ranges can be modified in the /etc/vbox/networks.conf file. For\n\u003e more information including valid format see:\n\u003e\n\u003e   https://www.virtualbox.org/manual/ch06.html#network_hostonly\n\u003e ```\n\u003e\n\u003e Create a new file at `/etc/vbox/networks.conf` on your Mac with content\n\u003e ```\n\u003e * 10.0.0.0/8 123.456.78.0/21\n\u003e * 2001::/64\n\u003e ```\n\nWait a bit until the virtual machines get started. It will take some time.\n\nOnce the execution of the command `vagrant up` finishes, we can check the state of all active Vagrant environments\n```\nvagrant status\n```\n\nCheck `keycloak-clustered` docker logs in `keycloak1` virtual machine\n```\nvagrant ssh keycloak1\nvagrant@vagrant:~$ docker logs keycloak-clustered -f\n```\n\u003e **Note:** To get out of the logging view press `Ctrl+C` and to exit the virtual machine type `exit`\n\nCheck `keycloak-clustered` docker logs in `keycloak2` virtual machine\n```\nvagrant ssh keycloak2\nvagrant@vagrant:~$ docker logs keycloak-clustered -f\n```\n\u003e **Note:** To get out of the logging view press `Ctrl+C` and to exit the virtual machine type `exit`\n\nCheck databases if you are using `JDBC_PING`\n```\nvagrant ssh databases\n```\n\u003e **Note:** To exit the virtual machine type `exit`\n\n- MySQL\n  ```\n  vagrant@vagrant:~$ docker exec -it -e MYSQL_PWD=password mysql mysql -ukeycloak --database keycloak\n  mysql\u003e show tables;\n  mysql\u003e SELECT * FROM JGROUPSPING;\n  ```\n  \u003e **Note:** To exit type `exit`\n\n- MariaDB\n  ```\n  vagrant@vagrant:~$ docker exec -it mariadb mysql -ukeycloak -ppassword --database keycloak\n  MariaDB [keycloak]\u003e show tables;\n  MariaDB [keycloak]\u003e SELECT * FROM JGROUPSPING;\n  ```\n  \u003e **Note:** To exit type `exit`\n\n- Postgres\n  ```\n  vagrant@vagrant:~$ docker exec -it postgres psql -U keycloak\n  keycloak=# \\dt *.*\n    \n  -- `public` schema\n  keycloak=# SELECT * FROM JGROUPSPING;\n    \n  -- in case the schema `myschema` was set\n  keycloak=# SELECT * FROM myschema.JGROUPSPING;\n  ```\n  \u003e **Note:** To exit type `\\q`\n\n### Testing\n\nIn order to test it, have a look at [How to check if keycloak-clustered instances are sharing user sessions](#how-to-check-if-keycloak-instances-are-sharing-user-sessions)\n\n### Using another database\n\nEdit `Vagrantfile` by setting to `DB_VENDOR` variable the database to be used\n\nReload Keycloak virtual machines by running\n```\nvagrant reload keycloak1 keycloak2 --provision\n```\n\n### Teardown\n\n#### Suspend the machines\n\nSuspending the virtual machines will stop them and save their current running state. For it run\n```\nvagrant suspend\n```\n\nTo bring the virtual machines back up run\n```\nvagrant up\n```\n\n#### Halt the machines\n\nHalting the virtual machines will gracefully shut down the guest operating system and power down the guest machine\n```\nvagrant halt\n```\n\nIt preserves the contents of disk and allows to start it again by running\n```\nvagrant up\n```\n\n#### Destroy the machines\n\nDestroying the virtual machine will remove all traces of the guest machine from your system. It'll stop the guest machine, power it down, and reclaim its disk space and RAM.\n```\nvagrant destroy -f\n```\n\nFor a complete clean up, you can remove Vagrant box used in this section\n```\nvagrant box remove hashicorp/bionic64\n```\n\n## Issues\n\n### Microsoft SQL Server\n\n```\nWARN  [com.arjuna.ats.jta] (main) ARJUNA016061: TransactionImple.enlistResource - XAResource.start returned: XAException.XAER_RMERR for \u003c formatId=131077, gtrid_length=35, bqual_length=36, tx_uid=0:ffffac160003:a743:65a52f11:0, node_name=quarkus, branch_uid=0:ffffac160003:a743:65a52f11:3f, subordinatenodename=null, eis_name=0 \u003e: javax.transaction.xa.XAException: com.microsoft.sqlserver.jdbc.SQLServerException: Failed to create the XA control connection. Error: \"The connection is closed.\"\n\tat com.microsoft.sqlserver.jdbc.SQLServerXAResource.DTC_XA_Interface(SQLServerXAResource.java:757)\n\tat com.microsoft.sqlserver.jdbc.SQLServerXAResource.start(SQLServerXAResource.java:791)\n\tat io.agroal.narayana.BaseXAResource.start(BaseXAResource.java:150)\n\tat com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionImple.enlistResource(TransactionImple.java:661)\n\tat com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionImple.enlistResource(TransactionImple.java:422)\n\tat io.agroal.narayana.NarayanaTransactionIntegration.associate(NarayanaTransactionIntegration.java:93)\n\tat io.agroal.pool.ConnectionPool.getConnection(ConnectionPool.java:252)\n\tat io.agroal.pool.DataSource.getConnection(DataSource.java:86)\n\tat io.quarkus.hibernate.orm.runtime.customized.QuarkusConnectionProvider.getConnection(QuarkusConnectionProvider.java:23)\n\tat org.hibernate.internal.NonContextualJdbcConnectionAccess.obtainConnection(NonContextualJdbcConnectionAccess.java:38)\n\tat org.hibernate.resource.jdbc.internal.LogicalConnectionManagedImpl.acquireConnectionIfNeeded(LogicalConnectionManagedImpl.java:113)\n\tat org.hibernate.resource.jdbc.internal.LogicalConnectionManagedImpl.getPhysicalConnection(LogicalConnectionManagedImpl.java:143)\n\tat org.hibernate.engine.jdbc.internal.StatementPreparerImpl.connection(StatementPreparerImpl.java:51)\n\tat org.hibernate.engine.jdbc.internal.StatementPreparerImpl$5.doPrepare(StatementPreparerImpl.java:150)\n\tat org.hibernate.engine.jdbc.internal.StatementPreparerImpl$StatementPreparationTemplate.prepareStatement(StatementPreparerImpl.java:177)\n\tat org.hibernate.engine.jdbc.internal.StatementPreparerImpl.prepareQueryStatement(StatementPreparerImpl.java:152)\n\tat org.hibernate.sql.exec.internal.JdbcSelectExecutorStandardImpl.lambda$list$0(JdbcSelectExecutorStandardImpl.java:102)\n\tat org.hibernate.sql.results.jdbc.internal.DeferredResultSetAccess.executeQuery(DeferredResultSetAccess.java:226)\n\tat org.hibernate.sql.results.jdbc.internal.DeferredResultSetAccess.getResultSet(DeferredResultSetAccess.java:163)\n\tat org.hibernate.sql.results.jdbc.internal.JdbcValuesResultSetImpl.advanceNext(JdbcValuesResultSetImpl.java:254)\n\tat org.hibernate.sql.results.jdbc.internal.JdbcValuesResultSetImpl.processNext(JdbcValuesResultSetImpl.java:134)\n\tat org.hibernate.sql.results.jdbc.internal.AbstractJdbcValues.next(AbstractJdbcValues.java:19)\n\tat org.hibernate.sql.results.internal.RowProcessingStateStandardImpl.next(RowProcessingStateStandardImpl.java:66)\n\tat org.hibernate.sql.results.spi.ListResultsConsumer.consume(ListResultsConsumer.java:198)\n\tat org.hibernate.sql.results.spi.ListResultsConsumer.consume(ListResultsConsumer.java:33)\n\tat org.hibernate.sql.exec.internal.JdbcSelectExecutorStandardImpl.doExecuteQuery(JdbcSelectExecutorStandardImpl.java:361)\n\tat org.hibernate.sql.exec.internal.JdbcSelectExecutorStandardImpl.executeQuery(JdbcSelectExecutorStandardImpl.java:168)\n\tat org.hibernate.sql.exec.internal.JdbcSelectExecutorStandardImpl.list(JdbcSelectExecutorStandardImpl.java:93)\n\tat org.hibernate.sql.exec.spi.JdbcSelectExecutor.list(JdbcSelectExecutor.java:31)\n\tat org.hibernate.query.sqm.internal.ConcreteSqmSelectQueryPlan.lambda$new$0(ConcreteSqmSelectQueryPlan.java:110)\n\tat org.hibernate.query.sqm.internal.ConcreteSqmSelectQueryPlan.withCacheableSqmInterpretation(ConcreteSqmSelectQueryPlan.java:303)\n\tat org.hibernate.query.sqm.internal.ConcreteSqmSelectQueryPlan.performList(ConcreteSqmSelectQueryPlan.java:244)\n\tat org.hibernate.query.sqm.internal.QuerySqmImpl.doList(QuerySqmImpl.java:518)\n\tat org.hibernate.query.spi.AbstractSelectionQuery.list(AbstractSelectionQuery.java:367)\n\tat org.hibernate.query.Query.getResultList(Query.java:119)\n\tat org.keycloak.models.jpa.MigrationModelAdapter.init(MigrationModelAdapter.java:59)\n\tat org.keycloak.models.jpa.MigrationModelAdapter.\u003cinit\u003e(MigrationModelAdapter.java:42)\n\tat org.keycloak.models.jpa.JpaRealmProvider.getMigrationModel(JpaRealmProvider.java:99)\n\tat org.keycloak.storage.datastore.LegacyMigrationManager.migrate(LegacyMigrationManager.java:128)\n\tat org.keycloak.migration.MigrationModelManager.migrate(MigrationModelManager.java:33)\n\tat org.keycloak.quarkus.runtime.storage.legacy.database.LegacyJpaConnectionProviderFactory.migrateModel(LegacyJpaConnectionProviderFactory.java:216)\n\tat org.keycloak.quarkus.runtime.storage.legacy.database.LegacyJpaConnectionProviderFactory.initSchema(LegacyJpaConnectionProviderFactory.java:210)\n\tat org.keycloak.models.utils.KeycloakModelUtils.lambda$runJobInTransaction$1(KeycloakModelUtils.java:260)\n\tat org.keycloak.models.utils.KeycloakModelUtils.runJobInTransactionWithResult(KeycloakModelUtils.java:382)\n\tat org.keycloak.models.utils.KeycloakModelUtils.runJobInTransaction(KeycloakModelUtils.java:259)\n\tat org.keycloak.quarkus.runtime.storage.legacy.database.LegacyJpaConnectionProviderFactory.postInit(LegacyJpaConnectionProviderFactory.java:135)\n\tat org.keycloak.quarkus.runtime.integration.QuarkusKeycloakSessionFactory.init(QuarkusKeycloakSessionFactory.java:105)\n\tat org.keycloak.quarkus.runtime.integration.jaxrs.QuarkusKeycloakApplication.createSessionFactory(QuarkusKeycloakApplication.java:56)\n\tat org.keycloak.services.resources.KeycloakApplication.startup(KeycloakApplication.java:130)\n\tat org.keycloak.quarkus.runtime.integration.jaxrs.QuarkusKeycloakApplication.onStartupEvent(QuarkusKeycloakApplication.java:46)\n\tat org.keycloak.quarkus.runtime.integration.jaxrs.QuarkusKeycloakApplication_Observer_onStartupEvent_67d48587b481b764f44181a34540ebd3d495c2c7.notify(Unknown Source)\n\tat io.quarkus.arc.impl.EventImpl$Notifier.notifyObservers(EventImpl.java:346)\n\tat io.quarkus.arc.impl.EventImpl$Notifier.notify(EventImpl.java:328)\n\tat io.quarkus.arc.impl.EventImpl.fire(EventImpl.java:82)\n\tat io.quarkus.arc.runtime.ArcRecorder.fireLifecycleEvent(ArcRecorder.java:155)\n\tat io.quarkus.arc.runtime.ArcRecorder.handleLifecycleEvents(ArcRecorder.java:106)\n\tat io.quarkus.deployment.steps.LifecycleEventsBuildStep$startupEvent1144526294.deploy_0(Unknown Source)\n\tat io.quarkus.deployment.steps.LifecycleEventsBuildStep$startupEvent1144526294.deploy(Unknown Source)\n\tat io.quarkus.runner.ApplicationImpl.doStart(Unknown Source)\n\tat io.quarkus.runtime.Application.start(Application.java:101)\n\tat io.quarkus.runtime.ApplicationLifecycleManager.run(ApplicationLifecycleManager.java:111)\n\tat io.quarkus.runtime.Quarkus.run(Quarkus.java:71)\n\tat org.keycloak.quarkus.runtime.KeycloakMain.start(KeycloakMain.java:117)\n\tat org.keycloak.quarkus.runtime.cli.command.AbstractStartCommand.run(AbstractStartCommand.java:33)\n\tat picocli.CommandLine.executeUserObject(CommandLine.java:2026)\n\tat picocli.CommandLine.access$1500(CommandLine.java:148)\n\tat picocli.CommandLine$RunLast.executeUserObjectOfLastSubcommandWithSameParent(CommandLine.java:2461)\n\tat picocli.CommandLine$RunLast.handle(CommandLine.java:2453)\n\tat picocli.CommandLine$RunLast.handle(CommandLine.java:2415)\n\tat picocli.CommandLine$AbstractParseResultHandler.execute(CommandLine.java:2273)\n\tat picocli.CommandLine$RunLast.execute(CommandLine.java:2417)\n\tat picocli.CommandLine.execute(CommandLine.java:2170)\n\tat org.keycloak.quarkus.runtime.cli.Picocli.parseAndRun(Picocli.java:119)\n\tat org.keycloak.quarkus.runtime.KeycloakMain.main(KeycloakMain.java:107)\n\tat java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\n\tat java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)\n\tat java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\n\tat java.base/java.lang.reflect.Method.invoke(Method.java:568)\n\tat io.quarkus.bootstrap.runner.QuarkusEntryPoint.doRun(QuarkusEntryPoint.java:61)\n\tat io.quarkus.bootstrap.runner.QuarkusEntryPoint.main(QuarkusEntryPoint.java:32)\n\n2024-01-15 13:11:45,598 WARN  [com.arjuna.ats.jta] (main) ARJUNA016138: Failed to enlist XA resource io.agroal.narayana.BaseXAResource@70d256e: jakarta.transaction.SystemException: TransactionImple.enlistResource - XAResource.start ARJUNA016054: could not register transaction: \u003c formatId=131077, gtrid_length=35, bqual_length=36, tx_uid=0:ffffac160003:a743:65a52f11:0, node_name=quarkus, branch_uid=0:ffffac160003:a743:65a52f11:3f, subordinatenodename=null, eis_name=0 \u003e\n\tat com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionImple.enlistResource(TransactionImple.java:714)\n\tat com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionImple.enlistResource(TransactionImple.java:422)\n\tat io.agroal.narayana.NarayanaTransactionIntegration.associate(NarayanaTransactionIntegration.java:93)\n\tat io.agroal.pool.ConnectionPool.getConnection(ConnectionPool.java:252)\n\tat io.agroal.pool.DataSource.getConnection(DataSource.java:86)\n\tat io.quarkus.hibernate.orm.runtime.customized.QuarkusConnectionProvider.getConnection(QuarkusConnectionProvider.java:23)\n\tat org.hibernate.internal.NonContextualJdbcConnectionAccess.obtainConnection(NonContextualJdbcConnectionAccess.java:38)\n\tat org.hibernate.resource.jdbc.internal.LogicalConnectionManagedImpl.acquireConnectionIfNeeded(LogicalConnectionManagedImpl.java:113)\n\tat org.hibernate.resource.jdbc.internal.LogicalConnectionManagedImpl.getPhysicalConnection(LogicalConnectionManagedImpl.java:143)\n\tat org.hibernate.engine.jdbc.internal.StatementPreparerImpl.connection(StatementPreparerImpl.java:51)\n\tat org.hibernate.engine.jdbc.internal.StatementPreparerImpl$5.doPrepare(StatementPreparerImpl.java:150)\n\tat org.hibernate.engine.jdbc.internal.StatementPreparerImpl$StatementPreparationTemplate.prepareStatement(StatementPreparerImpl.java:177)\n\tat org.hibernate.engine.jdbc.internal.StatementPreparerImpl.prepareQueryStatement(StatementPreparerImpl.java:152)\n\tat org.hibernate.sql.exec.internal.JdbcSelectExecutorStandardImpl.lambda$list$0(JdbcSelectExecutorStandardImpl.java:102)\n\tat org.hibernate.sql.results.jdbc.internal.DeferredResultSetAccess.executeQuery(DeferredResultSetAccess.java:226)\n\tat org.hibernate.sql.results.jdbc.internal.DeferredResultSetAccess.getResultSet(DeferredResultSetAccess.java:163)\n\tat org.hibernate.sql.results.jdbc.internal.JdbcValuesResultSetImpl.advanceNext(JdbcValuesResultSetImpl.java:254)\n\tat org.hibernate.sql.results.jdbc.internal.JdbcValuesResultSetImpl.processNext(JdbcValuesResultSetImpl.java:134)\n\tat org.hibernate.sql.results.jdbc.internal.AbstractJdbcValues.next(AbstractJdbcValues.java:19)\n\tat org.hibernate.sql.results.internal.RowProcessingStateStandardImpl.next(RowProcessingStateStandardImpl.java:66)\n\tat org.hibernate.sql.results.spi.ListResultsConsumer.consume(ListResultsConsumer.java:198)\n\tat org.hibernate.sql.results.spi.ListResultsConsumer.consume(ListResultsConsumer.java:33)\n\tat org.hibernate.sql.exec.internal.JdbcSelectExecutorStandardImpl.doExecuteQuery(JdbcSelectExecutorStandardImpl.java:361)\n\tat org.hibernate.sql.exec.internal.JdbcSelectExecutorStandardImpl.executeQuery(JdbcSelectExecutorStandardImpl.java:168)\n\tat org.hibernate.sql.exec.internal.JdbcSelectExecutorStandardImpl.list(JdbcSelectExecutorStandardImpl.java:93)\n\tat org.hibernate.sql.exec.spi.JdbcSelectExecutor.list(JdbcSelectExecutor.java:31)\n\tat org.hibernate.query.sqm.internal.ConcreteSqmSelectQueryPlan.lambda$new$0(ConcreteSqmSelectQueryPlan.java:110)\n\tat org.hibernate.query.sqm.internal.ConcreteSqmSelectQueryPlan.withCacheableSqmInterpretation(ConcreteSqmSelectQueryPlan.java:303)\n\tat org.hibernate.query.sqm.internal.ConcreteSqmSelectQueryPlan.performList(ConcreteSqmSelectQueryPlan.java:244)\n\tat org.hibernate.query.sqm.internal.QuerySqmImpl.doList(QuerySqmImpl.java:518)\n\tat org.hibernate.query.spi.AbstractSelectionQuery.list(AbstractSelectionQuery.java:367)\n\tat org.hibernate.query.Query.getResultList(Query.java:119)\n\tat org.keycloak.models.jpa.MigrationModelAdapter.init(MigrationModelAdapter.java:59)\n\tat org.keycloak.models.jpa.MigrationModelAdapter.\u003cinit\u003e(MigrationModelAdapter.java:42)\n\tat org.keycloak.models.jpa.JpaRealmProvider.getMigrationModel(JpaRealmProvider.java:99)\n\tat org.keycloak.storage.datastore.LegacyMigrationManager.migrate(LegacyMigrationManager.java:128)\n\tat org.keycloak.migration.MigrationModelManager.migrate(MigrationModelManager.java:33)\n\tat org.keycloak.quarkus.runtime.storage.legacy.database.LegacyJpaConnectionProviderFactory.migrateModel(LegacyJpaConnectionProviderFactory.java:216)\n\tat org.keycloak.quarkus.runtime.storage.legacy.database.LegacyJpaConnectionProviderFactory.initSchema(LegacyJpaConnectionProviderFactory.java:210)\n\tat org.keycloak.models.utils.KeycloakModelUtils.lambda$runJobInTransaction$1(KeycloakModelUtils.java:260)\n\tat org.keycloak.models.utils.KeycloakModelUtils.runJobInTransactionWithResult(KeycloakModelUtils.java:382)\n\tat org.keycloak.models.utils.KeycloakModelUtils.runJobInTransaction(KeycloakModelUtils.java:259)\n\tat org.keycloak.quarkus.runtime.storage.legacy.database.LegacyJpaConnectionProviderFactory.postInit(LegacyJpaConnectionProviderFactory.java:135)\n\tat org.keycloak.quarkus.runtime.integration.QuarkusKeycloakSessionFactory.init(QuarkusKeycloakSessionFactory.java:105)\n\tat org.keycloak.quarkus.runtime.integration.jaxrs.QuarkusKeycloakApplication.createSessionFactory(QuarkusKeycloakApplication.java:56)\n\tat org.keycloak.services.resources.KeycloakApplication.startup(KeycloakApplication.java:130)\n\tat org.keycloak.quarkus.runtime.integration.jaxrs.QuarkusKeycloakApplication.onStartupEvent(QuarkusKeycloakApplication.java:46)\n\tat org.keycloak.quarkus.runtime.integration.jaxrs.QuarkusKeycloakApplication_Observer_onStartupEvent_67d48587b481b764f44181a34540ebd3d495c2c7.notify(Unknown Source)\n\tat io.quarkus.arc.impl.EventImpl$Notifier.notifyObservers(EventImpl.java:346)\n\tat io.quarkus.arc.impl.EventImpl$Notifier.notify(EventImpl.java:328)\n\tat io.quarkus.arc.impl.EventImpl.fire(EventImpl.java:82)\n\tat io.quarkus.arc.runtime.ArcRecorder.fireLifecycleEvent(ArcRecorder.java:155)\n\tat io.quarkus.arc.runtime.ArcRecorder.handleLifecycleEvents(ArcRecorder.java:106)\n\tat io.quarkus.deployment.steps.LifecycleEventsBuildStep$startupEvent1144526294.deploy_0(Unknown Source)\n\tat io.quarkus.deployment.steps.LifecycleEventsBuildStep$startupEvent1144526294.deploy(Unknown Source)\n\tat io.quarkus.runner.ApplicationImpl.doStart(Unknown Source)\n\tat io.quarkus.runtime.Application.start(Application.java:101)\n\tat io.quarkus.runtime.ApplicationLifecycleManager.run(ApplicationLifecycleManager.java:111)\n\tat io.quarkus.runtime.Quarkus.run(Quarkus.java:71)\n\tat org.keycloak.quarkus.runtime.KeycloakMain.start(KeycloakMain.java:117)\n\tat org.keycloak.quarkus.runtime.cli.command.AbstractStartCommand.run(AbstractStartCommand.java:33)\n\tat picocli.CommandLine.executeUserObject(CommandLine.java:2026)\n\tat picocli.CommandLine.access$1500(CommandLine.java:148)\n\tat picocli.CommandLine$RunLast.executeUserObjectOfLastSubcommandWithSameParent(CommandLine.java:2461)\n\tat picocli.CommandLine$RunLast.handle(CommandLine.java:2453)\n\tat picocli.CommandLine$RunLast.handle(CommandLine.java:2415)\n\tat picocli.CommandLine$AbstractParseResultHandler.execute(CommandLine.java:2273)\n\tat picocli.CommandLine$RunLast.execute(CommandLine.java:2417)\n\tat picocli.CommandLine.execute(CommandLine.java:2170)\n\tat org.keycloak.quarkus.runtime.cli.Picocli.parseAndRun(Picocli.java:119)\n\tat org.keycloak.quarkus.runtime.KeycloakMain.main(KeycloakMain.java:107)\n\tat java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\n\tat java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)\n\tat java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\n\tat java.base/java.lang.reflect.Method.invoke(Method.java:568)\n\tat io.quarkus.bootstrap.runner.QuarkusEntryPoint.doRun(QuarkusEntryPoint.java:61)\n\tat io.quarkus.bootstrap.runner.QuarkusEntryPoint.main(QuarkusEntryPoint.java:32)\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fivangfr%2Fkeycloak-clustered","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fivangfr%2Fkeycloak-clustered","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fivangfr%2Fkeycloak-clustered/lists"}