12 min read

How to Self-Host Keycloak and Add Single Sign-On to Your Apps

Self-host Keycloak with Docker Compose to add SSO, OAuth 2.0, and OpenID Connect to all your apps. Make your auth server publicly accessible with a Localtonet tunnel.

🔐 Keycloak · SSO · Identity Management · Self-Host

How to Self-Host Keycloak and Add Single Sign-On to Your Apps

Keycloak is an open-source identity and access management platform that handles authentication so you do not have to build it yourself. One login, multiple apps, full control over your users and tokens. This guide covers installing Keycloak with Docker Compose, setting up a realm and client, and making the auth server reachable from anywhere using a Localtonet tunnel.

🔑 Single Sign-On for all your apps 🛡️ OAuth 2.0 and OpenID Connect 👥 User and role management 🌍 Accessible from anywhere

What Is Keycloak?

Keycloak is an open-source Identity and Access Management (IAM) solution developed and maintained by Red Hat. It centralizes authentication and authorization for all your applications behind a single login screen. Instead of building a user database, a login page, a password reset flow, and token management into every app you create, you point each app at Keycloak and let it handle all of that.

It supports OAuth 2.0, OpenID Connect, and SAML 2.0 out of the box, which means it works with virtually any modern web framework or language. Users log in once and get access to every connected application without logging in again that is Single Sign-On (SSO). When they log out, they are logged out everywhere.

🔑 Single Sign-On Users log in once and access all connected apps. Logging out from one app logs them out everywhere.
🛡️ Standard protocols OAuth 2.0, OpenID Connect, and SAML 2.0. Works with any framework that supports these standards.
🌐 Social login Let users sign in with Google, GitHub, Microsoft, Facebook, or any other OAuth provider with a few clicks of configuration.
🔒 Multi-factor authentication Add TOTP, WebAuthn, or SMS-based MFA for any user or group without changing your applications.
👥 User federation Connect to an existing LDAP or Active Directory. Keycloak syncs users from your directory without replacing it.
🎨 Customizable login pages Brand the login, registration, and password reset screens with your own theme and styles.

Install Keycloak with Docker Compose

The setup below runs Keycloak with PostgreSQL for persistent storage. SQLite is not supported by Keycloak PostgreSQL is the recommended database for all setups, including single-server development environments.

1

Create the project directory

mkdir keycloak && cd keycloak
2

Create the Docker Compose file

services:
  postgres:
    image: postgres:16-alpine
    container_name: keycloak-db
    restart: unless-stopped
    environment:
      POSTGRES_DB: keycloak
      POSTGRES_USER: keycloak
      POSTGRES_PASSWORD: changeme
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U keycloak"]
      interval: 10s
      timeout: 5s
      retries: 5

  keycloak:
    image: quay.io/keycloak/keycloak:latest
    container_name: keycloak
    restart: unless-stopped
    environment:
      KC_BOOTSTRAP_ADMIN_USERNAME: admin
      KC_BOOTSTRAP_ADMIN_PASSWORD: changeme
      KC_DB: postgres
      KC_DB_URL: jdbc:postgresql://postgres:5432/keycloak
      KC_DB_USERNAME: keycloak
      KC_DB_PASSWORD: changeme
      KC_HOSTNAME: localhost
      KC_HOSTNAME_STRICT: "false"
      KC_HTTP_ENABLED: "true"
      KC_HEALTH_ENABLED: "true"
    ports:
      - "127.0.0.1:8080:8080"
    depends_on:
      postgres:
        condition: service_healthy
    command: start-dev

volumes:
  postgres_data:
Change both passwords before starting

Replace changeme in all three places the PostgreSQL password, the Keycloak database password, and the bootstrap admin password with a strong value before running the stack. Use the same password for POSTGRES_PASSWORD and KC_DB_PASSWORD.

3

Start the stack

docker compose up -d
docker compose ps

Wait about 30 seconds for Keycloak to finish starting. Then open http://localhost:8080 in your browser. Click Administration Console and log in with the credentials you set in KC_BOOTSTRAP_ADMIN_USERNAME and KC_BOOTSTRAP_ADMIN_PASSWORD.

About start-dev mode

The start-dev command runs Keycloak in development mode with relaxed security settings. This is appropriate for local setups accessed through a Localtonet tunnel. For a production deployment where Keycloak is the public-facing auth server for many users, switch to start --optimized and configure proper TLS on Keycloak itself.

Create a Realm and Register Your App

A realm in Keycloak is an isolated namespace for a set of users, roles, and applications. The master realm is for Keycloak administration only you should create a separate realm for each project or group of applications you want to manage.

1

Create a new realm

In the Admin Console, click the dropdown in the top-left corner that shows Keycloak next to the current realm name. Click Create realm. Enter a name such as myrealm and click Create. Keycloak switches to your new realm automatically.

2

Create a client for your application

Go to Clients → Create client. Set Client type to OpenID Connect and give it a Client ID for example myapp. Click Next.

3

Configure the redirect URI

On the capability configuration screen, enable Client authentication if your app is a confidential client (server-side web app). Click Next. On the login settings screen, add your app's callback URL under Valid redirect URIs, for example http://localhost:3000/*. Click Save.

4

Copy the client secret

Go to the Credentials tab of your client and copy the Client secret. You will use this along with the Client ID in your application's authentication configuration.

OpenID Connect discovery URL

Most frameworks can auto-configure by fetching the OpenID Connect discovery document. For your realm, the discovery URL is:

http://localhost:8080/realms/myrealm/.well-known/openid-configuration

Add Users and Roles

1

Create a user

Go to Users → Add user. Fill in the username and email. Toggle Email verified to on for development purposes so the user can log in immediately. Click Create.

2

Set a password for the user

Open the user, go to the Credentials tab, click Set password, enter a password, and turn off Temporary so the user is not forced to change it on first login. Click Save.

3

Create a role and assign it to the user

Go to Realm roles → Create role, enter a name such as user or admin, and click Save. Then open the user, go to Role mapping → Assign role, select the role, and click Assign. Keycloak includes this role in the user's token, which your application can read to control access.

Test the login by opening the Keycloak account console for your realm: http://localhost:8080/realms/myrealm/account. Log in with the user you created and confirm everything is working before moving on.

Make Keycloak Accessible from Anywhere with Localtonet

Keycloak listens on port 8080 and is bound to 127.0.0.1 in the Compose file above, so nothing outside the machine can reach it. To use Keycloak as the auth server for apps running on other machines, phones, or the cloud, you need a public URL. A Localtonet HTTP tunnel gives you one in under a minute with a valid HTTPS certificate included.

1

Install and authenticate Localtonet

curl -fsSL https://localtonet.com/install.sh | sh
localtonet --authtoken <YOUR_TOKEN>
2

Create an HTTP tunnel for port 8080

Log in to the Localtonet dashboard, go to Tunnels → New Tunnel, select HTTP, set local IP to 127.0.0.1 and port to 8080. Click Create. You get a public HTTPS URL such as https://abc123.localto.net.

3

Update KC_HOSTNAME in the Compose file

Keycloak needs to know its own public address so it generates correct redirect URLs and tokens. Update the environment variable and restart the stack.

environment:
  KC_HOSTNAME: abc123.localto.net
  KC_HOSTNAME_STRICT: "false"
  KC_HTTP_ENABLED: "true"
docker compose up -d
4

Update the redirect URI in your client

Go to Clients → your client → Settings and add your application's public callback URL to Valid redirect URIs. If your app also uses Localtonet to expose its callback, add that tunnel URL here.

5

Update the discovery URL in your application

Point your application's OpenID Connect configuration to the public Keycloak URL:

https://abc123.localto.net/realms/myrealm/.well-known/openid-configuration

Your application can now authenticate users through Keycloak from any network. The login page, token issuance, and session management all work through the Localtonet tunnel.

Keep Everything Running After a Reboot

Both containers have restart: unless-stopped so they come back automatically with Docker after a reboot. Register Localtonet as a systemd service so the tunnel also returns without any manual steps:

sudo localtonet --install-service --authtoken <YOUR_TOKEN>
sudo localtonet --start-service --authtoken <YOUR_TOKEN>

Verify all services are active:

docker compose ps
systemctl status localtonet

Security Recommendations

🔑 Do not use the master realm for applications

The master realm is for Keycloak administration only. Create a separate realm for each project and manage your application users there. This isolates admin credentials from application users and prevents accidental privilege escalation.

🛡️ Change the bootstrap admin password immediately

The KC_BOOTSTRAP_ADMIN_PASSWORD variable creates a temporary admin account on first start. After logging in, go to Admin → Manage account and change the password to a strong one. The bootstrap credentials in the Compose file are only used on the very first startup.

🔒 Use a custom domain for a stable and trusted HTTPS URL

Attach a custom domain to your Localtonet HTTP tunnel. Keycloak's tokens embed the issuer URL, so a stable domain means tokens stay valid across tunnel restarts. A changing URL would invalidate existing sessions and require reconfiguring all connected apps. See the custom domain guide for setup steps.

🔐 Enable MFA for admin accounts

Go to Realm settings → Authentication → Required actions and enable Configure OTP as a default action. For the admin account specifically, go to the user record and require OTP under role mapping. A compromised Keycloak admin account means compromised authentication for every connected app.

💾 Back up the PostgreSQL volume regularly

All realms, clients, users, roles, and session data live in the PostgreSQL database. Back up the postgres_data Docker volume or dump the database regularly. A simple approach is a daily cron job that runs docker compose exec postgres pg_dump -U keycloak keycloak and stores the output somewhere safe.

Frequently Asked Questions

What is the difference between a realm, a client, and a user in Keycloak?

A realm is an isolated container with its own set of users, roles, and clients think of it as a tenant. A client is an application registered in Keycloak that uses it for authentication your web app, API, or mobile app. A user is a person who can log in to the applications within that realm. Each realm is completely independent from others.

Can I let users log in with Google or GitHub?

Yes. Go to Identity providers in your realm and add Google, GitHub, Microsoft, or any other OAuth provider. You will need to register an OAuth app on the provider's developer console and copy the client ID and secret into Keycloak. After that, users see a social login button on the Keycloak login page and can authenticate with their existing accounts.

How do I connect Keycloak to an existing LDAP or Active Directory?

Go to User federation → Add provider → ldap. Enter your LDAP server address, the bind DN and credentials, and the user search base. Keycloak syncs users from the directory and can authenticate them against LDAP passwords. Existing application code does not change it still talks to Keycloak using OpenID Connect.

The Localtonet tunnel URL keeps changing. Will that break Keycloak?

Yes, it can. Keycloak embeds the issuer URL (KC_HOSTNAME) in every token it issues. If the URL changes, existing tokens become invalid and redirect URIs in client configurations no longer match. The solution is to attach a custom domain to your Localtonet tunnel. The domain never changes, so tokens stay valid and client configurations remain correct across restarts.

Can I use Keycloak to secure a self-hosted app like Grafana or Gitea?

Yes. Both Grafana and Gitea support OpenID Connect as an authentication provider. In Grafana, configure the [auth.generic_oauth] section in grafana.ini pointing at your Keycloak realm. In Gitea, go to Site Administration → Authentication Sources → Add Authentication Source and select OAuth2. Users can then log in to both apps using their Keycloak credentials with a single click.

Is Keycloak suitable for a small personal project or is it overkill?

Keycloak is powerful and comes with a learning curve. For a single app where you just need login and registration, it is probably more than you need. Where Keycloak shines is when you have multiple apps that should share the same user accounts and login session, or when you want to add social login, MFA, or LDAP integration without writing any authentication code yourself. If that matches your situation, the setup effort pays off quickly.

One Login for All Your Apps on Your Own Infrastructure

Install Keycloak with Docker Compose, create a realm, register your apps as clients, and open a Localtonet tunnel. Your self-hosted SSO server is ready in under an hour.

Create Free Localtonet Account →

Localtonet is a secure multi-protocol tunneling and proxy platform designed to expose localhost, devices, private services, and AI agents to the public internet supporting HTTP/HTTPS tunnels, TCP/UDP forwarding, mobile proxy infrastructure, file server publishing, latency-optimized game connectivity, and developer-ready AI agent endpoint exposure from a single unified control plane.

support