Keycloak на сервере: частые ошибки и решения
Keycloak — не тот сервис, который прощает небрежность в конфигурации. Стоит перепутать KC_HOSTNAME с реальным адресом или забыть про reverse-proxy заголовки — и вместо единого входа для всех приложений вы получаете бесконечный редирект на логин или загадочный invalid_grant. Ниже — ошибки, с которыми сталкивается почти каждый, кто разворачивает Keycloak на своём VPS, и рабочие решения без танцев с бубном.
Содержание
- Keycloak не стартует: разбираем логи
- start-dev в продакшене: ловушка, в которую все попадают
- Бесконечный редирект на страницу логина
- Nginx как reverse-proxy перед Keycloak
- Проблемы с SSL и unable to find valid certification path
- База данных: подключение, деградация под нагрузкой и бэкапы
- Realm, клиенты и redirect URI: где чаще всего косячат руками
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →Keycloak не стартует: разбираем логи
Первый источник проблем — сам старт контейнера или процесса. Типичный docker-compose для Keycloak 26.x выглядит так:
services:
keycloak:
image: quay.io/keycloak/keycloak:26.0
command: start
environment:
KC_DB: postgres
KC_DB_URL: jdbc:postgresql://postgres:5432/keycloak
KC_DB_USERNAME: keycloak
KC_DB_PASSWORD: ${KC_DB_PASSWORD}
KC_HOSTNAME: auth.example.com
KC_PROXY_HEADERS: xforwarded
KC_HTTP_ENABLED: "true"
KEYCLOAK_ADMIN: admin
KEYCLOAK_ADMIN_PASSWORD: ${KC_ADMIN_PASSWORD}
ports:
- "127.0.0.1:8080:8080"
depends_on:
- postgres
restart: unless-stopped
Если контейнер падает сразу после старта, смотрите логи:
docker compose logs -f keycloak
Частые находки:
Failed to obtain JDBC connection— Postgres ещё не готов принимать подключения, а Keycloak уже пытается достучаться.depends_onв docker-compose не ждёт готовности БД, только запуска контейнера. Добавьтеhealthcheckдля postgres и условиеcondition: service_healthy.ERROR: relation "..." does not exist— Keycloak запускался с одной версией схемы БД, потом образ обновили без миграции. Перед апгрейдом всегда делайте бэкап БД и проверяйте release notes на breaking changes в схеме.Unknown option: '--...'— командаstart-devвместоstart(или наоборот) плюс параметр, который не поддерживается в вашей версии образа. Опции CLI между мажорными версиями Keycloak меняются заметно чаще, чем хотелось бы — не копируйте конфиг из статьи трёхлетней давности не глядя на версию.
Если используете systemd вместо контейнера — не забудьте отдельного пользователя без прав root и ограничение памяти в unit-файле:
[Service]
User=keycloak
Group=keycloak
Environment="JAVA_OPTS_APPEND=-Xms512m -Xmx1024m"
ExecStart=/opt/keycloak/bin/kc.sh start --optimized
Restart=on-failure
RestartSec=5
start-dev в продакшене: ловушка, в которую все попадают
Самая частая причина странного поведения Keycloak в проде — команда start-dev вместо start. Она включена в тысячах гайдов, потому что позволяет запустить сервер за 30 секунд без сертификатов и без настройки hostname. Но start-dev:
- отключает проверку hostname (redirect URI можно подделать);
- использует H2 в памяти, если явно не указана внешняя БД (данные теряются при перезапуске);
- не проверяет корректность SSL-цепочки.
Если вы видите в логах строку Running the server in development mode — это не шутка, это прямое указание, что конфигурация не готова к продакшену. Переход на start требует явно задать:
kc.sh build --db=postgres
kc.sh start --hostname=auth.example.com --https-certificate-file=/etc/keycloak/fullchain.pem --https-certificate-key-file=/etc/keycloak/privkey.pem
Либо, если TLS терминируется на reverse-proxy (что чаще всего и происходит), — --http-enabled=true --hostname-strict=false вместе с корректными заголовками X-Forwarded-* от прокси.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверБесконечный редирект на страницу логина
Классика: пользователь логинится, Keycloak редиректит обратно в приложение, приложение снова требует логин — и по кругу. Причины почти всегда одна из трёх:
- Cookie SameSite и домен не совпадают. Если Keycloak на
auth.example.com, а приложение наapp.example.com, куки сессии Keycloak (KEYCLOAK_SESSION,AUTH_SESSION_ID) должны корректно устанавливаться для своего домена, а не блокироваться браузером из-заSameSite=Strictза HTTP вместо HTTPS. - Расхождение времени между сервером Keycloak и клиентом/приложением. JWT-токены имеют
expиiatс допуском в несколько секунд. Если на VPS сбит NTP, токен может считаться просроченным ещё до выдачи. Проверьте:
timedatectl status
# при рассинхроне
sudo systemctl restart systemd-timesyncd
- Неверный
KC_HOSTNAMEза прокси. Если Keycloak не знает, что он открыт снаружи поhttps://auth.example.com, а сам генерирует ссылки наhttp://keycloak:8080(внутреннее имя контейнера), браузер получает редиректы на недоступный адрес. Проверка, какие заголовки реально доходят до Keycloak:
curl -sI https://auth.example.com/realms/master \
-H "X-Forwarded-Proto: https" \
-H "X-Forwarded-Host: auth.example.com"
Nginx как reverse-proxy перед Keycloak
Рабочий конфиг для nginx перед Keycloak — с обязательной передачей заголовков и увеличенными таймаутами, потому что операции с realm и импорт большого числа пользователей могут выполняться дольше стандартных 60 секунд:
server {
listen 443 ssl http2;
server_name auth.example.com;
ssl_certificate /etc/letsencrypt/live/auth.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/auth.example.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:8080;
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_set_header X-Forwarded-Port 443;
proxy_read_timeout 120s;
proxy_send_timeout 120s;
proxy_buffer_size 16k;
proxy_buffers 4 16k;
}
}
Без X-Forwarded-Proto Keycloak может генерировать ссылки на http://, что браузер с включённым HSTS просто заблокирует. Если вы только настраиваете proxy с нуля, у нас есть отдельный разбор — nginx как reverse-proxy: пошаговая установка — и статья о частых проблемах именно nginx-прокси: nginx как reverse-proxy: частые ошибки и решения.
Проблемы с SSL и unable to find valid certification path
Когда приложение (например, backend на Java/Spring) обращается к Keycloak по HTTPS с самоподписанным или внутренним CA сертификатом, вылезает классическая ошибка Java:
sun.security.validator.ValidatorException: PKIX path building failed:
unable to find valid certification path to requested target
Решение — добавить сертификат CA в truststore приложения:
keytool -import -alias keycloak-ca -file ca.crt \
-keystore $JAVA_HOME/lib/security/cacerts -storepass changeit -noprompt
Но правильнее в проде вообще не использовать самоподписанные сертификаты для публичного домена — Let's Encrypt через certbot закрывает вопрос бесплатно и без ручного распространения CA по клиентам. Если у вас уже был случай, когда сертификат вовремя не продлился и всё легло — почитайте почему не обновился SSL-сертификат и как это предотвратить, там разобраны типичные причины пропущенного renew.
База данных: подключение, деградация под нагрузкой и бэкапы
Keycloak активно пишет в БД на каждый логин — сессии, события, кэш офлайн-токенов. На небольшом VPS с 1-2 vCPU и Postgres на том же сервере под нагрузкой в несколько сотен логинов в минуту типична деградация: растёт max_connections, появляются таймауты.
Проверка активных подключений:
SELECT count(*), state FROM pg_stat_activity
WHERE datname = 'keycloak' GROUP BY state;
Если idle in transaction растёт бесконтрольно — Keycloak не успевает закрывать транзакции, часто из-за медленного диска или нехватки памяти для Postgres. Практический минимум для связки Keycloak + Postgres на отдельном VPS — 2 vCPU / 4 ГБ RAM на каждый сервис, то есть от 4 vCPU / 8 ГБ суммарно, если ожидается активная нагрузка от нескольких приложений одновременно; для тестового стенда хватит и меньше. Точные цифры сильно зависят от количества realm, пользователей и частоты логинов — ориентируйтесь на свой профиль нагрузки, а не на общие цифры из статьи.
Если Postgres настроен с нуля недавно, стоит свериться с общим чек-листом по типичным сбоям — PostgreSQL на сервере: частые ошибки и решения.
Realm, клиенты и redirect URI: где чаще всего косячат руками
Ошибка Invalid parameter: redirect_uri — почти всегда буквальное несовпадение строки в настройках клиента и тем, что реально шлёт приложение. Частые нюансы:
- слэш в конце URI важен:
https://app.example.com/callbackиhttps://app.example.com/callback/— это разные строки для Keycloak; - wildcard
*в redirect URI разрешён, но с версии 22+ Keycloak предупреждает о нём в консоли как о небезопасной практике — для продакшена перечисляйте точные URI; - при использовании нескольких окружений (dev/staging/prod) проще завести отдельные клиенты в одном realm, чем пытаться держать один клиент с десятком redirect URI — так проще отзывать доступ и не потерять в логах, какое окружение стучалось.
Проверка конфигурации клиента через Admin REST API, если веб-консоль недоступна:
TOKEN=$(curl -s -X POST https://auth.example.com/realms/master/protocol/openid-connect/token \
-d "client_id=admin-cli" -d "username=admin" -d "password=$KC_ADMIN_PASSWORD" -d "grant_type=password" \
| jq -r .access_token)
curl -s https://auth.example.com/admin/realms/myrealm/clients \
-H "Authorization: Bearer $TOKEN" | jq '.[] | {clientId, redirectUris}'
Отдельная тема — резервное копирование. Экспорт realm — то, о чём вспоминают обычно после того, как что-то сломалось. Делать это стоит регулярно и до инцидента:
kc.sh export --dir /tmp/kc-export --realm myrealm --users realm_file
Для полного восстановления после сбоя надёжнее иметь дамп самой БД, а не только экспорт realm — экспорт не всегда переносит все настройки один в один между версиями Keycloak:
pg_dump -U keycloak -d keycloak -F c -f keycloak_backup.dump
# восстановление
pg_restore -U keycloak -d keycloak --clean keycloak_backup.dump
Держите бэкапы вне того же сервера — если VPS выйдет из строя целиком, локальная копия дампа не поможет.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
Keycloak запускается, но админка отдаёт 403 или бесконечно грузится.
Чаще всего это несовпадение KC_HOSTNAME и реального адреса за прокси, либо блокировка WebSocket/keep-alive соединений на уровне nginx. Проверьте заголовки X-Forwarded-* и добавьте proxy_http_version 1.1; в конфиг nginx.
После обновления образа Keycloak realm пропали или клиенты не работают.
Между мажорными версиями (например, 24 → 26) меняется формат хранения некоторых сущностей. Всегда делайте дамп БД перед обновлением и проверяйте changelog конкретной версии на breaking changes, не полагаясь на автоматическую миграцию вслепую.
Можно ли обойтись без отдельного сервера под Keycloak и Postgres, и держать всё в одном контейнере?
Технически да через start-dev с встроенной H2, но это заведомо непригодно для продакшена — данные не переживут перезапуск контейнера, а H2 не рассчитана на конкурентную нагрузку. Для реального использования нужен отдельный Postgres минимум на том же сервере, а лучше — на отдельном.
Как понять, что дело именно в Keycloak, а не в приложении, которое к нему стучится?
Проверьте эндпоинт /realms/<realm>/.well-known/openid-configuration напрямую через curl — если он отвечает корректным JSON с нужными URL, Keycloak работает штатно, и проблему стоит искать в конфигурации client_id/client_secret на стороне приложения.
Нужен ли отдельный VPS под Keycloak, если приложений немного?
Для 1-2 небольших приложений можно держать Keycloak на том же сервере, что и сами приложения, если ресурсов достаточно. Но при росте нагрузки или количества realm разумнее вынести identity-провайдер отдельно — единая точка отказа для всей авторизации не должна конкурировать за CPU и память с прикладным кодом.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →