Is Keycloak Production Ready? A Practical Checklist

Guilliano Molaire Guilliano Molaire Updated May 8, 2026 8 min read

Last updated: March 2026

Keycloak works out of the box for development. Getting it production-ready is a different story. The default configuration uses an H2 in-memory database, has no clustering, runs over HTTP, and exposes the admin console to the world. None of that belongs in production.

This checklist covers every configuration area you need to address before deploying Keycloak to handle real users and real traffic. Each item includes the specific configuration changes required, not just a recommendation to “consider” doing something.

1. Database: Switch to PostgreSQL

Keycloak’s embedded H2 database is for development only. It does not support clustering, has no durability guarantees, and will lose data on restart unless configured for file-based persistence.

Use PostgreSQL. It is the best-supported database for Keycloak and the one tested most extensively by the Keycloak team.

Configuration

# Keycloak database configuration
KC_DB=postgres
KC_DB_URL=jdbc:postgresql://db-host:5432/keycloak
KC_DB_USERNAME=keycloak
KC_DB_PASSWORD=<strong-password-from-secrets-manager>

PostgreSQL Tuning

For a Keycloak database handling up to 10,000 concurrent sessions:

# postgresql.conf
max_connections = 200
shared_buffers = 2GB
effective_cache_size = 6GB
work_mem = 16MB
maintenance_work_mem = 512MB
wal_level = replica
max_wal_senders = 3
checkpoint_completion_target = 0.9
random_page_cost = 1.1   # For SSD storage

Set Keycloak’s connection pool size to match:

KC_DB_POOL_INITIAL_SIZE=25
KC_DB_POOL_MIN_SIZE=25
KC_DB_POOL_MAX_SIZE=100

Backups

Configure automated PostgreSQL backups using pg_dump or continuous archiving with WAL-E/pgBackRest:

# Daily logical backup
pg_dump -Fc -h db-host -U keycloak keycloak > /backups/keycloak_$(date +%Y%m%d).dump

# Restore
pg_restore -h db-host -U keycloak -d keycloak /backups/keycloak_20260414.dump

Test your restore procedure regularly. A backup you have never tested is not a backup.

For a quick local setup using PostgreSQL, use the Keycloak Docker Compose Generator which generates a production-like compose file with PostgreSQL by default.

2. Clustering and High Availability

A single Keycloak instance is a single point of failure. In production, run at least two nodes behind a load balancer.

Infinispan Configuration

Keycloak uses Infinispan for distributed caching. In production, you need to configure cache transport so nodes can discover each other.

For Kubernetes deployments, use DNS_PING:

<!-- conf/cache-ispn.xml -->
<transport stack="kubernetes" />
# Environment variables for Kubernetes discovery
KC_CACHE_STACK=kubernetes
JAVA_OPTS_KC_HEAP="-XX:MaxRAMPercentage=70.0"

For non-Kubernetes deployments, use JDBC_PING (stores discovery info in the database):

KC_CACHE_STACK=jdbc-ping

Load Balancer Configuration

Your load balancer must support sticky sessions (session affinity) for optimal performance, though Keycloak will work without them via the distributed cache:

# Nginx example
upstream keycloak {
    ip_hash;  # Sticky sessions
    server keycloak-1:8080;
    server keycloak-2:8080;
}

Health check endpoint:

location /health {
    proxy_pass http://keycloak/health/ready;
}

Session Configuration

Tune session lifetimes in the Keycloak admin console or via the Admin REST API:

Setting Recommended Value Notes
SSO Session Idle 30 minutes Time before idle sessions expire
SSO Session Max 10 hours Maximum session lifetime
Access Token Lifespan 5 minutes Keep short for security
Client Session Idle 30 minutes Client-level session timeout

For details on session management strategies, see Skycloak’s Session Management feature.

3. TLS Termination

Never run Keycloak over plain HTTP in production. There are two approaches:

Option A: TLS at the Reverse Proxy (Recommended)

Terminate TLS at your reverse proxy (Nginx, HAProxy, or a cloud load balancer) and proxy to Keycloak over HTTP:

KC_PROXY_HEADERS=xforwarded
KC_HTTP_ENABLED=true
KC_HOSTNAME=auth.example.com
# Nginx TLS termination
server {
    listen 443 ssl http2;
    server_name auth.example.com;

    ssl_certificate /etc/ssl/certs/auth.example.com.pem;
    ssl_certificate_key /etc/ssl/private/auth.example.com.key;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;

    location / {
        proxy_pass http://keycloak;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Host $host;
        proxy_buffer_size 128k;
        proxy_buffers 4 256k;
    }
}

For more on reverse proxy configuration, see How to Run Keycloak Behind a Reverse Proxy.

Option B: TLS on Keycloak Directly

KC_HTTPS_CERTIFICATE_FILE=/opt/keycloak/certs/tls.crt
KC_HTTPS_CERTIFICATE_KEY_FILE=/opt/keycloak/certs/tls.key
KC_HOSTNAME=auth.example.com

4. Admin Console Security

The admin console is the keys to the kingdom. Protect it aggressively.

Restrict Admin Console Access

Use Keycloak’s hostname configuration to serve the admin console on a separate URL or disable it on public-facing nodes:

# Serve admin on a different hostname
KC_HOSTNAME=auth.example.com
KC_HOSTNAME_ADMIN=admin.internal.example.com

Alternatively, use network-level restrictions. In Skycloak, you can use the configurable WAF to restrict admin console access by IP. For self-hosted setups, block at the reverse proxy:

location /admin {
    allow 10.0.0.0/8;
    deny all;
    proxy_pass http://keycloak;
}

Master Realm Hardening

The master realm controls all other realms. Lock it down:

  1. Change the default admin password immediately.
  2. Enable MFA for all master realm admin accounts.
  3. Create separate admin accounts per person — no shared credentials.
  4. Set aggressive session timeouts (5 minutes idle, 1 hour max).

See Secure Your Keycloak’s Master Realm for a detailed walkthrough.

5. Hostname Configuration

Keycloak embeds the hostname in tokens, SAML metadata, and OIDC discovery documents. Getting this wrong causes redirect failures and token validation errors.

# Production hostname configuration
KC_HOSTNAME=auth.example.com
KC_HOSTNAME_STRICT=true
KC_HOSTNAME_BACKCHANNEL_DYNAMIC=false

Setting KC_HOSTNAME_STRICT=true ensures Keycloak rejects requests that do not match the configured hostname. This prevents host header injection attacks.

If you encounter hostname-related issues, see Keycloak Redirect URI Mismatch Troubleshooting.

6. Monitoring and Observability

Health Endpoints

Keycloak exposes health endpoints that your orchestrator should use:

# Liveness (is the process running?)
curl http://keycloak:8080/health/live

# Readiness (can it accept traffic?)
curl http://keycloak:8080/health/ready

# Full health with details
curl http://keycloak:8080/health

Enable these in your Keycloak build:

KC_HEALTH_ENABLED=true

Metrics

Keycloak can expose Prometheus-compatible metrics:

KC_METRICS_ENABLED=true

Metrics are available at /metrics. Key metrics to monitor:

Metric Alert Threshold Notes
keycloak_request_duration_seconds p99 > 2s Login latency
vendor_cache_manager_default_cluster_size < expected nodes Cluster split
jvm_memory_used_bytes > 80% of max Memory pressure
db_pool_active_count > 80% of max Connection pool exhaustion

For dashboards and alerting, see how Skycloak’s Insights feature provides built-in monitoring for managed clusters.

Log Aggregation

Configure structured JSON logging for easier parsing:

KC_LOG=console
KC_LOG_CONSOLE_FORMAT=json
KC_LOG_LEVEL=info
KC_LOG_LEVEL_ORG_KEYCLOAK=info

Send logs to your aggregation platform (ELK, Loki, Datadog). Key events to alert on:

  • Failed admin login attempts
  • Client authentication failures
  • Token validation errors
  • Configuration changes

Keycloak’s event system captures user and admin events. Configure it in the realm settings to write events to the database or an external system. See Auditing in Keycloak: How to Catch Them All for event configuration and Skycloak’s Audit Logs feature for managed audit capabilities.

7. Rate Limiting and Brute Force Protection

Brute Force Protection

Enable brute force detection in each realm:

{
  "bruteForceProtected": true,
  "permanentLockout": false,
  "maxFailureWaitSeconds": 900,
  "minimumQuickLoginWaitSeconds": 60,
  "waitIncrementSeconds": 60,
  "quickLoginCheckMilliSeconds": 1000,
  "maxDeltaTimeSeconds": 43200,
  "failureFactor": 5
}

This locks accounts after 5 failed attempts with increasing wait times.

Rate Limiting at the Reverse Proxy

Keycloak does not have built-in request rate limiting. Implement it at the reverse proxy:

# Nginx rate limiting
limit_req_zone $binary_remote_addr zone=keycloak_login:10m rate=10r/s;

location /realms/*/protocol/openid-connect/token {
    limit_req zone=keycloak_login burst=20 nodelay;
    proxy_pass http://keycloak;
}

8. Theme Customization

Do not use the default Keycloak theme in production. At minimum, add your logo and brand colors so users know they are on a legitimate login page.

Create a custom theme JAR or mount a theme directory:

# Mount custom theme
docker run -v /path/to/themes:/opt/keycloak/themes quay.io/keycloak/keycloak:26.1.0 start

For a complete theming guide, see Keycloak Theme Customization: From Default to Branded Login. Skycloak’s Branding feature handles theme deployment and management for managed clusters.

9. Build Optimization

Keycloak introduced optimized builds in version 17. In production, always use a pre-built image:

FROM quay.io/keycloak/keycloak:26.1.0 AS builder

ENV KC_DB=postgres
ENV KC_HEALTH_ENABLED=true
ENV KC_METRICS_ENABLED=true
ENV KC_FEATURES=token-exchange,admin-fine-grained-authz

RUN /opt/keycloak/bin/kc.sh build

FROM quay.io/keycloak/keycloak:26.1.0
COPY --from=builder /opt/keycloak/ /opt/keycloak/

ENTRYPOINT ["/opt/keycloak/bin/kc.sh"]
CMD ["start", "--optimized"]

The --optimized flag skips the build phase at startup, reducing startup time significantly. This matters for Kubernetes rolling deployments where startup speed affects availability.

10. JVM Tuning

Keycloak runs on the JVM. Default settings are conservative. Tune for production:

# JVM settings
JAVA_OPTS_KC_HEAP="-XX:MaxRAMPercentage=70.0 -XX:InitialRAMPercentage=50.0"
KC_HTTP_POOL_MAX_THREADS=200

For a 4 GB container:

  • Heap: ~2.8 GB (70% of RAM)
  • Metaspace: Managed automatically by the JVM
  • Thread stack: 1 MB per thread (200 threads = 200 MB)
  • Remaining: OS overhead, off-heap buffers

Do not set -Xms and -Xmx directly when using percentage-based settings. They conflict.

11. Feature Flags

Keycloak has several features that are disabled by default. Enable only what you need:

# Enable specific features
KC_FEATURES=token-exchange,admin-fine-grained-authz,declarative-user-profile

# Disable features you do not need
KC_FEATURES_DISABLED=impersonation

Common features to consider:

Feature Use Case
token-exchange Token exchange between services
admin-fine-grained-authz Delegate admin permissions per realm
declarative-user-profile Custom user profile validation
organization Multi-tenancy with organizations

12. Realm Configuration Export

Always keep a version-controlled export of your realm configuration. This is your disaster recovery plan:

# Export realm configuration
/opt/keycloak/bin/kc.sh export 
  --dir /opt/keycloak/data/export 
  --realm myrealm 
  --users realm_file

Store exports in git or your artifact repository. Automate exports as part of your CI/CD pipeline.

For infrastructure-as-code approaches, see Using Terraform to Set Up and Configure Keycloak.

The Complete Checklist

Use this as a final review before going live:

  • [ ] PostgreSQL database with connection pooling and automated backups
  • [ ] At least 2 Keycloak nodes with Infinispan clustering configured
  • [ ] TLS termination with TLS 1.2+ and strong cipher suites
  • [ ] Admin console restricted by network or separate hostname
  • [ ] Master realm hardened with MFA and short session timeouts
  • [ ] Hostname configuration set with KC_HOSTNAME_STRICT=true
  • [ ] Health endpoints enabled and integrated with your orchestrator
  • [ ] Prometheus metrics enabled and dashboarded
  • [ ] Structured JSON logging shipped to your aggregation platform
  • [ ] Brute force protection enabled in all realms
  • [ ] Rate limiting at the reverse proxy
  • [ ] Custom login theme deployed
  • [ ] Optimized Docker image built with only required features
  • [ ] JVM memory and thread pool tuned for expected load
  • [ ] Realm configuration exported and version-controlled
  • [ ] Session lifetimes reviewed and tightened
  • [ ] Disaster recovery procedure tested

When Self-Hosting Is Too Much

This checklist has 17 items, each with sub-tasks. Maintaining a production Keycloak cluster requires ongoing work: security patches, version upgrades, certificate rotation, database maintenance, cache tuning, and capacity planning.

If that operational burden is more than your team can absorb, consider a managed option. Skycloak handles all of the items on this checklist — database management, clustering, TLS, monitoring, backups, and security hardening — so your team can focus on building your application rather than maintaining infrastructure. See our SLA for uptime guarantees and security practices for compliance details.

Check Skycloak pricing to compare the cost against self-hosting.

Guilliano Molaire
Written by Guilliano Molaire Founder

Guilliano is the founder of Skycloak and a cloud infrastructure specialist with deep expertise in product development and scaling SaaS products. He discovered Keycloak while consulting on enterprise IAM and built Skycloak to make managed Keycloak accessible to teams of every size.

Ready to simplify your authentication?

Deploy production-ready Keycloak in minutes. Unlimited users, flat pricing, no SSO tax.

© 2026 Skycloak. All Rights Reserved. Design by Yasser Soliman