# Deploy a self-hosted identity provider with the auth-oidc template

Source: https://docs.quake.ai/resources/deployments/deploy-auth-oidc-template
Markdown: https://docs.quake.ai/resources/deployments/deploy-auth-oidc-template.md

---

# Deploy a self-hosted identity provider with the auth-oidc template

Stand up [Keycloak](https://www.keycloak.org), an open-source OIDC identity provider, on a single Quake AI instance using the [validated OpenTofu template](/docs/platform/validation#how-infrastructure-templates-are-checked) `auth-oidc`. You apply the template, sign in to the bootstrap admin console, create a realm and a client, point a domain at the host and serve it over HTTPS, switch Keycloak to production mode, and register a redirect URI for a test application.

Keycloak keeps your sign-in flows on infrastructure you own. You run it yourself; this is a self-hosted tool you operate, not a managed service.

<Figure size="md" caption="What you'll build: a Keycloak host on a single instance with a bundled PostgreSQL, reached over HTTPS through a Caddy reverse proxy once you move off the start-dev bootstrap">

```d2
direction: right

user: Application user {shape: person}
app: Your application
fip: Floating IP
instance: Ubuntu instance {
  caddy: Caddy\nreverse proxy
  keycloak: Keycloak\n(realm + client)
  db: PostgreSQL
  caddy -> keycloak: proxies 443 to 8080
  keycloak -> db: realms, clients, users
}

user -> app: sign-in
app -> fip: OIDC redirect (HTTPS)
fip -> instance.caddy
```

</Figure>

<PricingCompanion
  components={[
    { kind: "template", slug: "auth-oidc", required: true },
  ]}
/>

## Prerequisites

You need:

- OpenTofu 1.6.0 or later (or Terraform 1.6.0 or later) installed locally.
- Your OpenStack credentials sourced into the shell (`source openrc.sh`). See [the OpenStack CLI guide](/docs/tools/openstack-cli).
- An SSH keypair that already exists in your project. Record its name for the `key_name` variable.
- A copy of the `auth-oidc` template directory from [the template reference page](/resources/iac-templates/auth-oidc).
- A domain you can point at the instance once you move off the `start-dev` bootstrap.

## Step 1: Apply the template

Copy the template's example variables file and set `key_name`:

```bash
cp terraform.tfvars.example terraform.tfvars
```

```hcl
key_name = "YOUR_KEY_NAME"
```

Initialize, preview, and apply:

```bash
tofu init
tofu plan
tofu apply
```

OpenTofu provisions a private network, a router, a security group, a block volume mounted at `/var/lib/docker`, an instance, and a floating IP. On first boot, cloud-init installs Docker Engine, generates the bootstrap admin password and the database password into `/opt/auth-oidc/.env`, and starts Keycloak in `start-dev` mode against the bundled PostgreSQL.

Read the outputs and record `floating_ip`:

```bash
tofu output
```

## Step 2: Read the bootstrap admin password and sign in

SSH to the instance and read the generated password:

```bash
sudo grep KC_BOOTSTRAP_ADMIN_PASSWORD /opt/auth-oidc/.env
```

Open `http://YOUR_FLOATING_IP:8080` (reachable from `app_allowed_cidr`, the private network by default; tunnel over SSH if you set it to the default). Sign in to the **Administration Console** with username `admin` and the password you read above.

## Step 3: Create a realm and a client

1. In the top-left realm selector, select **Create realm**. Name it for your application (for example `myapp`).
2. Inside the new realm, go to **Clients** > **Create client**.
3. Set **Client type** to `OpenID Connect`, give it a **Client ID** (for example `myapp-web`), and continue.
4. Enable **Client authentication** if your application can hold a secret (confidential client), or leave it off for a public client (a single-page app).
5. Set **Valid redirect URIs** to your application's callback path, for example `https://app.example.com/auth/callback`. You can update this later once you have a real domain.
6. Save. If you enabled client authentication, open the **Credentials** tab and record the client secret.

## Step 4: Point a domain at the host and serve HTTPS with Caddy

Keycloak's production mode refuses to issue tokens over plain HTTP, so it needs a domain with TLS before real applications sign users in through it.

1. Create a DNS **A record** for your domain (for example `auth.example.com`) pointing at `YOUR_FLOATING_IP`. Follow [How to point a domain at a Quake AI resource](/docs/network/how-to/point-domain-to-quake-ai). Wait until it resolves:

```bash
dig +short auth.example.com
```

2. SSH to the instance and create `/opt/auth-oidc/Caddyfile`:

```text
auth.example.com {
  reverse_proxy 127.0.0.1:8080
}
```

3. Add Caddy to `/opt/auth-oidc/docker-compose.yml`:

```yaml
services:
  caddy:
    image: caddy:2
    restart: unless-stopped
    network_mode: host
    volumes:
      - /opt/auth-oidc/Caddyfile:/etc/caddy/Caddyfile
      - caddy_data:/data
volumes:
  caddy_data:
```

For background on certificates, see [How to issue and auto-renew a TLS certificate with Let's Encrypt](/docs/network/how-to/lets-encrypt-certificate).

## Step 5: Switch to production mode

Edit `/opt/auth-oidc/.env` and uncomment the production block:

```text
KC_HOSTNAME=https://auth.example.com
KC_PROXY_HEADERS=xforwarded
KC_HTTP_ENABLED=true
```

Edit `/opt/auth-oidc/docker-compose.yml` and change the `keycloak` service's `command` from `start-dev` to `start`. Then restart:

```bash
cd /opt/auth-oidc
sudo docker compose up -d
```



The realm, client, and user data lives in the bundled PostgreSQL on the attached volume, not in the Keycloak container. Switching `start-dev` to `start` restarts the server against the same database; nothing you configured in Step 3 is lost.



## Step 6: Verify the realm and update the redirect URI

1. Confirm the realm's OIDC discovery document resolves over your domain:

```bash
curl -s https://auth.example.com/realms/myapp/.well-known/openid-configuration | head
```

2. In the admin console, update the client's **Valid redirect URIs** to your application's real production callback, now that you have a domain.
3. Point your application's OIDC client library at the discovery URL above, with the client ID (and secret, for a confidential client) from Step 3.

## What you built

- **Applied the `auth-oidc` template** to provision a network, security group, data volume, instance, and floating IP, with Keycloak and PostgreSQL started by cloud-init
- **Created a realm and a client** in Keycloak's admin console
- **Served Keycloak over HTTPS** by pointing a domain at the floating IP and routing it through a Caddy reverse proxy
- **Switched Keycloak from `start-dev` to production `start` mode**, which Keycloak requires before it issues real tokens over HTTPS
- **Verified the realm's OIDC discovery document** and updated the client's redirect URI for production use

## Scope of this deployment

This template runs a single-VM Keycloak host, not a managed identity cloud. The instance is CPU-only and runs in one region, and it bundles PostgreSQL as a container on the same host. You operate the instance, Docker, Keycloak, the database, and the data volume yourself: back them up, patch them, and snapshot the volume before you resize or rebuild. For larger deployments, move PostgreSQL onto its own instance and run Keycloak in clustered mode, which this template does not set up.

## Next steps

- [Self-hosted OIDC identity provider template](/resources/iac-templates/auth-oidc): the template reference, parameters, and resource map
- [Self-managed PostgreSQL template](/resources/iac-templates/self-managed-postgres): the database to point at when you outgrow the bundled one
- [Deploy Outline with the outline-docs template](/resources/deployments/deploy-outline-docs-template): a worked example of an application that consumes an OIDC provider like this one
- [Self-hosted vibecode stack](/resources/solutions/self-hosted-vibecode-stack): where auth fits in the broader self-hosted stack
- [Security hardening checklist](/docs/security/hardening-checklist): tighten SSH access and exposure before you serve real traffic

## Clean up

When you no longer need the deployment, destroy everything the template created:

```bash
tofu destroy
```

Then remove the DNS A record you created in Step 4 and any client registrations your test application held. Because Keycloak, its database, and the realm data all live on the instance and its attached volume, `tofu destroy` removes them along with the infrastructure. Export your realm configuration first if you want to keep it: **Realm settings** > **Action** > **Partial export**.
