Keycloak 26.8.1
Overview
Keycloak is an open-source identity and access management server. It gives your applications single sign-on with OpenID Connect, OAuth 2.0 and SAML 2.0. This guide covers the Keycloak 26.8.1 on Ubuntu 24.04 AMI from SecureAnyCloud on Amazon EC2, one version of the SecureAnyCloud identity, secrets and PKI listing on AWS Marketplace. You pick it as the software version when you subscribe and launch.
With Keycloak you can:
- Create realms, each an independent identity provider with its own users, applications, sign-in pages and signing keys
- Register clients: your web apps, single-page apps, mobile apps and services
- Manage users and groups
- Connect LDAP / Active Directory user federation and external identity providers, such as social sign-in or other OpenID Connect or SAML providers
- Add multi-factor authentication and passkeys
You configure Keycloak through a web admin console or a REST admin API. Every realm also has an account console where users manage their own profile, password and sessions.
The SecureAnyCloud image is a hardened, ready-to-use Keycloak server. What you get:
- The upstream release, in production mode. The image runs the official Keycloak 26.8.1 distribution from the Keycloak project (not a vendor build), verified against its published checksum and OpenPGP signature. It is pre-built for production (
kc.sh build) and started withkc.sh start --optimized. Development mode (start-dev) is never used. Java is OpenJDK 21 from Ubuntu. - PostgreSQL 16, created on your first boot. The database cluster does not exist in the image. First boot creates it, with a password unique to your instance for the
keycloakdatabase role. PostgreSQL listens on127.0.0.1only and accepts only that role, only from127.0.0.1. - HTTPS from first boot. nginx serves Keycloak on 443 with a certificate generated on your instance (an end-entity certificate, not a certificate authority). Port 80 redirects to HTTPS. At first boot, Keycloak's address (
hostname) is set from the instance's EC2 public DNS name. Every issuer, redirect and admin-console URL uses it, never the address a request claims. You can change it to your own domain. - No default login. There is no
admin/admin. First boot creates the master realm with exactly one temporary administrator,temp-admin, with a random password unique to your instance (Keycloak's own bootstrap mechanism,KC_BOOTSTRAP_ADMIN_USERNAME/KC_BOOTSTRAP_ADMIN_PASSWORD). Keycloak shows a warning banner until you replace it with your own administrator (Step 8). - Back ends on the loopback address only. Keycloak's HTTP listener (
127.0.0.1:8080), its management interface with the health endpoints (127.0.0.1:9000) and PostgreSQL (127.0.0.1:5432) are never reachable from the network. A firewall rule lets only nginx (and root) connect to Keycloak's ports. Metrics are off and there is no cluster listener. - The real client address. nginx sets
X-Forwarded-Forto the TCP peer and never passes on a client's own header. Keycloak trusts forwarded headers only from127.0.0.1, so login events, brute-force protection and rate limits see the real address. nginx also limits sign-in requests per client address. - An admin-console access list. You can limit
/admin/and the master realm (where administrators sign in) to your own networks in one file (Step 10). - Metadata service blocked. Keycloak, PostgreSQL, nginx and the first-boot service cannot reach the EC2 instance metadata service, so an identity-provider discovery URL or a JWKS URL pointing at it fails. The AMI requires IMDSv2.
- Fully patched. Ubuntu 24.04 LTS is patched at build time, with unattended security upgrades switched on.
- Support from SecureAnyCloud
What is on the image: Keycloak 26.8.1 (Apache-2.0), OpenJDK 21, PostgreSQL 16, nginx, nftables, cloud-init and the AWS Systems Manager agent, all from Ubuntu 24.04 except Keycloak itself.
Default user: ubuntu. Architecture: x86_64. Recommended instance type: m5.large (2 vCPUs, 8 GiB).
Prerequisites
- An AWS account subscribed to the listing, an EC2 key pair and a VPC subnet.
- A security group that allows TCP 22 (SSH) from your own networks only, TCP 443 from wherever your users and applications are, and TCP 80 (redirects to HTTPS). Never open 8080, 9000 or 5432: nothing listens on them off the instance.
- For production, a DNS name you control (for example
auth.example.com) and, ideally, a certificate from a certificate authority. Until then, the instance uses its EC2 public DNS name with its own self-signed certificate.
Launch the instance
In the AWS Marketplace listing, choose Continue to Subscribe, then Continue to Configuration. Pick Keycloak 26.8.1 on Ubuntu 24.04 as the software version, choose your Region, then choose Launch through EC2. Select m5.large, your key pair and a security group as described above.
To launch with the AWS CLI instead, run the following and replace the values in angle brackets:
aws ec2 run-instances --image-id <ami-id-from-the-listing> --instance-type m5.large \
--key-name <your-key-pair> --security-group-ids <your-security-group> --subnet-id <your-subnet> \
--metadata-options HttpTokens=required,HttpEndpoint=enabled \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=keycloak}]'First boot takes about one to two minutes after the instance is running. It creates the database, names the instance, creates the certificate and the temporary administrator, then starts Keycloak and nginx.
Connect to your instance
Connect over SSH as the ubuntu user (Ubuntu 24.04):
ssh -i <your-key.pem> ubuntu@<public-ip>Check the services
systemctl is-active keycloak nginx postgresql@16-main keycloak-guard
sudo ss -tln | awk 'NR>1 {print $4}' | sort -uactive active active active 0.0.0.0:22 0.0.0.0:443 0.0.0.0:80 127.0.0.1:5432 127.0.0.53%lo:53 127.0.0.54:53 [::]:22 [::]:443 [::]:80 [::ffff:127.0.0.1]:8080 [::ffff:127.0.0.1]:9000Only SSH, HTTP and HTTPS listen on the network. Keycloak (8080), its management interface (9000) and PostgreSQL (5432) are on 127.0.0.1 only. Java's sockets show 127.0.0.1 as [::ffff:127.0.0.1], and port 53 is Ubuntu's local DNS resolver.
Get your address and the temporary administrator
First boot writes everything unique to your instance to a file that only root can read. Print it:
sudo cat /root/keycloak-credentials.txt# Keycloak 26.8.1 - written on first boot by keycloak-firstboot.service. Unique to this instance. Keep it secret. KEYCLOAK_URL=https://ec2-3-87-204-47.compute-1.amazonaws.com KEYCLOAK_ADMIN_CONSOLE=https://ec2-3-87-204-47.compute-1.amazonaws.com/admin/master/console/ KEYCLOAK_TLS_SHA256=C4:64:84:FC:... KEYCLOAK_BOOTSTRAP_ADMIN_USER=temp-admin KEYCLOAK_BOOTSTRAP_ADMIN_PASSWORD=<48 random hexadecimal characters> ...- KEYCLOAK_URL is your Keycloak address. Every realm's OpenID Connect issuer is
KEYCLOAK_URL/realms/<realm>. - KEYCLOAK_TLS_SHA256 is the fingerprint of the instance's own certificate. Your browser warns that the certificate is not trusted. Compare the fingerprint it shows with this one before you continue. Do not add the certificate to a trust store. Instead, replace it with a certificate from a certificate authority for your own name (Step 11).
- KEYCLOAK_BOOTSTRAP_ADMIN_USER / PASSWORD are the credentials for the admin console. They belong to a temporary administrator, so replace it (Step 8).
Run the self-test
The image includes an end-to-end test. It signs in as the temporary administrator and creates a throwaway realm, user and OpenID Connect client. It then obtains a token for that user, verifies the token's signature against the realm's published keys and deletes the realm. Run it:
sudo keycloak-selftesttoken OK: RS256 kid CF_Ni2kJ verifies against https://ec2-3-87-204-47.compute-1.amazonaws.com/realms/selftest-6e1f3eaf/protocol/openid-connect/certs; wrong password refused KEYCLOAK_SELFTEST_OKSign in to the admin console
Open KEYCLOAK_ADMIN_CONSOLE from Step 4 in your browser, or open KEYCLOAK_URL, which redirects there. Check the certificate fingerprint, then sign in as temp-admin with the password from Step 4. The default admin / admin does not exist.
Keycloak shows a banner while you are signed in as the temporary administrator.
Create a realm, a client and a user
Keep the master realm for administrators only and create a separate realm for your users. In the admin console, use Manage realms > Create realm, then Clients > Create client and Users > Add user. You can also use the admin REST API.
The commands below create a realm my-company with brute-force protection and login events on. They also create an OpenID Connect client my-app for an application at https://my-app.example.com and a user jane with a random password stored in /root/jane-password.txt:
sudo bash -s <<'SETUP' set -euo pipefail CA=/etc/ssl/keycloak/keycloak.crt
KC=$(sed -n 's/^KEYCLOAK_URL=//p' /root/keycloak-credentials.txt)
# an admin token for the temporary administrator (the password goes to curl on stdin, never on the command line)
TOKEN=$(sed -n 's/^KEYCLOAK_BOOTSTRAP_ADMIN_PASSWORD=//p' /root/keycloak-credentials.txt | tr -d '\n' | curl -s --cacert "$CA" \
-d grant_type=password -d client_id=admin-cli -d username=temp-admin --data-urlencode password@- \
"$KC/realms/master/protocol/openid-connect/token" | jq -r .access_token) api() { curl -s --cacert "$CA" -H @<(printf 'Authorization: Bearer %s\n' "$TOKEN") -H 'Content-Type: application/json' "$@"; }
# 1. a realm for your users, with brute-force protection and login events on api -o /dev/null -w 'realm my-company: %{http_code}\n' -X POST "$KC/admin/realms" \
-d '{"realm":"my-company","enabled":true,"bruteForceProtected":true,"eventsEnabled":true}'
# 2. an OpenID Connect client for your application api -o /dev/null -w 'client my-app: %{http_code}\n' -X POST "$KC/admin/realms/my-company/clients" \
-d '{"clientId":"my-app","name":"My App","publicClient":false,"standardFlowEnabled":true,"directAccessGrantsEnabled":true,"fullScopeAllowed":false,"redirectUris":["https://my-app.example.com/*"],"webOrigins":["https://my-app.example.com"]}'
# 3. a user with a random password (kept in /root/jane-password.txt, 0600) ( umask 077; openssl rand -base64 18 > /root/jane-password.txt )
python3 - /root/jane-password.txt > /run/jane.json <<'PY' import json, sys print(json.dumps({"username": "jane", "enabled": True, "email": "jane@example.com", "emailVerified": True, "firstName": "Jane", "lastName": "Doe", "credentials": [{"type": "password", "value": open(sys.argv[1]).read().strip(), "temporary": False}]})) PY api -o /dev/null -w 'user jane: %{http_code}\n' -X POST "$KC/admin/realms/my-company/users" --data-binary @/run/jane.json shred -u /run/jane.json SETUPrealm my-company: 201 client my-app: 201 user jane: 201Each realm has its own signing keys, generated on your instance. View them under Realm settings > Keys.
Users sign in on the realm's own pages, for example its account console at KEYCLOAK_URL/realms/my-company/account/:
Replace the temporary administrator
Create your own permanent administrator, then delete temp-admin:
- In the admin console, switch to the master realm, open Users > Add user, enter a username (and an e-mail address) and select Create.
- On the new user's Credentials tab, select Set password with Temporary off.
- On the Role mapping tab, select Assign role > Filter by realm roles, choose admin and select Assign.
- Sign out and sign in as your new administrator. Open Users and Delete
temp-admin. The warning banner disappears. - Remove the temporary password from
/root/keycloak-credentials.txt(sudo nano /root/keycloak-credentials.txt).
Also turn on brute-force protection for the master realm under Realm settings > Security defenses > Brute force detection. Consider requiring a one-time password (OTP) or passkey for administrators under Authentication.
You can do the same with the admin API. The command asks for the new administrator's name and password, and never shows or stores the password:
read -rp 'New administrator username: ' NEWADMIN; read -rsp 'Password: ' NEWPW; echo CA=/etc/ssl/keycloak/keycloak.crt;
KC=$(sudo sed -n 's/^KEYCLOAK_URL=//p' /root/keycloak-credentials.txt)
TOKEN=$(sudo sed -n 's/^KEYCLOAK_BOOTSTRAP_ADMIN_PASSWORD=//p' /root/keycloak-credentials.txt | tr -d '\n' | curl -s --cacert "$CA" \
-d grant_type=password -d client_id=admin-cli -d username=temp-admin --data-urlencode password@- \
"$KC/realms/master/protocol/openid-connect/token" | jq -r .access_token) api() { curl -s --cacert "$CA" -H @<(printf 'Authorization: Bearer %s\n' "$TOKEN") -H 'Content-Type: application/json' "$@"; } export NEWPW; jq -n --arg u "$NEWADMIN" '{username:$u, enabled:true, credentials:[{type:"password", value:env.NEWPW, temporary:false}]}' \
| api -X POST "$KC/admin/realms/master/users" --data-binary @- -o /dev/null -w 'user: %{http_code}\n'; unset NEWPW
UID_NEW=$(api "$KC/admin/realms/master/users?username=$NEWADMIN&exact=true" | jq -r '.[0].id') api "$KC/admin/realms/master/roles/admin" | jq -c '[.]' | api -X POST "$KC/admin/realms/master/users/$UID_NEW/role-mappings/realm" --data-binary @- -o /dev/null -w 'admin role: %{http_code}\n'Sign in to the console as the new administrator to check it, then delete temp-admin there.
Get a token and verify it
An application verifies the tokens Keycloak issues against the realm's published keys (its JWKS, KEYCLOAK_URL/realms/<realm>/protocol/openid-connect/certs). This signs jane in through my-app and checks the token's signature with nothing but Python and OpenSSL:
sudo bash -s <<'TOKEN' set -euo pipefail CA=/etc/ssl/keycloak/keycloak.crt
KC=$(sed -n 's/^KEYCLOAK_URL=//p' /root/keycloak-credentials.txt)
ADMIN=$(sed -n 's/^KEYCLOAK_BOOTSTRAP_ADMIN_PASSWORD=//p' /root/keycloak-credentials.txt | tr -d '\n' | curl -s --cacert "$CA" \
-d grant_type=password -d client_id=admin-cli -d username=temp-admin --data-urlencode password@- \
"$KC/realms/master/protocol/openid-connect/token" | jq -r .access_token) api() { curl -s --cacert "$CA" -H @<(printf 'Authorization: Bearer %s\n' "$ADMIN") "$@"; }
CID=$(api "$KC/admin/realms/my-company/clients?clientId=my-app" | jq -r '.[0].id') api "$KC/admin/realms/my-company/clients/$CID/client-secret" | jq -j .value > /run/my-app.secret
# jane signs in through my-app (the resource-owner password grant, for this test alone) and gets an access token tr -d '\n' < /root/jane-password.txt | curl -s --cacert "$CA" -d grant_type=password -d client_id=my-app -d username=jane \
--data-urlencode client_secret@/run/my-app.secret --data-urlencode password@- -d scope=openid \
"$KC/realms/my-company/protocol/openid-connect/token" | jq -r .access_token > /run/jane.token shred -u /run/my-app.secret
# verify its RS256 signature against the realm's published keys (JWKS), with no third-party library python3 - "$KC/realms/my-company/protocol/openid-connect/certs" /run/jane.token "$CA" <<'PY' import base64, json, ssl, subprocess, sys, tempfile, urllib.request url, tokfile, ca = sys.argv[1:4] h64, p64, s64 = open(tokfile).read().strip().split(".") b = lambda x: base64.urlsafe_b64decode(x + "=" * (-len(x) % 4)) hdr, claims = json.loads(b(h64)), json.loads(b(p64)) jwks = json.load(urllib.request.urlopen(url, context=ssl.create_default_context(cafile=ca))) key = next(k for k in jwks["keys"] if k["kid"] == hdr["kid"]) def der(tag, body): n = len(body) ln = bytes([n]) if n < 128 else bytes([0x80 | ((n.bit_length() + 7) // 8)]) + n.to_bytes((n.bit_length() + 7) // 8, "big") return bytes([tag]) + ln + body def dint(x): return der(2, x.to_bytes((x.bit_length() + 8) // 8, "big")) rsa = der(0x30, dint(int.from_bytes(b(key["n"]), "big")) + dint(int.from_bytes(b(key["e"]), "big"))) spki = der(0x30, bytes.fromhex("300d06092a864886f70d0101010500") + der(3, b"\0" + rsa)) with tempfile.NamedTemporaryFile(dir="/run") as k, tempfile.NamedTemporaryFile(dir="/run") as s, tempfile.NamedTemporaryFile(dir="/run") as m: k.write(spki); s.write(b(s64)); m.write((h64 + "." + p64).encode()); k.flush(); s.flush(); m.flush() ok = subprocess.run(["openssl", "dgst", "-sha256", "-verify", k.name, "-keyform", "DER", "-signature", s.name, m.name], capture_output=True, text=True).stdout.strip() print("alg:", hdr["alg"], " kid:", hdr["kid"]) print("iss:", claims["iss"]); print("user:", claims["preferred_username"], " client:", claims["azp"]) print("signature against the realm JWKS:", ok) PY shred -u /run/jane.token TOKENalg: RS256 kid: uDgUHIdMg5kCZ5_lLfC7zJcOpRfA_66XJ-sPJAml7_w iss: https://ec2-3-87-204-47.compute-1.amazonaws.com/realms/my-company user: jane client: my-app signature against the realm JWKS: Verified OKThe password grant used here is for testing only. A real web application uses the authorization code flow with PKCE, which is the standard flow my-app has enabled. Point your OpenID Connect library at the issuer KEYCLOAK_URL/realms/my-company with the client ID my-app and its secret (Clients > my-app > Credentials). Then turn Direct access grants off for my-app.
Keycloak records the real client address of every sign-in. The login events of my-company show the address the request came from (here, the instance itself), whatever X-Forwarded-For a client sends:
sudo bash -s <<'EVENTS' set -euo pipefail CA=/etc/ssl/keycloak/keycloak.crt
KC=$(sed -n 's/^KEYCLOAK_URL=//p' /root/keycloak-credentials.txt)
ADMIN=$(sed -n 's/^KEYCLOAK_BOOTSTRAP_ADMIN_PASSWORD=//p' /root/keycloak-credentials.txt | tr -d '\n' | curl -s --cacert "$CA" \
-d grant_type=password -d client_id=admin-cli -d username=temp-admin --data-urlencode password@- \
"$KC/realms/master/protocol/openid-connect/token" | jq -r .access_token)
curl -s --cacert "$CA" -H @<(printf 'Authorization: Bearer %s\n' "$ADMIN") "$KC/admin/realms/my-company/events?max=5" \
| jq -c '.[] | {type, clientId, ipAddress, error}' EVENTS{"type":"LOGIN","clientId":"my-app","ipAddress":"172.31.94.3","error":null} {"type":"LOGIN_ERROR","clientId":"my-app","ipAddress":"172.31.94.3","error":"invalid_client_credentials"}Restrict the admin console to your networks
The admin console and admin API (/admin/) and the master realm (/realms/master/, where administrators sign in) use the access list in /etc/nginx/sac/keycloak-admin-access.conf. By default it is allow all;. To allow only your own networks, replace that line with your addresses and a final deny all;:
sudo tee /etc/nginx/sac/keycloak-admin-access.conf >/dev/null <<'EOF' allow 203.0.113.0/24; allow 198.51.100.7; deny all; EOF
sudo nginx -t && sudo systemctl reload nginxEveryone else then gets 403 Forbidden on the admin console and the master realm's sign-in, whatever the spelling of the path (/ADMIN/, /realms/MASTER/, percent-encoded or doubled slashes). A path that contains ; is refused outright.
Your other realms stay reachable for your users and applications. If Keycloak serves only an internal network, you can also restrict TCP 443 in the security group.
Use your own domain and certificate
Point a DNS name at the instance (an Elastic IP is a good idea), then run:
sudo keycloak-set-domain auth.example.comThis sets Keycloak's hostname to https://auth.example.com, renders nginx for the name (the instance's other names redirect to it) and creates a new self-signed certificate for it.
Token issuers change to https://auth.example.com/realms/<realm>, so update the applications that trust this server.
To use a certificate from a certificate authority, replace /etc/ssl/keycloak/keycloak.crt and keycloak.key (keep the key 0600 root) and run sudo systemctl reload nginx.
Check the security posture
systemctl show keycloak -p IPAddressDeny
sudo grep -E '^(hostname|http-host|http-management-host|proxy-headers|proxy-trusted-addresses|db-url)=' /etc/keycloak/keycloak.conf
sudo -u nobody curl -s -m 5 -o /dev/null -w 'Keycloak port 8080 from another local user: %{http_code}\n' http://127.0.0.1:8080/realms/master/ || true curl -s -m 5 -o /dev/null -w 'metadata service without a token: %{http_code}\n' http://169.254.169.254/latest/meta-data/ || trueIPAddressDeny=::ffff:169.254.169.254/128 fd00:ec2::254/128 169.254.169.254/32 168.63.129.16/32 db-url=jdbc:postgresql://127.0.0.1:5432/keycloak hostname=https://ec2-3-87-204-47.compute-1.amazonaws.com http-host=127.0.0.1 proxy-headers=xforwarded proxy-trusted-addresses=127.0.0.1 http-management-host=127.0.0.1 Keycloak port 8080 from another local user: 000 metadata service without a token: 401Keycloak's own configuration is /etc/keycloak/keycloak.conf. keycloak-set-domain renders it from /usr/local/lib/sac/keycloak/keycloak.conf.tmpl. The file holds the database password and is readable only by root and the keycloak service account.
A monitoring agent on the instance can use this health check: curl http://127.0.0.1/health/ready.
Passwords and secrets
- The database password is in
/etc/keycloak/db-password(root only) andkeycloak.conf. Keycloak is the only client. - The temporary administrator's password is in
/root/keycloak-credentials.txtuntil you remove it (Step 8). - Keycloak creates every realm's signing and encryption keys in your database. Rotate them under Realm settings > Keys > Add providers when your policy requires.
- No secret is on any command line, in a systemd unit or in a log.
Your data and backups
Everything Keycloak stores (realms, clients, users, credentials, keys, sessions) is in the PostgreSQL database keycloak. Back it up with pg_dump to a file only root can read:
sudo -u postgres pg_dump -Fc keycloak | sudo sh -c 'umask 077; cat > /var/backups/keycloak-$(date +%F).dump'
sudo ls -l /var/backups/keycloak-*.dump-rw------- 1 root root 254960 Oct 31 11:37 /var/backups/keycloak-2026-10-31.dumpThe dump holds password hashes and realm private keys. Store it encrypted and off the instance, for example in an S3 bucket with encryption and restricted access.
EBS snapshots of the instance are a simple way to keep point-in-time copies of the whole server.
Licences and source
ls /usr/share/doc/sac-keycloak/ sed -n '3,7p' /usr/share/doc/sac-keycloak/BUILD-INFOBUILD-INFO LICENSE-keycloak-Apache-2.0.txt third-party-notice-keycloak.html Upstream distribution: https://github.com/keycloak/keycloak/releases/download/26.8.1/keycloak-26.8.1.zip sha256 4cd312c889af358a819a74a6c6f068f9ea159a6959e49924607cec95cfd4c1a4 sha1 616f35b37fb9ce12f07487bd7fe52ecc3684bdd4 (= upstream's published .sha1) OpenPGP: keycloak-26.8.1.zip.asc verified at port time (Good signature), EdDSA key 861AB50E8CC6611FB6BC01A6B8F12EA26FD6EEBA "Keycloak Bot <keycloak.bot@gmail.com>" (the key that signs 26.0.0+)Keycloak is licensed under the Apache License 2.0; the libraries in its distribution and their licences are listed in third-party-notice-keycloak.html. No Keycloak file is modified.
Security notes
- Advisories. Keycloak 26.8.1 is the current release and includes the fixes of 26.6.4 to 26.8.1, among them CVE-2026-9800 and CVE-2026-9086 (rated high). No published Keycloak advisory affects 26.8.1. A scan of its bundled libraries reports a few issues in code paths this configuration does not use. Guava's Java-deserialization memory exhaustion (CVE-2026-102554) does not apply, because Keycloak deserializes no untrusted Java data and runs without a cluster transport. Four MariaDB driver issues do not apply, because the database is PostgreSQL and the MariaDB driver is never loaded. An OpenTelemetry tracing-header parsing issue does not apply, because tracing is off and nginx drops tracing headers. Upstream library updates in a later Keycloak release will resolve these.
- Upstream notice at start. Keycloak logs that two deprecated features (
identity-brokering-api:v1,twitter-broker:v1) are enabled by default. That is Keycloak's own default, not a setting of this image. - Outbound requests. Keycloak fetches URLs that administrators configure (identity-provider discovery and keys, client JWKS URLs, back-channel logout). Those requests cannot reach the EC2 metadata service and time out after 10 seconds.
- Self-registration is off in new realms until you turn it on under Realm settings > Login > User registration. No mail server is configured. Set one under Realm settings > Email for password resets and e-mail verification.
Support
SecureAnyCloud provides support for this image by e-mail at sales@secureanycloud.com. The Keycloak documentation is at https://www.keycloak.org/documentation.
Deploy on AWS
Launch Keycloak 26.8.1 on Ubuntu 24.04 on AWS with 24/7 support from SecureAnyCloud.
View on Marketplace
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com