Exporting Keycloak Resources
The Keycloak Operator includes an export command that extracts resources from an existing Keycloak instance and generates Kubernetes CRD manifests. This is useful for:
- Migration: Moving from manual Keycloak configuration to operator-managed resources
- Discovery: Generating manifests from an existing Keycloak setup
- Backup: Creating declarative representations of your Keycloak configuration
Quick Start
Export a realm to stdout:
docker run --rm ghcr.io/hostzero-gmbh/keycloak-operator export \
--url https://keycloak.example.com \
--username admin \
--password "$KEYCLOAK_PASSWORD" \
--realm my-realm
Running the Export Command
The export command is part of the operator binary. Either download a prebuilt binary or use the container image.
Release Binary
Prebuilt binaries for Linux, macOS and Windows (amd64/arm64) are attached to every GitHub release, together with a checksums.txt:
VERSION=0.14.0
curl -fsSLO "https://github.com/Hostzero-GmbH/keycloak-operator/releases/download/v${VERSION}/keycloak-operator_${VERSION}_$(uname -s | tr '[:upper:]' '[:lower:]')_$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/').tar.gz"
tar -xzf keycloak-operator_${VERSION}_*.tar.gz
./keycloak-operator_${VERSION}_*/keycloak-operator export --help
The binary reads ~/.kube/config directly, so --from-instance works without volume mounts. The examples below use Docker; replace docker run --rm ghcr.io/hostzero-gmbh/keycloak-operator with the binary path to run them locally.
Container Image
The export command is also included in the operator container image. Run it via Docker:
Direct Connection Mode
Connect directly to a Keycloak instance using URL and credentials:
docker run --rm ghcr.io/hostzero-gmbh/keycloak-operator export \
--url https://keycloak.example.com \
--username admin \
--password "$KEYCLOAK_PASSWORD" \
--realm my-realm \
--target-namespace production \
--instance-ref keycloak-prod
From Existing KeycloakInstance CR
If you already have the operator deployed with a KeycloakInstance configured, you can reuse those connection details:
docker run --rm -v ~/.kube:/root/.kube ghcr.io/hostzero-gmbh/keycloak-operator export \
--from-instance my-keycloak \
--namespace keycloak-operator \
--realm my-realm
This reads the connection URL and credentials from the existing KeycloakInstance CR and its associated Secret.
For cluster-scoped instances:
docker run --rm -v ~/.kube:/root/.kube ghcr.io/hostzero-gmbh/keycloak-operator export \
--from-cluster-instance my-cluster-keycloak \
--realm my-realm
Output Options
Stdout (Default)
Output all manifests as multi-document YAML, suitable for piping:
docker run --rm ghcr.io/hostzero-gmbh/keycloak-operator export \
--url https://keycloak.example.com \
--username admin \
--password "$KEYCLOAK_PASSWORD" \
--realm my-realm \
> manifests.yaml
Or apply directly:
docker run --rm ghcr.io/hostzero-gmbh/keycloak-operator export \
--url https://keycloak.example.com \
--username admin \
--password "$KEYCLOAK_PASSWORD" \
--realm my-realm \
| kubectl apply -f -
Single File
Write all manifests to a single file:
docker run --rm -v $(pwd):/output ghcr.io/hostzero-gmbh/keycloak-operator export \
--url https://keycloak.example.com \
--username admin \
--password "$KEYCLOAK_PASSWORD" \
--realm my-realm \
--output /output/manifests.yaml
Directory Structure
Create an organized directory hierarchy:
docker run --rm -v $(pwd)/manifests:/output ghcr.io/hostzero-gmbh/keycloak-operator export \
--url https://keycloak.example.com \
--username admin \
--password "$KEYCLOAK_PASSWORD" \
--realm my-realm \
--output-dir /output
This creates:
manifests/
realm.yaml
clients/
my-app.yaml
another-client.yaml
users/
john-doe.yaml
groups/
admin-group.yaml
roles/
custom-role.yaml
...
Filtering Resources
Include Specific Types
Export only certain resource types:
docker run --rm ghcr.io/hostzero-gmbh/keycloak-operator export \
--url https://keycloak.example.com \
--username admin \
--password "$KEYCLOAK_PASSWORD" \
--realm my-realm \
--include clients,users,groups
Exclude Types
Skip certain resource types:
docker run --rm ghcr.io/hostzero-gmbh/keycloak-operator export \
--url https://keycloak.example.com \
--username admin \
--password "$KEYCLOAK_PASSWORD" \
--realm my-realm \
--exclude role-mappings,protocol-mappers
Resource Types
Available resource types for filtering:
| Type | Description |
|---|---|
realm | The realm itself |
clients | OAuth2/OIDC clients |
client-scopes | Client scopes |
users | User accounts |
groups | User groups |
roles | Realm and client roles |
role-mappings | Role assignments to users/groups |
identity-providers | External identity providers (SAML, OIDC, etc.). Org-linked IdPs emit spec.organizationRef instead of definition.organizationId. |
components | LDAP federation, key providers, etc. |
protocol-mappers | Token claim mappers |
organizations | Organizations (Keycloak 26+) |
A user’s group memberships are not part of the Keycloak user representation. When both users and groups are exported, each KeycloakUser gets spec.groups with the memberships as absolute paths (/developers, /engineering/team-alpha), which is how KeycloakUser reconciles them; a user without memberships leaves spec.groups unset. Apply the exported groups before the users, otherwise the operator skips the unknown groups until its next sync.
Sub-components, such as the mappers of an LDAP provider, are exported with parentComponentRef pointing at their parent’s resource, and their resource name is prefixed with the parent’s. A sub-component whose parent is not exported (for example a key provider skipped by --skip-defaults) is left out rather than exported with a dangling reference.
The exported KeycloakRealm carries the realm’s own attributes only. Nested collections such as clients, identityProviders or authenticationFlows become resources of their own kinds and are stripped from the realm definition, which would reject them (see Nested Resources).
Identity providers linked to an organization are exported with spec.organizationRef pointing at the generated KeycloakOrganization (named from the organization name). The links are read from each organization’s identity-provider list (GET /admin/realms/{realm}/organizations/{id}/identity-providers), which is the only place Keycloak 26.8 reports them; the organizationId field older versions return on the identity provider itself, and the organizationLinks list Keycloak 26.8 returns, are ignored and stripped from definition so the exported manifest applies without being rejected. An identity provider that Keycloak 26.8 links to several organizations is exported with the first one in organization-name order, since organizationRef holds a single reference.
Skip Built-in Resources
By default, Keycloak’s built-in resources are skipped (--skip-defaults=true). These include:
- Default clients:
account,account-console,admin-cli,broker,realm-management,security-admin-console - Default client scopes:
address,email,offline_access,phone,profile,roles,web-origins, etc. - Default roles:
offline_access,uma_authorization,default-roles-{realm} - Service account users (prefixed with
service-account-)
To include built-in resources:
docker run --rm ghcr.io/hostzero-gmbh/keycloak-operator export \
--url https://keycloak.example.com \
--username admin \
--password "$KEYCLOAK_PASSWORD" \
--realm my-realm \
--skip-defaults=false
Manifest Generation Options
Target Namespace
Set the namespace for generated manifests:
docker run --rm ghcr.io/hostzero-gmbh/keycloak-operator export \
--url https://keycloak.example.com \
--username admin \
--password "$KEYCLOAK_PASSWORD" \
--realm my-realm \
--target-namespace production
Instance Reference
Set the KeycloakInstance reference for all generated resources:
docker run --rm ghcr.io/hostzero-gmbh/keycloak-operator export \
--url https://keycloak.example.com \
--username admin \
--password "$KEYCLOAK_PASSWORD" \
--realm my-realm \
--instance-ref keycloak-prod
Realm Reference
Override the realm reference name (defaults to the sanitized realm name):
docker run --rm ghcr.io/hostzero-gmbh/keycloak-operator export \
--url https://keycloak.example.com \
--username admin \
--password "$KEYCLOAK_PASSWORD" \
--realm my-realm \
--realm-ref production-realm
Security Considerations
Secrets Are Never Exported
The export command never exports secrets. This includes:
- Client secrets
- User passwords
- Identity provider client secrets
- LDAP bind credentials (
config.bindCredential)
You must create these secrets separately and set the matching reference on the CR (clientSecretRef, configSecretRef, userSecret). Export does not emit configSecretRef; wire it after creating the Secret. See Secret references.
Password Handling
Pass passwords via environment variable instead of command line:
export KEYCLOAK_PASSWORD="your-password"
docker run --rm -e KEYCLOAK_PASSWORD ghcr.io/hostzero-gmbh/keycloak-operator export \
--url https://keycloak.example.com \
--username admin \
--password "$KEYCLOAK_PASSWORD" \
--realm my-realm
Example Workflow: Migrating to the Operator
- Export existing configuration:
docker run --rm -v $(pwd)/manifests:/output ghcr.io/hostzero-gmbh/keycloak-operator export \
--url https://keycloak.example.com \
--username admin \
--password "$KEYCLOAK_PASSWORD" \
--realm production \
--target-namespace keycloak \
--instance-ref keycloak-prod \
--output-dir /output
- Review and customize manifests:
# Review generated files
ls -la manifests/
# Edit as needed (add client secrets, customize settings)
vim manifests/clients/my-app.yaml
- Create required secrets:
# Create client secrets referenced by the manifests
kubectl create secret generic my-app-credentials \
--namespace keycloak \
--from-literal=client-secret=your-client-secret
# LDAP bind credentials (then set spec.configSecretRef on the exported Component)
kubectl create secret generic ldap-credentials \
--namespace keycloak \
--from-literal=bindCredential=your-ldap-password
- Deploy the operator (if not already installed):
helm install keycloak-operator oci://ghcr.io/hostzero-gmbh/charts/keycloak-operator \
--namespace keycloak-operator \
--create-namespace
- Create the KeycloakInstance:
# Create credentials secret
kubectl create secret generic keycloak-admin \
--namespace keycloak \
--from-literal=username=admin \
--from-literal=password=your-admin-password
# Apply instance configuration
kubectl apply -f - <<EOF
apiVersion: keycloak.hostzero.com/v1beta1
kind: KeycloakInstance
metadata:
name: keycloak-prod
namespace: keycloak
spec:
baseUrl: https://keycloak.example.com
auth:
passwordGrant:
secretRef:
name: keycloak-admin
EOF
- Apply the exported manifests:
kubectl apply -f manifests/
Command Reference
Usage: keycloak-operator export [options]
Connection Options (choose one mode):
--url Keycloak server URL
--username Admin username
--password Admin password (or use KEYCLOAK_PASSWORD env var)
--from-instance Name of KeycloakInstance CR
--from-cluster-instance Name of ClusterKeycloakInstance CR
--namespace Namespace of KeycloakInstance
Export Options:
--realm Realm to export (required)
Output Options:
--output Output file path (default: stdout)
--output-dir Output directory (creates file structure)
Manifest Options:
--target-namespace Namespace for generated manifests (default: "default")
--instance-ref KeycloakInstance name to reference
--realm-ref KeycloakRealm name to reference
Filtering Options:
--include Resource types to include (comma-separated)
--exclude Resource types to exclude (comma-separated)
--skip-defaults Skip built-in Keycloak resources (default: true)
General Options:
--verbose Enable verbose output