Keycloak
Curated name keycloak. Adds a Keycloak identity
& access management server to the workbench in compose mode, started in
development mode and pre-loaded with the realm you mount.
What it’s for
Section titled “What it’s for”A real OAuth2 / OpenID Connect / SAML server for local development, so your app delegates login, users, roles and clients to Keycloak instead of faking auth. Language-agnostic - your app talks to it over the standard protocols.
Add it
Section titled “Add it”At init, or to an existing workbench:
monoceros init acme --with-services=keycloakmonoceros add-service acme keycloakMounting your realm
Section titled “Mounting your realm”A fresh Keycloak is empty. To get your realm, clients and test users without clicking through the admin UI every time, keep a realm export in your project repo and mount it into Keycloak’s import directory - it is imported at startup. This is the one thing you need to wire up.
The curated entry ships a commented volumes: scaffold; uncomment it and
point it at the realm file in your cloned repo. Paths are relative to the
container root (where your repos land under projects/):
services: - name: keycloak image: quay.io/keycloak/keycloak:26.7 port: 8080 httpPort: 8080 command: start-dev --import-realm --proxy-headers=xforwarded # ... volumes: - projects/myapp/keycloak/realm.json:/opt/keycloak/data/import/myapp.json:roAfter monoceros apply acme, Keycloak comes up with the realm imported.
The --proxy-headers=xforwarded in the command lets Keycloak build its issuer
from the request’s host and scheme, so an OIDC login works both at
acme.localhost on the host (http) and over
monoceros share (https) on a phone.
Without it Keycloak would stamp the wrong scheme and the token exchange would
fail on the https client.
Several realms
Section titled “Several realms”Keycloak imports every *.json in the import directory. You can’t mount
two directories onto the same target, so mount each realm as its own file to a
distinct target name:
volumes: - projects/app-a/keycloak/realm.json:/opt/keycloak/data/import/app-a.json:ro - projects/app-b/keycloak/realm.json:/opt/keycloak/data/import/app-b.json:roImport is one-way
Section titled “Import is one-way”--import-realm seeds Keycloak’s database from the file once at startup -
it does not write back. Changes you make in the admin UI live in Keycloak’s
(ephemeral) database, not the file. To keep a change, export the realm
(admin UI -> Realm settings -> Action -> Partial export, or kc.sh export) and
overwrite the committed realm.json. The database re-seeds from the file on
every monoceros apply, so the committed realm stays the source of truth.
One thing to know about the export: a partial export never contains users. Clients, roles, groups, flows and the realm settings come back, so structure you clicked together is safe, but a user you created by hand is not. Users stay hand-written in the file.
Applying a changed realm while you work
Section titled “Applying a changed realm while you work”The startup import only fills an empty database, so editing realm.json
changes nothing by itself. monoceros apply acme recreates the service, which
is what wipes the database and re-seeds it from your file. A plain
monoceros stop acme and monoceros start acme keeps the database, so the old
realm stays.
While the workbench is up, apply the file to the running Keycloak instead. From
inside the workbench (monoceros shell acme), starting at the workspace root:
.monoceros/bin/keycloak-realm projects/myapp/keycloak/realm.jsonOr from your host, without entering the workbench first:
monoceros run acme -- .monoceros/bin/keycloak-realm projects/myapp/keycloak/realm.jsonmonoceros run starts in the workspace root
too, so the same relative paths work. The -- separates the workbench’s own
flags from the command you want run inside.
The tool ships with the Keycloak service, so it is there whenever the service is. It replaces the realm from the file, which is what makes the file win: a user you created at runtime, every open session, and the generated secrets of confidential clients are gone afterwards. The realm gets new signing keys, so you log in again. It reads the connection details from the workbench environment, which means it can only ever reach this workbench’s Keycloak.
Mounting a custom theme
Section titled “Mounting a custom theme”Custom themes live under /opt/keycloak/themes/<name>. Mount your theme folder
there, then set loginTheme / accountTheme to <name> in the realm:
volumes: - projects/myapp/keycloak/realm.json:/opt/keycloak/data/import/myapp.json:ro - projects/myapp/keycloak/theme:/opt/keycloak/themes/myappIn development mode themes are not cached, so edits show on reload without a restart.
Remove it
Section titled “Remove it”monoceros remove-service acme keycloakOr delete the service’s block from acme.yml by hand and re-run
monoceros apply acme - the yml is the source of truth.
The yml entry
Section titled “The yml entry”services: - name: keycloak # host name in the container image: quay.io/keycloak/keycloak:26.7 port: 8080 # in-container port (feeds `monoceros tunnel`) httpPort: 8080 # the port `monoceros share` offers to other devices env: KC_BOOTSTRAP_ADMIN_USERNAME: ${KC_BOOTSTRAP_ADMIN_USERNAME} # values live in acme.env KC_BOOTSTRAP_ADMIN_PASSWORD: ${KC_BOOTSTRAP_ADMIN_PASSWORD} restart: unless-stopped command: start-dev --import-realm --proxy-headers=xforwarded connectionEnv: # → the workspace env vars below URL: http://${host}:${port} HOST: ${host} PORT: ${port} USER: ${KC_BOOTSTRAP_ADMIN_USERNAME} PASSWORD: ${KC_BOOTSTRAP_ADMIN_PASSWORD} # volumes: # uncomment + edit to mount your realm/theme # - projects/<app>/keycloak/realm.json:/opt/keycloak/data/import/<app>.json:ro # - projects/<app>/keycloak/theme:/opt/keycloak/themes/<app>The admin credentials are ${VAR} references; the real values live in
acme.env (dev default admin / admin), so the yml stays shareable without
baking credentials in (see
Workbench configuration). There is no
persistent data dir - the dev database is ephemeral and re-seeds from the
mounted realm on every apply.
What your code sees
Section titled “What your code sees”Monoceros injects a connection env into the workspace container, prefixed with the service name:
| Variable | Value (defaults) |
|---|---|
KEYCLOAK_URL | http://keycloak:8080 |
KEYCLOAK_HOST | keycloak |
KEYCLOAK_PORT | 8080 |
KEYCLOAK_USER | admin |
KEYCLOAK_PASSWORD | admin |
KEYCLOAK_PUBLIC_URL | http://acme-keycloak.localhost |
The prefix is the service name, uppercased, so a second instance added with
--as=auth2 gets AUTH2_URL, … and never collides.