Keycloak Docker Compose 部署指南

Docker Compose 部署 Keycloak 26.8.1:dev 与 Postgres+生产启动两套编排、初始化管理员、数据持久化与升级兼容实录。

KeycloakKeycloak2026/10/17

Keycloak Docker Compose 部署指南

‍‍​​‌​> 目标:用 Docker Compose 在 Ubuntu 22.04 上完成 Keycloak 26.8.1 的两种落地形态——开发验证用的 dev 模式(H2 内置存储)与生产可用的 Postgres 16 + 生产启动模式,覆盖镜像准备、编排文件、初始化管理员、数据持久化验证与升级兼容,全部配置与回显逐行实测通过。

一、环境信息

  • 操作系统:Ubuntu 22.04(x86_64)
  • Docker Engine:27.5.1(含 Compose v2 编排器,实测 docker compose version 为 Docker Compose version v5.5.1)
  • Keycloak 镜像:quay.io/keycloak/keycloak,实测固定版本 26.8.1(Quarkus 3.40.1 底座)
  • 数据库镜像:postgres:16
  • 端口约定:宿主机 8080 → Keycloak 8080;Postgres 不向宿主机映射端口(仅编排内网访问)
  • 数据落盘目录:/opt/data/keycloak-pg(绑定挂载到 Postgres 数据目录)
  • 容器命名:dev 形态 keycloak-26.8.1-sso;生产形态 keycloak-16-db + keycloak-26.8.1-pg

💡 说明:本机 8080 端口在 IPv4 侧被宿主机既有服务(haproxy)占用,因此端口映射写作 [::]:8080:8080(仅经 IPv6 栈发布);若无此冲突,直接写 8080:8080 即可。

二、镜像与版本准备

拉取官方镜像并确认实际版本——生产编排建议固定具体版本号 tag,避免 latest 随上游漂移:

docker pull quay.io/keycloak/keycloak:latest
docker pull postgres:16

实测回显(节选):

latest: Pulling from keycloak/keycloak
Digest: sha256:56e5a5d2f1bbd7f22c66b5b451e034ce7754a2bfe8b6d05f14b7d2a36ae4d472
Status: Downloaded newer image for quay.io/keycloak/keycloak:latest

用容器内自带的 kc.sh --version 核对镜像内真实版本:

docker run --rm quay.io/keycloak/keycloak:latest --version

实测回显:

Keycloak 26.8.1
JVM: 21.0.12.1 (Red Hat, Inc. OpenJDK 64-Bit Server VM 21.0.12.1+1-LTS)
OS: Linux 6.8.0-138-generic amd64

确认 latest 当前解析为 26.8.1 后,后续编排统一写 quay.io/keycloak/keycloak:26.8.1(tag 与 digest 一一对应,升级时只改 tag)。

三、方案一:dev 模式部署(start-dev,开箱即用)

3.1 编排文件

dev 模式(start-dev)跳过 hostname/TLS 硬校验、使用内置 H2 存储,适合功能验证与本地联调。compose-dev.yml:

services:
  keycloak:
    image: quay.io/keycloak/keycloak:26.8.1
    container_name: keycloak-26.8.1-sso
    command: start-dev
    environment:
      KC_BOOTSTRAP_ADMIN_USERNAME: admin
      KC_BOOTSTRAP_ADMIN_PASSWORD: q1w2e3r4
    ports:
      - "[::]:8080:8080"

适用场景:本地功能验证、与业务应用联调 SSO 流程、临时演示。磁盘占用小(无需独立数据库)、启动快,但数据不跨容器重建持久,禁止用于生产。

3.2 启动与验证

docker compose -f compose-dev.yml -p keycloak-dev up -d

实测回显:

[+] Running 2/2
 ✔ Network keycloak-dev_default    Created
 ✔ Container keycloak-26.8.1-sso   Started

等待约 30 秒后查看状态与服务日志:

docker compose -f compose-dev.yml -p keycloak-dev ps

实测回显:

NAME                  IMAGE                           COMMAND                  SERVICE    CREATED          STATUS          PORTS
keycloak-26.8.1-sso   quay.io/keycloak/keycloak:26.8.1   "/opt/keycloak/bin/kc…"   keycloak   31 seconds ago   Up 29 seconds   [::]:8080->8080/tcp
docker logs keycloak-26.8.1-sso 2>&1 | grep -E 'started in|Listening on|Keycloak 26' | tail -2

实测回显:

Keycloak 26.8.1 on JVM (powered by Quarkus 3.40.1) started in 9.394s. Listening on: http://0.0.0.0:8080

验证服务可达:

curl -s -o /dev/null -w 'HTTP %{http_code}\n' http://localhost:8080/

实测回显:

HTTP 302

3.3 初始化管理员并登录控制台

编排里已通过 KC_BOOTSTRAP_ADMIN_USERNAME/PASSWORD 声明引导管理员(admin / q1w2e3r4),首次访问 http://localhost:8080/admin/ 即以该账号登录 Admin Console。控制台可达性与登录链路验证:

curl -s -o /dev/null -w 'console HTTP %{http_code}\n' http://localhost:8080/admin/master/console/

实测回显:

console HTTP 200
curl -s -X POST http://localhost:8080/realms/master/protocol/openid-connect/token \
  -d 'grant_type=password&client_id=admin-cli&username=admin&password=q1w2e3r4'

实测回显(输出节选,能取得 token 即账号可用):

{"access_token":"eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJES29MN3ZFdVJMSnJKNDlTXy1wTUJGd1ZyLVdrczljdU52Rl9HNVhCU1Q4In0...(400 chars)","expires_in":1800,"refresh_expires_in":3600,"token_type":"Bearer","not-before-policy":0,"scope":"profile email"}

⚠️ 注意:dev 模式首次启动若漏配 KC_BOOTSTRAP_ADMIN_*,日志中不会创建任何管理员,首次登录前需补配变量重建或进入容器执行 /opt/keycloak/bin/kc.sh bootstrap-admin user。

四、方案二:Postgres + 生产启动模式(start,可落地生产)

4.1 编排文件

compose-prod.yml,要点:Postgres 16 独立服务(不映射宿主端口)、数据绑定挂载到 /opt/data/keycloak-pg、健康检查先行、Keycloak 以 start 生产模式连接数据库:

services:
  postgres:
    image: postgres:16
    container_name: keycloak-16-db
    environment:
      POSTGRES_USER: keycloak
      POSTGRES_PASSWORD: q1w2e3r4
      POSTGRES_DB: keycloak
    volumes:
      - /opt/data/keycloak-pg:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U keycloak -d keycloak"]
      interval: 5s
      timeout: 3s
      retries: 12

  keycloak:
    image: quay.io/keycloak/keycloak:26.8.1
    container_name: keycloak-26.8.1-pg
    command: start
    depends_on:
      postgres:
        condition: service_healthy
    environment:
      KC_DB: postgres
      KC_DB_URL: jdbc:postgresql://postgres:5432/keycloak
      KC_DB_USERNAME: keycloak
      KC_DB_PASSWORD: q1w2e3r4
      KC_HTTP_ENABLED: "true"
      KC_HOSTNAME: localhost
      KC_BOOTSTRAP_ADMIN_USERNAME: admin
      KC_BOOTSTRAP_ADMIN_PASSWORD: q1w2e3r4
    ports:
      - "[::]:8080:8080"

💡 说明:KC_DB_URL 显式写全 JDBC 地址,避免依赖默认拼接规则;KC_HTTP_ENABLED=true 与 KC_HOSTNAME=localhost 的组合对应"TLS 由前置代理终结、按 Hostname v2 显式声明主机名"的部署形态,参数语义见《Keycloak 配置参数详解》。

4.2 启动与验证

docker compose -f compose-prod.yml -p keycloak-prod up -d

实测回显(健康检查通过后 Keycloak 才启动):

[+] Running 2/2
 ✔ Container keycloak-16-db     Healthy
 ✔ Container keycloak-26.8.1-pg Started
docker compose -f compose-prod.yml -p keycloak-prod ps
docker logs keycloak-26.8.1-pg 2>&1 | grep -E 'started in|Listening on' | tail -1

实测回显:

NAME                  IMAGE                           COMMAND                  SERVICE    CREATED         STATUS          PORTS
keycloak-16-db        postgres:16                     "docker-entrypoint.s…"   postgres   2 minutes ago   Up 2 minutes    5432/tcp
keycloak-26.8.1-pg    quay.io/keycloak/keycloak:26.8.1   "/opt/keycloak/bin/kc…"   keycloak   2 minutes ago   Up 2 minutes    [::]:8080->8080/tcp

Keycloak 26.8.1 on JVM (powered by Quarkus 3.40.1) started in 15.919s. Listening on: http://0.0.0.0:8080

⚠️ 注意:生产模式启动有硬校验,缺 hostname 会直接报 hostname is not configured、缺 HTTPS/HTTP 声明会报 Key material not provided to setup HTTPS,逐层补齐的方法见《Keycloak 配置参数详解》6.2 节的三步实录。

4.3 数据持久化验证

数据库数据落在 /opt/data/keycloak-pg(绑定挂载),容器销毁重建不丢数据。实测闭环:先在生产实例创建业务域 quzuo-demo,然后 down 全部销毁、up -d 重建,再查域列表:

docker compose -f compose-prod.yml -p keycloak-prod down

实测回显:

[+] Running 3/3
 ✔ Container keycloak-26.8.1-pg   Removed
 ✔ Container keycloak-16-db       Removed
 ✔ Network keycloak-prod_default  Removed
docker compose -f compose-prod.yml -p keycloak-prod up -d
sleep 45
AT=$(curl -s -X POST http://localhost:8080/realms/master/protocol/openid-connect/token \
  -d 'grant_type=password&client_id=admin-cli&username=admin&password=q1w2e3r4' \
  | python3 -c 'import sys,json;print(json.load(sys.stdin)["access_token"])')
curl -s http://localhost:8080/admin/realms -H "Authorization: Bearer $AT" \
  | python3 -c 'import sys,json;[print("realm:", r["realm"]) for r in json.load(sys.stdin)]'

实测回显:

realm: master
realm: quzuo-demo

验证结论:销毁重建后 quzuo-demo 与管理员账号均完好,持久化链路(绑定挂载 → Postgres → Keycloak 元数据)成立。

五、初始化管理员

两种形态的管理员初始化统一约定:

方式触发时机命令/配置
编排变量引导首次启动自动建号KC_BOOTSTRAP_ADMIN_USERNAME / KC_BOOTSTRAP_ADMIN_PASSWORD
交互式补建库中已有 master 域、变量不再生效时docker compose exec keycloak /opt/keycloak/bin/kc.sh bootstrap-admin user

实测要点:引导管理员仅在 master 域创建瞬间生效(镜像内 kc.sh --help 对该参数的说明原文:Used only when the master realm is created);为生产环境建议用强密码替换本指南的示例值,并在首次登录后按需开启二步验证。

六、升级与数据兼容

6.1 升级路径(小版本升级)

  1. 备份先行:导出域数据 + 数据库快照。
docker compose -f compose-prod.yml -p keycloak-prod exec keycloak \
  /opt/keycloak/bin/kc.sh export --dir /tmp/kc-export

实测回显(节选):

INFO  [org.keycloak.exportimport.dir.DirExportProvider] (main) Realm 'quzuo-demo' - data exported
INFO  [org.keycloak.exportimport.dir.DirExportProvider] (main) Realm 'master' - data exported
INFO  [org.keycloak.services] (main) KC-SERVICES0035: Export finished successfully
  1. 改 tag 升级:把编排里的镜像 tag 从 26.8.1 改为目标版本(如 26.x.y),执行 docker compose -f compose-prod.yml -p keycloak-prod pull && docker compose -f compose-prod.yml -p keycloak-prod up -d;新版本容器启动时自动完成数据库 schema 迁移,旧数据无需手工处理。
  2. 回滚预案:若新版本启动失败,把 tag 改回 26.8.1 重新 up -d;已执行 schema 迁移的库建议回滚到第 1 步的数据库快照再回退。

6.2 兼容性检查

26.x 提供滚动更新兼容性检查工具(kc.sh update-compatibility 子命令,官方标注 Preview):metadata 记录旧实例的配置元数据,check 判断当前配置是否可与旧元数据滚动共存,退出码 0 表示可滚动更新。多副本滚动升级前建议先跑该检查。

七、常见问题

Q1:up -d 后端口映射失败/服务不通? 检查宿主机 8080 是否被既有服务占用(本机实测 haproxy 占用 IPv4 8080):ss -tlnp | grep :8080;处理方式三选一——IPv6 栈发布([::]:8080:8080,本指南采用)、改 Keycloak 侧端口(KC_HTTP_PORT)、换宿主端口(8081:8080)。

Q2:Keycloak 一直重启,日志报 Connection to postgres refused? Keycloak 比 Postgres 先启动导致。必须用 depends_on + service_healthy(4.1 编排已含),并确认健康检查命令 pg_isready -U keycloak -d keycloak 与数据库账号匹配。

Q3:生产模式报 hostname is not configured 或 Key material not provided to setup HTTPS? 生产启动硬校验项,编排里必须有 KC_HOSTNAME 与 TLS 证书(或 KC_HTTP_ENABLED=true 交由前置代理终结 TLS),完整三步排障见《Keycloak 配置参数详解》6.2。

Q4:重建后用户与域全丢了? Postgres 数据目录没有做绑定挂载(或挂载路径写错),容器删除即丢库。确认 4.1 编排中 /opt/data/keycloak-pg:/var/lib/postgresql/data 存在,并按 4.3 的 down/up 闭环实测一次。

Q5:镜像 tag 该用 latest 还是固定版本? 生产编排固定版本号(本指南 26.8.1);latest 仅用于了解上游最新版本——本次拉取实测解析为 26.8.1,但随上游发布随时漂移,会造成"重建即升级"的不可控行为。

八、参考链接