Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

KeycloakContainer

A single-node Keycloak container in start-dev mode (in-memory H2, no external database — fine for tests, never for production).

Default image: quay.io/keycloak/keycloak:26.4 Guest ports: 8080 (HTTP / auth server), 9000 (management — health lives here, see below)

MethodOnEffect
KeycloakContainer::new()builderPinned default image, admin/admin, with_memory_limit(1024).
KeycloakContainer::with_image(image)builderCaller-chosen image.
.with_admin_username(u) / .with_admin_password(p)builderOverride either bootstrap admin credential before start().
.start()builder → Result<KeycloakGuard>Boots the container.
.admin_username() / .admin_password()guardThe configured bootstrap admin credentials.
.auth_server_url()guardThe auth server’s base URI (HTTP port) — realm/OIDC endpoints live under this.
.management_url()guardThe management interface’s base URI (health/metrics — port 9000, a different port than auth_server_url()).
.stop()guardStops and removes the container, releases its ports.

Admin bootstrap env — 26.x renamed these, verified against the pinned tag

Keycloak 26.x replaced the old KEYCLOAK_ADMIN/KEYCLOAK_ADMIN_PASSWORD pair with KC_BOOTSTRAP_ADMIN_USERNAME/KC_BOOTSTRAP_ADMIN_PASSWORD. Verified directly against quay.io/keycloak/keycloak:26.4: booting with only the new names produces KC-SERVICES0077: Created temporary admin user with username admin in the log; the legacy names are not read by this image at all. This module sets only the new names.

Health lives on the management port (9000), not 8080

Captured verbatim from a real boot:

Listening on: http://0.0.0.0:8080. Management interface listening on http://0.0.0.0:9000.

With KC_HEALTH_ENABLED=true set, GET /health on port 9000 returns 200 OK (body: literal OK); the same path on 8080 404s, and the commonly assumed /health/ready sub-path 404s on this tag too — this image’s SmallRye Health root aggregate is served bare at /health, not /health/ready — pinned to /health on 9000 accordingly.

Memory — Quarkus JVM, needed the ladder

Booted under msb’s default ~450M microVM RAM, the JVM is Killed (OOM) partway through startup (captured: 'java' ... -XX:MaxRAMPercentage=70 ... Killed) — same Paketo/Quarkus-on-microVM story as SpringCloudConfigContainer. Retried with -m 1024M: boots clean, /health reports 200 well within the startup timeout. with_memory_limit(1024) is this module’s default.

Complete example

use rightsize_modules::KeycloakContainer;

#[tokio::test]
async fn oidc_discovery_document_asserts_the_issuer() -> Result<(), Box<dyn std::error::Error>> {
    let guard = KeycloakContainer::new().start().await?;

    let mut resp = ureq::Agent::new_with_defaults()
        .get(format!(
            "{}/realms/master/.well-known/openid-configuration",
            guard.auth_server_url()
        ))
        .call()?;
    let body = resp.body_mut().read_to_string()?;
    let expected = format!("\"issuer\":\"{}/realms/master\"", guard.auth_server_url());
    assert!(body.contains(&expected));

    guard.stop().await?;
    Ok(())
}

Backend notes

with_memory_limit(1024) is set unconditionally by the module — see Memory above.