Keycloak es un servidor de identidad de código abierto que centraliza el inicio de sesión de tus aplicaciones mediante OpenID Connect, OAuth 2.0 y SAML 2.0. Con un único punto de autenticación, los usuarios inician sesión una vez y acceden a todas las aplicaciones conectadas (Single Sign-On). En este tutorial desplegarás Keycloak 26 con Docker Compose y PostgreSQL en Ubuntu 24.04, lo publicarás con Nginx y un certificado de Let's Encrypt, y crearás un realm, un usuario y un cliente OpenID Connect listos para integrar una aplicación.
Requisitos previos
Para seguir esta guía necesitas:
- Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath, con al menos 2 GB de RAM (4 GB recomendados en producción).
- Un usuario no root con privilegios
sudoy UFW activo con SSH permitido. - Docker Engine y el plugin de Docker Compose instalados desde el repositorio oficial de Docker.
- Un dominio con un registro A que apunte a la IP del servidor. En esta guía se usa
sso.your_domain; sustitúyelo por el tuyo. - Nginx instalado (
sudo apt install nginx).
Paso 1: Preparar las credenciales
Keycloak necesita dos secretos: la contraseña de la base de datos y la de un administrador inicial. Crea el directorio del proyecto y un archivo .env con contraseñas aleatorias:
sudo mkdir -p /opt/keycloak
sudo tee /opt/keycloak/.env > /dev/null <<EOF
POSTGRES_PASSWORD=$(openssl rand -hex 24)
KC_BOOTSTRAP_ADMIN_PASSWORD=$(openssl rand -hex 16)
EOF
sudo chmod 600 /opt/keycloak/.env
Consulta la contraseña del administrador inicial, porque la necesitarás en el paso 4:
sudo grep KC_BOOTSTRAP_ADMIN_PASSWORD /opt/keycloak/.env
Paso 2: Crear el archivo de Docker Compose
Crea el archivo:
sudo nano /opt/keycloak/docker-compose.yml
Pega esta configuración:
services:
postgres:
image: postgres:17
restart: unless-stopped
environment:
POSTGRES_DB: keycloak
POSTGRES_USER: keycloak
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
- postgres-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U keycloak -d keycloak"]
interval: 10s
timeout: 5s
retries: 5
keycloak:
image: quay.io/keycloak/keycloak:26.7
restart: unless-stopped
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: ${POSTGRES_PASSWORD}
KC_HOSTNAME: https://sso.your_domain
KC_PROXY_HEADERS: xforwarded
KC_HTTP_ENABLED: "true"
KC_HEALTH_ENABLED: "true"
KC_BOOTSTRAP_ADMIN_USERNAME: admin
KC_BOOTSTRAP_ADMIN_PASSWORD: ${KC_BOOTSTRAP_ADMIN_PASSWORD}
ports:
- "127.0.0.1:8080:8080"
- "127.0.0.1:9000:9000"
volumes:
postgres-data:
Qué hace cada ajuste importante:
command: startarranca Keycloak en modo producción. El modostart-devusa una base de datos embebida y no debe usarse fuera de pruebas.KC_HOSTNAMEfija la URL pública. Keycloak la usa para generar los enlaces y las URL de redirección de OpenID Connect, así que debe coincidir exactamente con la que usan los navegadores.KC_PROXY_HEADERS: xforwardedhace que Keycloak confíe en las cabecerasX-Forwarded-*que envía Nginx, yKC_HTTP_ENABLEDpermite HTTP entre Nginx y el contenedor, porque TLS termina en Nginx.KC_HEALTH_ENABLEDexpone los endpoints de salud en el puerto de gestión 9000.- Los puertos se publican solo en
127.0.0.1. Docker se salta UFW, así que publicarlos en todas las interfaces dejaría Keycloak accesible sin HTTPS.
Notala etiqueta
26.7recibe las versiones de parche de esa rama. Consulta las versiones nuevas en la página de releases de Keycloak antes de cambiar a una rama superior.
Paso 3: Arrancar Keycloak
Arranca los contenedores:
cd /opt/keycloak
sudo docker compose up -d
El primer arranque tarda un minuto o dos porque Keycloak crea el esquema en PostgreSQL. Sigue los logs:
sudo docker compose logs -f keycloak
Cuando esté listo verás una línea como esta:
keycloak-1 | ... INFO [io.quarkus] (main) Keycloak 26.7.4 on JVM (powered by Quarkus 3.x) started in 18.214s. Listening on: http://0.0.0.0:8080. Management interface listening on http://0.0.0.0:9000.
Pulsa Ctrl+C y comprueba el endpoint de disponibilidad:
curl http://127.0.0.1:9000/health/ready
{
"status": "UP",
"checks": [
{
"name": "Keycloak database connections async health check",
"status": "UP"
}
]
}
Paso 4: Publicar Keycloak con Nginx y HTTPS
Crea el bloque de servidor:
sudo nano /etc/nginx/sites-available/keycloak
Keycloak devuelve cabeceras grandes en algunas respuestas (cookies y tokens), por lo que conviene ampliar los búferes del proxy:
server {
listen 80;
listen [::]:80;
server_name sso.your_domain;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
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 $server_port;
proxy_buffer_size 128k;
proxy_buffers 4 256k;
proxy_busy_buffers_size 256k;
}
}
No publiques el puerto 9000 a través de Nginx: los endpoints de gestión son solo para uso interno.
Activa el sitio, comprueba la sintaxis, recarga Nginx y abre los puertos web:
sudo ln -s /etc/nginx/sites-available/keycloak /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
sudo ufw allow 'Nginx Full'
Obtén el certificado con Certbot. El plugin de Nginx añade la configuración TLS y la redirección de HTTP a HTTPS:
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d sso.your_domain
Comprueba que Keycloak responde por HTTPS y que anuncia URL con https:
curl -s https://sso.your_domain/realms/master/.well-known/openid-configuration | grep -o '"issuer":"[^"]*"'
"issuer":"https://sso.your_domain/realms/master"
Si el issuer aparece con http://, Keycloak no está recibiendo la cabecera X-Forwarded-Proto; revisa el bloque de Nginx.
Paso 5: Crear un administrador permanente
La cuenta admin creada con las variables KC_BOOTSTRAP_ADMIN_* es temporal, y la consola muestra un aviso mientras exista. Abre https://sso.your_domain/admin/ e inicia sesión con admin y la contraseña del paso 1.
En el realm master:
- Ve a Users > Add user, escribe un nombre de usuario propio (por ejemplo
your_user) y pulsa Create. - En la pestaña Credentials, pulsa Set password, introduce una contraseña robusta y desactiva Temporary.
- En la pestaña Role mapping, pulsa Assign role, filtra por roles de realm y asigna el rol
admin.
Cierra sesión, entra con la cuenta nueva y, en Users, elimina el usuario admin temporal. Después borra la línea KC_BOOTSTRAP_ADMIN_PASSWORD de /opt/keycloak/.env y las dos variables KC_BOOTSTRAP_ADMIN_* de docker-compose.yml, y recrea el contenedor:
cd /opt/keycloak
sudo docker compose up -d
Paso 6: Crear un realm, un usuario y un cliente OpenID Connect
Un realm es un espacio aislado con sus propios usuarios, roles y aplicaciones. El realm master es solo para administrar Keycloak; tus aplicaciones deben usar un realm propio.
Aunque puedes hacerlo todo desde la consola, la herramienta kcadm.sh incluida en la imagen permite automatizarlo. Ejecuta todos los comandos de este paso desde /opt/keycloak. Autentícate con tu administrador permanente (pedirá la contraseña):
sudo docker compose exec keycloak /opt/keycloak/bin/kcadm.sh config credentials \
--server http://localhost:8080 --realm master --user your_user
Crea el realm empresa:
sudo docker compose exec keycloak /opt/keycloak/bin/kcadm.sh create realms \
-s realm=empresa -s enabled=true
Crea un usuario de prueba en ese realm y asígnale una contraseña (sustituye user_password):
sudo docker compose exec keycloak /opt/keycloak/bin/kcadm.sh create users -r empresa \
-s username=jgarcia -s enabled=true -s [email protected] \
-s firstName=Juan -s lastName=Garcia
sudo docker compose exec keycloak /opt/keycloak/bin/kcadm.sh set-password -r empresa \
--username jgarcia --new-password user_password
Crea un cliente OpenID Connect confidencial para una aplicación web con backend. redirectUris debe contener las URL a las que Keycloak puede devolver al usuario tras el login; evita comodines amplios en producción:
sudo docker compose exec keycloak /opt/keycloak/bin/kcadm.sh create clients -r empresa \
-s clientId=mi-aplicacion \
-s protocol=openid-connect \
-s publicClient=false \
-s standardFlowEnabled=true \
-s 'redirectUris=["https://app.your_domain/callback"]' \
-s 'webOrigins=["https://app.your_domain"]' \
-i
La opción -i imprime el identificador interno del cliente, algo como 8f1c2d3e-.... Úsalo para obtener el secreto que configurarás en la aplicación:
sudo docker compose exec keycloak /opt/keycloak/bin/kcadm.sh get clients/client_internal_id/client-secret -r empresa
{
"type" : "secret",
"value" : "Xy7...generated-secret"
}
Paso 7: Verificar el inicio de sesión
Comprueba que el realm publica su configuración de descubrimiento, que es lo que leen las librerías OpenID Connect de las aplicaciones:
curl -s https://sso.your_domain/realms/empresa/.well-known/openid-configuration | python3 -m json.tool | head -8
{
"issuer": "https://sso.your_domain/realms/empresa",
"authorization_endpoint": "https://sso.your_domain/realms/empresa/protocol/openid-connect/auth",
"token_endpoint": "https://sso.your_domain/realms/empresa/protocol/openid-connect/token",
...
Para probar el login de un usuario sin tener todavía una aplicación, abre la consola de cuenta del realm en https://sso.your_domain/realms/empresa/account e inicia sesión con jgarcia. Si ves la página de perfil, la autenticación funciona.
Para integrar tu aplicación, configura su librería OpenID Connect con estos datos:
| Parámetro | Valor |
|---|---|
| Issuer / discovery URL | https://sso.your_domain/realms/empresa |
| Client ID | mi-aplicacion |
| Client secret | el obtenido en el paso 6 |
| Redirect URI | https://app.your_domain/callback |
| Scopes | openid profile email |
Paso 8: Actualizar y hacer copias de seguridad
Todo el estado de Keycloak (realms, usuarios, clientes, sesiones persistentes) vive en PostgreSQL. Haz una copia de la base de datos antes de cada actualización:
cd /opt/keycloak
sudo docker compose exec -T postgres pg_dump -U keycloak keycloak | gzip | sudo tee /root/keycloak-$(date +%F).sql.gz > /dev/null
Para actualizar dentro de la rama 26.7, descarga la imagen y recrea el contenedor. Keycloak migra el esquema automáticamente al arrancar:
sudo docker compose pull
sudo docker compose up -d
Solución de problemas
Keycloak no arranca y los logs muestran errores de conexión a la base de datos. Comprueba que PostgreSQL está sano con sudo docker compose ps y que las variables POSTGRES_PASSWORD y KC_DB_PASSWORD coinciden. Si cambiaste la contraseña en .env después del primer arranque, PostgreSQL sigue usando la original, porque solo la aplica al crear el volumen.
El navegador muestra Invalid parameter: redirect_uri. La URL de redirección que envía la aplicación no coincide con ninguna de redirectUris del cliente. Compara ambas carácter a carácter, incluida la barra final.
La consola de administración se queda cargando o redirige a http://. KC_HOSTNAME no coincide con la URL pública o Nginx no envía X-Forwarded-Proto. Revisa ambos y recrea el contenedor con sudo docker compose up -d.
Error 502 Bad Gateway o upstream sent too big header en Nginx. El primero indica que el contenedor no está escuchando todavía (revisa los logs); el segundo, que faltan las directivas proxy_buffer_size y proxy_buffers del paso 4.
Conclusión
Ya tienes Keycloak 26 en producción con PostgreSQL, publicado con HTTPS detrás de Nginx, con un administrador permanente y un realm con un usuario y un cliente OpenID Connect listos para integrar. Como siguientes pasos, puedes activar la autenticación en dos pasos (OTP o WebAuthn) en el realm, federar usuarios desde un directorio LDAP existente en User federation y programar la copia de PostgreSQL con un temporizador de systemd.
