Security AWS

Keycloak 26.8.1 on AWS User Guide

| Product: Keycloak 26.8.1 on Ubuntu 24.04 on AWS

Overview

This guide covers launching and using the Keycloak 26.8.1 on Ubuntu 24.04 AMI from cloudimg on Amazon EC2. It is one version of the cloudimg identity, secrets and PKI listing on AWS Marketplace; you pick it as the software version when you subscribe and launch.

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: you create realms (each one is 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 (social sign-in, other OpenID Connect or SAML providers), and add multi-factor authentication and passkeys. A web admin console and a REST admin API configure it; every realm also has an account console where users manage their own profile, password and sessions.

The cloudimg image is a hardened, ready-to-use Keycloak server:

  • 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, pre-built for production (kc.sh build) and started with kc.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 keycloak database role. PostgreSQL listens on 127.0.0.1 only and accepts only that role, only from 127.0.0.1.
  • HTTPS from the 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. Keycloak's address (hostname) is set from the instance's EC2 public DNS name at first boot; 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, and 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-For to the TCP peer (it never passes on a client's own header) and Keycloak trusts forwarded headers only from 127.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. /admin/ and the master realm (where administrators sign in) can be limited 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: an identity-provider discovery URL or a JWKS URL pointing at it fails. The AMI requires IMDSv2.
  • Fully patched Ubuntu 24.04 LTS at build time, with unattended security upgrades switched on.

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.

Step 1: 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 and your Region, then Launch through EC2. Choose m5.large, your key pair and a security group as above.

With the AWS CLI (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, and then starts Keycloak and nginx.

Step 2: Connect to your instance

Operating system SSH user
Ubuntu 24.04 ubuntu
ssh -i <your-key.pem> ubuntu@<public-ip>

Step 3: Check the services

systemctl is-active keycloak nginx postgresql@16-main keycloak-guard
sudo ss -tln | awk 'NR>1 {print $4}' | sort -u
active
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]:9000

Only SSH, HTTP and HTTPS listen on the network. Keycloak (8080), its management interface (9000) and PostgreSQL (5432) are on 127.0.0.1 only ([::ffff:127.0.0.1] is how Java's sockets show 127.0.0.1); 53 is Ubuntu's local DNS resolver.

Step 4: Get your address and the temporary administrator

First boot writes everything that is unique to your instance to a file only root can read:

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 will warn 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: replace it with a certificate from a certificate authority for your own name instead (Step 11).
  • KEYCLOAK_BOOTSTRAP_ADMIN_USER / PASSWORD sign in to the admin console. This is a temporary administrator: replace it (Step 8).

Step 5: Run the self-test

The image includes an end-to-end test that signs in as the temporary administrator, creates a throwaway realm, user and OpenID Connect client, obtains a token for that user and verifies its signature against the realm's published keys, and then deletes the realm:

sudo keycloak-selftest
token 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_OK

Step 6: Sign in to the admin console

Open KEYCLOAK_ADMIN_CONSOLE from Step 4 in your browser (or simply 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.

The Keycloak admin console sign-in page (master realm), served over HTTPS

Keycloak shows a banner while you are signed in as the temporary administrator:

The admin console home, signed in as the temporary administrator

Step 7: Create a realm, a client and a user

Keep the master realm for administrators only and create a realm for your users. You can do this in the admin console (Manage realms > Create realm, then Clients > Create client and Users > Add user) or with the admin REST API. This creates a realm my-company with brute-force protection and login events on, 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
SETUP
realm my-company: 201
client my-app: 201
user jane: 201

The my-app client in the my-company realm

The user jane in the my-company realm

Each realm has its own signing keys, generated on your instance (Realm settings > Keys):

The my-company realm's keys, with its RS256 signing key

Users sign in on the realm's own pages, for example its account console at KEYCLOAK_URL/realms/my-company/account/:

The my-company realm sign-in page

Jane's account console after signing in

Step 8: Replace the temporary administrator

Create your own permanent administrator, then delete temp-admin:

  1. In the admin console, switch to the master realm, open Users > Add user, enter a username (and an e-mail address), and Create.
  2. On the new user's Credentials tab, Set password with Temporary off.
  3. On its Role mapping tab, Assign role > Filter by realm roles, select admin, and Assign.
  4. Sign out, sign in as your new administrator, open Users, and Delete temp-admin. The warning banner disappears.
  5. 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 (Realm settings > Security defenses > Brute force detection) and consider adding a one-time password (OTP) or passkey requirement for administrators (Authentication).

The same with the admin API (it 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.

Step 9: 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 only) 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
TOKEN
alg: 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 OK

The password grant used here is for testing only. A real web application uses the authorization code flow with PKCE (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"}

Step 10: 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/cloudimg/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/cloudimg/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 nginx

Everyone 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 with a ; is refused outright. Your other realms stay reachable for your users and applications. You can also restrict TCP 443 in the security group if Keycloak serves only an internal network.

Step 11: Use your own domain and certificate

Point a DNS name at the instance (an Elastic IP is a good idea), then:

sudo keycloak-set-domain auth.example.com

This 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>: 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.

Step 12: 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/ || true
IPAddressDeny=::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: 401

Keycloak's own configuration is /etc/keycloak/keycloak.conf (rendered by keycloak-set-domain from /usr/local/lib/cloudimg/keycloak/keycloak.conf.tmpl; it holds the database password and is readable by root and the keycloak service account only). Health checks for a monitoring agent on the instance: curl http://127.0.0.1/health/ready.

Step 13: Passwords and secrets

  • The database password is in /etc/keycloak/db-password (root only) and keycloak.conf. Keycloak is the only client.
  • The temporary administrator's password is in /root/keycloak-credentials.txt until you remove it (Step 8).
  • Every realm's signing and encryption keys are generated by Keycloak 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.

Step 14: 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.dump

The 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.

Step 15: Licences and source

ls /usr/share/doc/cloudimg-keycloak/
sed -n '3,7p' /usr/share/doc/cloudimg-keycloak/BUILD-INFO
BUILD-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.

Step 16: 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; Keycloak deserializes no untrusted Java data and runs without a cluster transport), four MariaDB driver issues (the database is PostgreSQL, so the MariaDB driver is never loaded) and an OpenTelemetry tracing-header parsing issue (tracing is off and nginx drops tracing headers). They will be resolved by upstream library updates in a later Keycloak release.
  • 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 (Realm settings > Login > User registration). No mail server is configured; set one under Realm settings > Email for password resets and e-mail verification.

Support

cloudimg provides support for this image by e-mail at support@cloudimg.co.uk. Keycloak itself is documented at https://www.keycloak.org/documentation.