Keycloak in Docker reports port already in use

I have run into an issue where Keycloak reports that the port it is attempting to start the http server on is already in use:

  • The port in question is the http port, which I’ve set using the KC_HTTP_PORT environment variable, although it is the default of 8080. - I’ve also tried changing the port, with the same result.
  • I’ve verified that none of the ports I’ve attempted to use are in use on the host, using lsof -nP -iTCP -sTCP:LISTEN; the only time anything is listening on these ports is when I start the container.
  • I have run into the same issue using a standard/prebuild container (quay.io/keycloak/keycloak:26.2) and with a customized build.

It’s beyond me, at this point, where the issue lies and I would welcome any thoughts on potential causes.

My Docker compose:

services:
  postgres:
    container_name: keycloak-database
    hostname: keycloak-database
    image: postgres:17-alpine
    env_file:
      - .env
    environment:
      POSTGRES_DB: ${KC_DB_URL_DATABASE}
      POSTGRES_USER: ${KC_DB_USERNAME}
      POSTGRES_PASSWORD: ${KC_DB_PASSWORD}
      POSTGRES_PORT: ${KC_DB_URL_PORT}
      TZ: America/New_York
    volumes:
      - /docker/data/keycloak/pg_data:/var/lib/postgresql/data/
    ports:
      - 5432:5432
    healthcheck:
      test: [ "CMD", "pg_isready", "-q", "-d", "keycloak_db", "-U", "keycloak" ]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 5s
    restart: unless-stopped

  keycloak:
    container_name: keycloak-server
    hostname: keycloak-server
    env_file:
      - .env
    build:
      dockerfile: Dockerfile
      args:
        KC_HEALTH_ENABLED: ${KC_HEALTH_ENABLED}
        KC_METRICS_ENABLED: ${KC_METRICS_ENABLED}
        KC_HOSTNAME: ${KC_HOSTNAME}
        KC_HOSTNAME_ADMIN: ${KC_HOSTNAME_ADMIN}
        KC_DB: ${KC_DB_VENDOR}
        KC_DB_URL_HOST: ${KC_DB_URL_HOST}
        KC_DB_URL_DATABASE: ${KC_DB_URL_DATABASE}
        KC_DB_URL_PORT: ${KC_DB_URL_PORT}
        KC_DB_USERNAME: ${KC_DB_USERNAME}
        KC_DB_PASSWORD: ${KC_DB_PASSWORD}
        KC_DB_SCHEMA: ${KC_DB_SCHEMA}
        KC_HTTP_ENABLED: ${KC_HTTP_ENABLED}
        KC_HTTP_HOST: ${KC_HTTP_HOST}
        KC_HTTP_PORT: ${KC_HTTP_PORT}
        KC_HTTP_MAX_QUEUED_REQUESTS: ${KC_HTTP_MAX_QUEUED_REQUESTS}
        KC_HTTP_RELATIVE_PATH: ${KC_HTTP_RELATIVE_PATH}
        KC_PROXY_HEADERS: ${KC_PROXY_HEADERS}
        KC_PROXY_TRUSTED_ADDRESSES: ${KC_PROXY_TRUSTED_ADDRESSES}
        KC_BOOTSTRAP_ADMIN_USERNAME: ${KC_BOOTSTRAP_ADMIN_USERNAME}
        KC_BOOTSTRAP_ADMIN_PASSWORD: ${KC_BOOTSTRAP_ADMIN_PASSWORD}
    # image: quay.io/keycloak/keycloak:26.2
    depends_on:
      postgres:
        condition: service_healthy
    environment:
      KC_VERSION: ${KC_VERSION}
      KC_HEALTH_ENABLED: ${KC_HEALTH_ENABLED}
      KC_METRICS_ENABLED: ${KC_METRICS_ENABLED}
      KC_HOSTNAME: ${KC_HOSTNAME}
      KC_HOSTNAME_ADMIN: ${KC_HOSTNAME_ADMIN}
      KC_DB: ${KC_DB_VENDOR}
      KC_DB_URL_HOST: ${KC_DB_URL_HOST}
      KC_DB_URL_PORT: ${KC_DB_URL_PORT}
      KC_DB_URL_DATABASE: ${KC_DB_URL_DATABASE}
      KC_DB_USERNAME: ${KC_DB_USERNAME}
      KC_DB_PASSWORD: ${KC_DB_PASSWORD}
      KC_PROXY_HEADERS: ${KC_PROXY_HEADERS}
      KC_PROXY_TRUSTED_ADDRESSES: ${KC_PROXY_TRUSTED_ADDRESSES}
      KC_HTTP_ENABLED: ${KC_HTTP_ENABLED}
      KC_HTTP_HOST: ${KC_HTTP_HOST}
      KC_HTTP_PORT: ${KC_HTTP_PORT}
      KC_HTTP_MAX_QUEUED_REQUESTS: ${KC_HTTP_MAX_QUEUED_REQUESTS}
      KC_HTTP_RELATIVE_PATH: ${KC_HTTP_RELATIVE_PATH}
      KC_BOOTSTRAP_ADMIN_USERNAME: ${KC_BOOTSTRAP_ADMIN_USERNAME}
      KC_BOOTSTRAP_ADMIN_PASSWORD: ${KC_BOOTSTRAP_ADMIN_PASSWORD}
    volumes:
      - /docker/data/keycloak/kc_data:/data
    ports:
      - 8080:8080 # http
      - 9000:9000 # management interface

Dockerfile:

ARG KC_VERSION=26.2
FROM quay.io/keycloak/keycloak:${KC_VERSION} AS builder

ARG KC_VERSION
ARG KC_HEALTH_ENABLED
ARG KC_METRICS_ENABLED

ARG KC_HOSTNAME
ARG KC_HOSTNAME_ADMIN

ARG KC_DB 
ARG KC_DB_URL_HOST
ARG KC_DB_URL_DATABASE
ARG KC_DB_URL_PORT
ARG KC_DB_USERNAME 
ARG KC_DB_PASSWORD
ARG KC_DB_SCHEMA

ARG KC_PROXY_HEADERS
ARG KC_PROXY_TRUSTED_ADDRESSES

ARG KC_HTTP_ENABLED
ARG KC_HTTP_HOST
ARG KC_HTTP_PORT
ARG KC_HTTP_MAX_QUEUED_REQUESTS
ARG KC_HTTP_RELATIVE_PATH

ARG KC_BOOTSTRAP_ADMIN_USERNAME
ARG KC_BOOTSTRAP_ADMIN_PASSWORD

ENV KC_HEALTH_ENABLED ${KC_HEALTH_ENABLED}
ENV KC_METRICS_ENABLED ${KC_METRICS_ENABLED}

ENV KC_HOSTNAME ${KC_HOSTNAME}
ENV KC_HOSTNAME_ADMIN ${KC_HOSTNAME_ADMIN}

ENV KC_DB ${KC_DB}
ENV KC_DB_URL_HOST ${KC_DB_URL_HOST}
ENV KC_DB_URL_PORT ${KC_DB_URL_PORT}
ENV KC_DB_URL_DATABASE ${KC_DB_URL_DATABASE}
ENV KC_DB_USERNAME ${KC_DB_USERNAME}
ENV KC_DB_PASSWORD ${KC_DB_PASSWORD}
ENV KC_DB_SCHEMA ${KC_DB_SCHEMA}

ENV KC_PROXY_HEADERS ${KC_PROXY_HEADERS}
ENV KC_PROXY_TRUSTED_ADDRESSES ${KC_PROXY_TRUSTED_ADDRESSES}

ENV KC_HTTP_ENABLED ${KC_HTTP_ENABLED}
ENV KC_HTTP_HOST ${KC_HTTP_HOST}
ENV KC_HTTP_PORT ${KC_HTTP_PORT}
ENV KC_HTTP_MAX_QUEUED_REQUESTS ${KC_HTTP_MAX_QUEUED_REQUESTS}
ENV KC_HTTP_RELATIVE_PATH ${KC_HTTP_RELATIVE_PATH}

ENV KC_BOOTSTRAP_ADMIN_USERNAME ${KC_BOOTSTRAP_ADMIN_USERNAME}
ENV KC_BOOTSTRAP_ADMIN_PASSWORD ${KC_BOOTSTRAP_ADMIN_PASSWORD}

WORKDIR /opt/keycloak
# for demonstration purposes only, please make sure to use proper certificates in production instead
#RUN keytool -genkeypair -storepass password -storetype PKCS12 -keyalg RSA -keysize 2048 -dname "CN=server" -alias server -ext "SAN:c=DNS:localhost,IP:127.0.0.1" -keystore conf/server.keystore
RUN /opt/keycloak/bin/kc.sh build

FROM quay.io/keycloak/keycloak:${KC_VERSION}
COPY --from=builder /opt/keycloak/ /opt/keycloak/

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

.env:

KC_DB_VENDOR=postgres
KC_DB_USERNAME=keycloak
KC_DB_PASSWORD=my-password
KC_DB_URL_HOST=keycloak-database
KC_DB_URL_DATABASE=keycloak_db
KC_DB_URL_PORT=5432
KC_DB_SCHEMA=public

KC_VERSION=26.2
KC_HOSTNAME=auth.my.tld
KC_HOSTNAME_ADMIN=auth-admin.my.tld
KC_HEALTH_ENABLED=true
KC_METRICS_ENABLED=true

KC_BOOTSTRAP_ADMIN_USERNAME=admin-username
KC_BOOTSTRAP_ADMIN_PASSWORD=admin-password

KC_PROXY_HEADERS=xforwarded
KC_PROXY_TRUSTED_ADDRESSES=10.0.0.0/8
KC_HTTP_ENABLED=true
KC_HTTP_HOST=0.0.0.0
KC_HTTP_PORT=8080
KC_HTTP_MAX_QUEUED_REQUESTS=10
KC_HTTP_RELATIVE_PATH="/"

And, the logs:

root@dmh-auth-kc0:/docker/compose/keycloak# docker compose up -d && docker logs --follow keycloak-server
[+] Running 3/3
 ✔ Network keycloak_keycloak_network  Created                                                                                              0.1s
 ✔ Container keycloak-database        Healthy                                                                                              6.2s
 ✔ Container keycloak-server          Started                                                                                              6.2s
2025-05-07 21:39:46,815 INFO  [org.keycloak.quarkus.runtime.storage.infinispan.CacheManagerFactory] (main) Starting Infinispan embedded cache manager
2025-05-07 21:39:46,836 INFO  [org.keycloak.quarkus.runtime.storage.infinispan.CacheManagerFactory] (main) JGroups JDBC_PING discovery enabled.
2025-05-07 21:39:47,484 INFO  [org.keycloak.quarkus.runtime.storage.infinispan.CacheManagerFactory] (main) JGroups Encryption enabled (mTLS).
2025-05-07 21:39:48,084 INFO  [org.keycloak.infinispan.module.certificates.CertificateReloadManager] (main) Starting JGroups certificate reload manager
2025-05-07 21:39:48,734 INFO  [org.infinispan.CONTAINER] (main) ISPN000556: Starting user marshaller 'org.infinispan.commons.marshall.ImmutableProtoStreamMarshaller'
2025-05-07 21:39:49,581 INFO  [org.infinispan.CLUSTER] (main) ISPN000078: Starting JGroups channel `ISPN` with stack `jdbc-ping`
2025-05-07 21:39:49,586 INFO  [org.jgroups.JChannel] (main) local_addr: 6ad55e9c-3905-430e-8109-c3b81c0dad0a, name: keycloak-server-41060
2025-05-07 21:39:49,607 INFO  [org.jgroups.protocols.FD_SOCK2] (main) server listening on *:57800
2025-05-07 21:39:49,632 INFO  [org.jgroups.protocols.pbcast.GMS] (main) keycloak-server-41060: no members discovered after 19 ms: creating cluster as coordinator
2025-05-07 21:39:49,772 INFO  [org.infinispan.CLUSTER] (main) ISPN000094: Received new cluster view for channel ISPN: [keycloak-server-41060|0] (1) [keycloak-server-41060]
2025-05-07 21:39:49,784 INFO  [org.keycloak.infinispan.module.certificates.CertificateReloadManager] (main) Reloading JGroups Certificate
2025-05-07 21:39:49,970 INFO  [org.infinispan.CLUSTER] (main) ISPN000079: Channel `ISPN` local address is `keycloak-server-41060`, physical addresses are `[172.19.0.3:7800]`
2025-05-07 21:39:51,841 INFO  [org.keycloak.connections.infinispan.DefaultInfinispanConnectionProviderFactory] (main) Node name: keycloak-server-41060, Site name: null
2025-05-07 21:39:55,480 INFO  [org.infinispan.CLUSTER] (main) ISPN000080: Disconnecting JGroups channel `ISPN`
2025-05-07 21:39:55,507 INFO  [org.keycloak.infinispan.module.certificates.CertificateReloadManager] (main) Stopping JGroups certificate reload manager
2025-05-07 21:39:55,529 INFO  [com.arjuna.ats.jbossatx] (main) ARJUNA032014: Stopping transaction recovery manager
2025-05-07 21:39:55,607 ERROR [org.keycloak.quarkus.runtime.cli.ExecutionExceptionHandler] (main) ERROR: Failed to start server in (production) mode
2025-05-07 21:39:55,609 ERROR [org.keycloak.quarkus.runtime.cli.ExecutionExceptionHandler] (main) Error details:: java.lang.RuntimeException: Unable to start HTTP server
        at io.quarkus.vertx.http.runtime.VertxHttpRecorder.doServerStart(VertxHttpRecorder.java:997)
        at io.quarkus.vertx.http.runtime.VertxHttpRecorder.startServer(VertxHttpRecorder.java:371)
        at io.quarkus.runner.recorded.VertxHttpProcessor$openSocket1873327713.deploy_0(Unknown Source)
        at io.quarkus.runner.recorded.VertxHttpProcessor$openSocket1873327713.deploy(Unknown Source)
        at io.quarkus.runner.ApplicationImpl.doStart(Unknown Source)
        at io.quarkus.runtime.Application.start(Application.java:101)
        at io.quarkus.runtime.ApplicationLifecycleManager.run(ApplicationLifecycleManager.java:121)
        at io.quarkus.runtime.Quarkus.run(Quarkus.java:77)
        at org.keycloak.quarkus.runtime.KeycloakMain.start(KeycloakMain.java:145)
        at org.keycloak.quarkus.runtime.cli.Picocli.start(Picocli.java:1000)
        at org.keycloak.quarkus.runtime.cli.command.AbstractStartCommand.run(AbstractStartCommand.java:49)
        at picocli.CommandLine.executeUserObject(CommandLine.java:2030)
        at picocli.CommandLine.access$1500(CommandLine.java:148)
        at picocli.CommandLine$RunLast.executeUserObjectOfLastSubcommandWithSameParent(CommandLine.java:2465)
        at picocli.CommandLine$RunLast.handle(CommandLine.java:2457)
        at picocli.CommandLine$RunLast.handle(CommandLine.java:2419)
        at picocli.CommandLine$AbstractParseResultHandler.execute(CommandLine.java:2277)
        at picocli.CommandLine$RunLast.execute(CommandLine.java:2421)
        at picocli.CommandLine.execute(CommandLine.java:2174)
        at org.keycloak.quarkus.runtime.cli.Picocli.parseAndRun(Picocli.java:128)
        at org.keycloak.quarkus.runtime.KeycloakMain.main(KeycloakMain.java:116)
        at org.keycloak.quarkus.runtime.KeycloakMain.main(KeycloakMain.java:71)
        at io.quarkus.bootstrap.runner.QuarkusEntryPoint.doRun(QuarkusEntryPoint.java:68)
        at io.quarkus.bootstrap.runner.QuarkusEntryPoint.main(QuarkusEntryPoint.java:36)
Caused by: java.util.concurrent.ExecutionException: io.quarkus.runtime.QuarkusBindException: Port(s) already bound: 8080: Cannot assign requested address
        at java.base/java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:396)
        at java.base/java.util.concurrent.CompletableFuture.get(CompletableFuture.java:2073)
        at io.quarkus.vertx.http.runtime.VertxHttpRecorder.doServerStart(VertxHttpRecorder.java:914)
        ... 23 more
Caused by: io.quarkus.runtime.QuarkusBindException: Port(s) already bound: 8080: Cannot assign requested address
        at io.quarkus.vertx.http.runtime.VertxHttpRecorder$13.handle(VertxHttpRecorder.java:881)
        at io.quarkus.vertx.http.runtime.VertxHttpRecorder$13.handle(VertxHttpRecorder.java:862)
        at io.vertx.core.impl.future.FutureImpl$4.onFailure(FutureImpl.java:188)
        at io.vertx.core.impl.future.FutureBase.emitFailure(FutureBase.java:81)
        at io.vertx.core.impl.future.FutureImpl.tryFail(FutureImpl.java:278)
        at io.vertx.core.impl.future.Mapping.onFailure(Mapping.java:45)
        at io.vertx.core.impl.future.FutureBase.emitFailure(FutureBase.java:81)
        at io.vertx.core.impl.future.FutureImpl.tryFail(FutureImpl.java:278)
        at io.vertx.core.impl.future.PromiseImpl.onFailure(PromiseImpl.java:54)
        at io.vertx.core.impl.future.PromiseImpl.handle(PromiseImpl.java:43)
        at io.vertx.core.impl.future.PromiseImpl.handle(PromiseImpl.java:23)
        at io.vertx.core.impl.DeploymentManager.lambda$reportResult$2(DeploymentManager.java:129)
        at io.vertx.core.impl.ContextInternal.dispatch(ContextInternal.java:270)
        at io.vertx.core.impl.ContextInternal.dispatch(ContextInternal.java:252)
        at io.vertx.core.impl.ContextInternal.lambda$runOnContext$0(ContextInternal.java:50)
        at io.netty.util.concurrent.AbstractEventExecutor.runTask(AbstractEventExecutor.java:173)
        at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:166)
        at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:472)
        at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:569)
        at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:998)
        at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
        at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
        at java.base/java.lang.Thread.run(Thread.java:1583)