Keycloak start vs start-dev: Development and Production Modes

Tested on Red Hat Enterprise Linux 10.2 (Coughlan)
Package podman-5.8.2-5.el10_2.x86_64
quay.io/keycloak/keycloak:26.7.3
Applies to Any Linux host with Podman or Docker — the start and start-dev commands are the same inside the official Quay image
Privilege Normal user for rootless Podman; root when your site policy requires rootful containers
Lab environment Running Keycloak container — Install Keycloak with Podman
Scope Development vs production startup modes on Keycloak 26.7.x — HTTP, hostname, HTTPS, cache, and database behavior, plus the common bare-start failure — not a full PostgreSQL, TLS, or optimized-image walkthrough.
Related guides Keycloak environment variables
Keycloak HTTPS and TLS
Keycloak reverse proxy
Short on time?Beta Get the gist of this 10-minute read in seconds, then ask follow-up questions

In this article we will compare Keycloak start-dev and production start on the same 26.7.3 image.

start-dev starts Keycloak with development-friendly defaults such as HTTP, relaxed hostname handling, and local caching. start activates the production profile, where HTTP is disabled and hostname/TLS configuration is expected. Both commands can run the Keycloak build automatically when needed.


Keycloak start vs start-dev at a glance

Both commands launch the same Keycloak binary. The difference is which profile Keycloak applies. A profile is the set of default behaviors Keycloak uses for a particular mode. start-dev uses the development profile; start uses the production profile. Logs show this as Profile dev or Profile prod.

start-dev start
Mode Development Production
HTTP Enabled Disabled by default
Hostname Strict hostname resolution disabled Hostname configuration expected
HTTPS Not required Expected unless HTTP is explicitly enabled
Cache Local Distributed cache enabled
Theme/template cache Disabled / reduced for fast edits Production defaults
Intended use Local development and testing Production deployments

Both commands can automatically run kc.sh build. That is not the main difference between development and production mode.

Changing the command from start-dev to start does more than disable development conveniences. It activates the production profile, so settings that were optional in development (especially hostname and HTTPS) must now be configured appropriately.


What start-dev changes

start-dev turns on development conveniences so you can open the Admin Console without designing hostname, TLS, or clustering first. On Keycloak 26.7.3 the practical defaults include:

  • HTTP enabled on port 8080 so http://127.0.0.1:8080/admin/ works on the container host
  • Strict hostname resolution disabled so minor hostname mismatches during local testing do not block redirects
  • Local cache for Infinispan instead of clustering-oriented defaults
  • Theme and template caching reduced so theme edits show up quickly while you iterate

These defaults make it easier to test realms, clients, themes, and login flows locally without first configuring HTTPS or clustering. The server tells you plainly in the logs:

text
Running the server in development mode. DO NOT use this configuration in production.

Keycloak prints this warning because start-dev deliberately uses development-oriented defaults and should not be used for production deployments.


What changes when you use production start

Production start activates the prod profile. Keycloak then assumes you are publishing a real URL to users and browsers, not only opening localhost on the container host.

Production mode requires Keycloak to know how its public hostname should be resolved. The normal approach is to set KC_HOSTNAME (or hostname in keycloak.conf) to the name clients use in the browser. If your deployment intentionally uses dynamic hostname resolution, you can instead disable hostname strictness with hostname-strict=false.

You also need either HTTPS on Keycloak, or explicitly enabled HTTP when TLS terminates elsewhere.

Hostname v2 production rules require either --hostname or explicitly --hostname-strict=false.

For an actual production deployment, also use a supported production database rather than the embedded dev-file database. Keycloak 26.7.x may print a deprecation warning when the production profile still relies on that default:

text
WARNING: Usage of the default value for the db option in the production profile is deprecated. Please explicitly set the db instead.

Wire PostgreSQL in Configure Keycloak with PostgreSQL for durable storage. This article keeps the embedded database only to isolate the mode difference.


Why start fails when start-dev works

The fastest way to see the break is to run production start with no hostname or HTTP/TLS settings. Remove any leftover lab container so the name is free:

bash
podman rm -f keycloak-mode-lab 2>/dev/null || true

Start Keycloak with production start and no extra environment variables. Host port 8081 avoids colliding with a start-dev lab on 8080:

bash
podman run -d --name keycloak-mode-lab -p 127.0.0.1:8081:8080 quay.io/keycloak/keycloak:26.7.3 start

The container exits quickly. Read the logs:

bash
podman logs keycloak-mode-lab
output
Changes detected in configuration. Updating the server image.
WARNING: Usage of the default value for the db option in the production profile is deprecated. Please explicitly set the db instead.
WARNING: With HTTPS not enabled, `proxy-headers` unset, and `hostname-strict=false`, the server is running in an insecure context. Secure contexts are required for full functionality, including cross-origin cookies. Also if you are using a proxy, requests from the proxy to the server will fail CORS checks with 403s because the wrong origin will be determined. Make sure `proxy-headers` are configured properly.
Key material not provided to setup HTTPS. Please configure your keys/certificates, or if HTTPS access is not needed see the `http-enabled` option. If you meant to start the server in development mode, see the `start-dev` command.

In this test, Keycloak stopped because HTTPS key material was missing. Production mode also expects hostname configuration, which is why the working command below explicitly sets KC_HOSTNAME=localhost.

Fix production start for a local HTTP lab

For a lab-only production-profile container on loopback, set hostname and enable HTTP. localhost is used only because this example is intentionally bound to loopback on the same machine. In a real deployment, set KC_HOSTNAME to the public hostname users access.

Remove the failed container first:

bash
podman rm -f keycloak-mode-lab

Then run production start with explicit values:

bash
podman run -d \
  --name keycloak-mode-lab \
  -p 127.0.0.1:8081:8080 \
  -e KC_HOSTNAME=localhost \
  -e KC_HTTP_ENABLED=true \
  -e KC_BOOTSTRAP_ADMIN_USERNAME=admin \
  -e KC_BOOTSTRAP_ADMIN_PASSWORD='ChangeMe!2026' \
  quay.io/keycloak/keycloak:26.7.3 \
  start

Wait for the listening line in the logs:

bash
podman logs keycloak-mode-lab
output
Changes detected in configuration. Updating the server image.
WARNING: Usage of the default value for the db option in the production profile is deprecated. Please explicitly set the db instead.
Next time you run the server, just add --optimized to the command to ensure this build is used.
2026-09-18 17:27:09,968 INFO  [io.quarkus] (main) Keycloak 26.7.3 on JVM (powered by Quarkus 3.33.3.1) started in 53.673s. Listening on: http://0.0.0.0:8080
2026-09-18 17:27:09,970 INFO  [io.quarkus] (main) Profile prod activated.

Profile prod activated confirms you are no longer in start-dev. Listening on: http://0.0.0.0:8080 is normal inside the container; -p 127.0.0.1:8081:8080 still limits access to localhost on the host.

Confirm HTTP responds:

bash
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8081/
output
302

HTTP 302 is expected. Keycloak redirects to its welcome route even in production profile when HTTP is enabled for the lab.

IMPORTANT
KC_HTTP_ENABLED=true is for lab and reverse-proxy layouts where TLS terminates in front of Keycloak. Do not expose a production Keycloak instance on plain HTTP to the public internet.

Can production Keycloak use HTTP?

Yes, when TLS terminates before Keycloak, not when browsers hit Keycloak directly on port 8080 without encryption.

  • Direct TLS — Keycloak handles HTTPS itself with certificates and https-* settings
  • Edge TLS termination — users connect to the proxy over HTTPS, while the proxy connects to Keycloak over HTTP

With edge TLS termination, enable Keycloak HTTP and configure proxy headers so Keycloak still generates HTTPS URLs for users. A typical pattern is start --hostname https://my.keycloak.org --http-enabled true (or the matching KC_* environment variables) plus proxy-headers. Skipping both HTTPS material and http-enabled is what triggers the Key material not provided error in the previous section.

For certificate paths and proxy header combinations, see Keycloak HTTPS and TLS and Keycloak reverse proxy.


Does start-dev require the dev database?

No. Development mode and database choice are separate settings.

Combination What it means
start-dev + embedded dev-file Default tutorial layout
start-dev + PostgreSQL Still the development profile — development defaults while testing against an external database
start + embedded database Production profile with the embedded development database — suitable only for short tests, not durable production use
start + PostgreSQL Target layout for real deployments

You can run start-dev with PostgreSQL when you want development-mode defaults while testing against an external database. It is still development mode.

Likewise, start with PostgreSQL and KC_HTTP_ENABLED=true on localhost is still the production profile. Hostname rules, cache behavior, and HTTPS expectations follow start, not start-dev.


Cache differences

Keycloak uses Infinispan for realms, sessions, and related data. start-dev uses local cache and disables clustering. start uses the production distributed-cache setup.

For cluster topology and discovery options, see Configuring distributed caches.


Is start --optimized another Keycloak mode?

No. --optimized does not select development or production mode. start --optimized still runs production mode; it tells Keycloak to use a server that has already been built instead of performing the startup build/check.

For Dockerfile/Containerfile examples and kc.sh build, see Build an optimized Keycloak container.


Which mode should you use?

Scenario Use
Local learning and theme development start-dev
Testing production-mode behavior start
Staging or production start

Use start-dev when you deliberately want development defaults. Use start whenever you need to test or deploy production-mode behavior.


Common start vs start-dev problems

Symptom Likely cause Fix
Container exits immediately after switching to start Production mode without HTTPS or http-enabled Add KC_HTTP_ENABLED=true for lab HTTP, or configure TLS / reverse proxy
Key material not provided to setup HTTPS Bare start with no certificates and HTTP not enabled Enable HTTP only behind a proxy, or install TLS material
Redirect loops or wrong issuer URL KC_HOSTNAME missing or does not match the browser URL Set KC_HOSTNAME to the public host; behind a proxy, set KC_PROXY_HEADERS
Admin Console works on HTTP in dev but not prod Expected — prod disables plain HTTP by default Enable HTTP only for internal proxy paths, or terminate TLS in front
Dev mode warning in logs during a load test Container still on start-dev Switch to start with production hostname/TLS/database settings
Warning about default db in production profile Embedded database under prod profile Move to PostgreSQL with KC_DB=postgres

Summary

start-dev runs Keycloak with development defaults: HTTP enabled, relaxed hostname handling, local caching, and reduced theme caching. start runs the production profile, where hostname and secure transport must be configured explicitly.

Database choice is separate from the startup mode, and --optimized is a startup optimization rather than another mode. For production deployments, use start with the appropriate hostname, TLS/proxy, database, and cache configuration.


References


Frequently Asked Questions

Is start-dev the same as production mode with HTTP enabled?

No. start-dev activates the development profile with relaxed hostname handling, local cache, and other conveniences. Production start with KC_HTTP_ENABLED=true still runs the prod profile and expects you to configure hostname and proxy behavior for real URLs.

Can I use PostgreSQL with start-dev?

Yes. Set KC_DB=postgres and the JDBC environment variables while keeping start-dev as the command. You get a real database but still run under the development profile, not a production deployment.

Why does start fail after my start-dev lab worked?

start-dev enables HTTP and relaxes hostname checks by default. Production start expects hostname configuration and either HTTPS, or explicitly enabled HTTP for a deliberate local or reverse-proxy setup. Change the command and the environment together.

Does switching from start-dev to start migrate my realms automatically?

No. Mode is a startup command and profile choice. Realm data moves with the database or container filesystem you configured. Moving to production usually means start, PostgreSQL, and correct hostname URLs, not only renaming the command.

Finished reading?Beta Get the key takeaways in seconds, then ask follow-up questions
Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with 15+ years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive experience, he excels across development, DevOps, networking, and security, delivering robust and efficient solutions for diverse projects.

  • Go (programming language)
  • Python (programming language)
  • DevOps
  • Computer Security
  • Cloud Computing
  • Kubernetes
  • Linux
  • Ansible (software)