Quick start: Set up Keycloak and PostgreSQL in 5 minutes
Enterprises running dozens of internal applications from Jira and GitLab to CRM systems often face the same challenge: users constantly forgetting passwords. When an employee leaves, IT must manually review and revoke accounts across every single system. This is both time-consuming and prone to oversights. Keycloak completely solves this problem through Single Sign-On (SSO): a single account grants access to all services, with permissions managed centrally from a unified interface.
Below are the steps to quickly set up the service cluster on CentOS Stream 9 with Java 17, PostgreSQL 16, and Keycloak Quarkus.
1. Install OpenJDK 17 and PostgreSQL 16
# Update the OS and install prerequisite packages
sudo dnf update -y
sudo dnf install -y java-17-openjdk-headless wget tar
# Add the official PostgreSQL repository for EL9
sudo dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-9-x86_64/pgdg-redhat-repo-latest.noarch.rpm
sudo dnf -qy module disable postgresql
sudo dnf install -y postgresql16-server postgresql16
# Initialize the database and enable the service
sudo /usr/pgsql-16/bin/postgresql-16-setup initdb
sudo systemctl enable --now postgresql-16
2. Initialize Database and User for Keycloak
Starting from PostgreSQL 15, object creation privileges on the public schema are revoked by default. You need to explicitly grant privileges on the schema of the keycloak database:
sudo -u postgres psql -c "CREATE DATABASE keycloak;"
sudo -u postgres psql -c "CREATE USER keycloak WITH ENCRYPTED PASSWORD 'MatKhauSieuKho2026!';"
sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE keycloak TO keycloak;"
# Connect to the keycloak database to grant privileges on the public schema
sudo -u postgres psql -d keycloak -c "GRANT ALL ON SCHEMA public TO keycloak;"
3. Download Keycloak and Test in Development Mode
cd /opt
sudo wget https://github.com/keycloak/keycloak/releases/download/24.0.2/keycloak-24.0.2.tar.gz
sudo tar -xvzf keycloak-24.0.2.tar.gz
sudo mv keycloak-24.0.2 keycloak
# Initialize the initial admin account and run in dev mode on port 8080
export KEYCLOAK_ADMIN=admin
export KEYCLOAK_ADMIN_PASSWORD=AdminSecurePass123!
/opt/keycloak/bin/kc.sh start-dev
Access http://<IP-server>:8080 to verify the Admin Console before proceeding to production configuration.
Operational Architecture: Why Keycloak, PostgreSQL, and Nginx?
To stably serve anywhere from several hundred to tens of thousands of active sessions, a 3-tier architecture clearly separates responsibilities:
- Keycloak (Identity Provider – IdP): Handles the entire authentication flow for OIDC, OAuth2, and SAML 2.0. Keycloak issues JWT tokens, handles refresh tokens, manages user sessions, and enforces RBAC permissions for client applications.
- PostgreSQL (Storage Layer): Provides persistent storage for accounts, realms, client secrets, and audit logs. Keycloak comes with an embedded H2 database, but H2 is strictly for local testing and will corrupt data under concurrent loads.
- Nginx (Reverse Proxy & SSL Termination): Acts as a secure gateway shielding Keycloak. Nginx terminates HTTPS, enforces SSL ciphers, compresses headers, and securely forwards traffic to internal port 8080.
Configure Keycloak in Production Mode with PostgreSQL
Open /opt/keycloak/conf/keycloak.conf and configure the connection parameters:
# /opt/keycloak/conf/keycloak.conf
# Database configuration
db=postgres
db-username=keycloak
db-password=MatKhauSieuKho2026!
db-url=jdbc:postgresql://127.0.0.1:5432/keycloak
# Reverse proxy configuration (standard for Keycloak 24+)
proxy-headers=xforwarded
http-enabled=true
hostname=sso.example.com
Rebuild the application so Quarkus can optimize the configuration before running as a daemon:
cd /opt/keycloak
sudo ./bin/kc.sh build
Production Deployment with Systemd, SELinux, and Nginx
1. Create a Dedicated User and Configure Systemd Service
Never run application processes as the root user in production environments:
sudo useradd -r -d /opt/keycloak -s /sbin/nologin keycloak
sudo chown -R keycloak:keycloak /opt/keycloak
# Create systemd unit file
sudo tee /etc/systemd/system/keycloak.service << 'EOF'
[Unit]
Description=Keycloak Identity Provider Server
After=network.target postgresql-16.service
Wants=postgresql-16.service
[Service]
Type=exec
User=keycloak
Group=keycloak
ExecStart=/opt/keycloak/bin/kc.sh start --optimized
Restart=on-failure
RestartSec=10
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now keycloak
2. Configure Nginx Reverse Proxy with HTTPS
Install Nginx:
sudo dnf install -y nginx
sudo systemctl enable --now nginx
Create a virtual host at /etc/nginx/conf.d/keycloak.conf. Note that you should increase the buffer size to avoid 502 Bad Gateway errors when Keycloak returns headers containing large cookies or JWT tokens:
server {
listen 80;
server_name sso.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name sso.example.com;
ssl_certificate /etc/pki/tls/certs/sso.example.com.crt;
ssl_certificate_key /etc/pki/tls/private/sso.example.com.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
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 $server_port;
# Buffer headers to handle large JWT tokens
proxy_buffer_size 128k;
proxy_buffers 4 256k;
proxy_busy_buffers_size 256k;
}
}
Verify the syntax and apply the configuration:
sudo nginx -t
sudo systemctl reload nginx
Production Best Practices for Operating Keycloak
1. SELinux Blocking Connections Between Nginx and Keycloak
After configuring Nginx, administrators often encounter a 502 Bad Gateway error. Checking /var/log/nginx/error.log will show the message Permission denied while connecting to upstream. By default, SELinux on RHEL/CentOS 9 prevents web server processes from initiating outbound network connections. Simply enable the corresponding boolean flag:
sudo setsebool -P httpd_can_network_connect 1
2. Fine-Tuning RAM and JVM Heap Size
The Quarkus-powered Keycloak distribution starts up in just 3 to 5 seconds, significantly faster than the legacy WildFly platform (which typically took 30–45 seconds). Initial baseline memory usage is only around 450MB. However, as the system approaches 1,000 concurrent online users, the JVM will dynamically expand its heap.
On a server with 4GB RAM, you should explicitly constrain the heap size. Create the file /opt/keycloak/conf/kc.opts:
JAVA_OPTS_APPEND="-Xms512m -Xmx2048m -XX:+UseG1GC"
3. Enforce 2FA/MFA for the Master Realm
Identity Provider servers are prime targets for privilege escalation attacks. Log in to the Master Realm, navigate to Authentication > Required Actions, then enable Configure OTP and set it as a Default Action. All administrative accounts will now be required to configure Google Authenticator or a YubiKey upon their first login.
