> For the complete documentation index, see [llms.txt](https://docs.openiam.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.openiam.com/installation/cluster.md).

# Quick start — Kubernetes

Deploy OpenIAM into a Kubernetes cluster you already run — the shortest Helm path from a prepared cluster to a console you can sign in to.

This is the shortest supported path to a working OpenIAM deployment on a cluster you already run: Helm, the bundled in-cluster database, and the console open in a browser at the end of it.

**It is the same procedure as the full route, with the decisions made for you.** Every command here comes from the pages it links to, and each step names the page that explains it. When something does not match, the linked page is the authority.

**Take this route for an evaluation, a demonstration, or a development environment.** For production, work through [*Deploy with Helm*](/installation/kubernetes/deploy-with-helm.md) in full — the decisions this page makes for you are the ones a production deployment needs to make deliberately.

**Creating a new AWS or Azure environment from nothing?** [*EKS with Terraform*](/installation/kubernetes/terraform-eks.md) and [*AKS with Terraform*](/installation/kubernetes/terraform-aks.md) build the network, the cluster and the managed database as well, and are the shorter path in that case. This page assumes the cluster already exists.

## What this builds, and how long it takes

<table><thead><tr><th width="122.80000305175781"></th><th></th></tr></thead><tbody><tr><td><strong>You get</strong></td><td>Around thirty pods in one namespace — the application services, the six interfaces, the infrastructure tiers, the interface router and the reverse proxy — with the console reachable</td></tr><tr><td><strong>Time</strong></td><td>Half a day. About an hour preparing the cluster, 30–45 minutes for the releases, up to another 30 for the pods to settle</td></tr><tr><td><strong>Hosts</strong></td><td>Your cluster, and one deployment machine</td></tr><tr><td><strong>Downtime</strong></td><td>None</td></tr><tr><td><strong>Reversible</strong></td><td>Yes — the deployment is a set of Helm releases in one namespace</td></tr></tbody></table>

## Before you start

**A cluster, with:**

|                                                                             |                                                                                                                   |
| --------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
| **Version**                                                                 | The Kubernetes version recorded for this release in [*System requirements*](/installation/system-requirements.md) |
| **Capacity available to the deployment**                                    | 32 GB memory, 8 CPU, 80 GB storage                                                                                |
| **A default storage class**                                                 | Several charts request storage without naming a class                                                             |
| **A class supporting `ReadWriteMany`**                                      | EFS on EKS, Azure Files on AKS, NFS or a distributed filesystem on your own cluster                               |
| **`vm.max_map_count` ≥ 262144 on every node** that will run the search tier | Set it in the node pool or launch template                                                                        |
| **Cluster-admin** for the account doing the deployment                      |                                                                                                                   |
| **Egress** to `registry.openiam.com` and `openiam.jfrog.io`                 | Images and charts                                                                                                 |

Those are the non-production figures. [*Prepare the cluster*](/installation/kubernetes/prepare-the-cluster.md) is the full check and includes a `ReadWriteMany` test you should run before anything else — it is the requirement that most often stops a deployment, and it stops it late.

**A deployment machine**, inside the network the API server is reachable from, with `kubectl` configured against the cluster, **Helm 3**, `git`, `openssl`, and `envsubst` (from the `gettext` package, which is often not installed by default).

**From your OpenIAM account team:** registry credentials, and **the deployment materials with the exact revision issued for this release**. Use that revision and no other — the values files, the chart versions and the image tags are matched to the release.

**All deployments in this release are single-zone.** [*Scope and availability*](/installation/scope-and-availability.md) states what that does and does not protect against.

## Set these first

{% code overflow="wrap" %}

```bash
export NS=openiam
export APP=<the deployment name — it prefixes every release and service name>
export OPENIAM_VERSION=<the release you are installing>
export OPENIAM_URL=<the address users will type>
export RWX_CLASS=<the storage class that supports ReadWriteMany>
```

{% endcode %}

**The deployment name cannot be changed later** without redeploying, and you will see it everywhere.

***

## Build it

### 1. Confirm the cluster is ready — 30 to 60 minutes

{% code overflow="wrap" %}

```bash
kubectl version -o json | jq -r '.serverVersion.gitVersion'
kubectl get nodes
kubectl get storageclass
kubectl auth can-i create clusterrole
```

{% endcode %}

Then prove `ReadWriteMany` actually works, rather than discovering it when a pod will not start:

{% code overflow="wrap" expandable="true" %}

```bash
kubectl create namespace "$NS"
cat <<EOF | kubectl apply -n "$NS" -f -
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: rwx-test
spec:
  accessModes: [ReadWriteMany]
  storageClassName: $RWX_CLASS
  resources:
    requests:
      storage: 1Gi
EOF
kubectl get pvc rwx-test -n "$NS"
```

{% endcode %}

**Expected: `Bound`.** If it stays `Pending`, the class does not provide `ReadWriteMany` and the deployment will not come up. Install your provider's shared-filesystem driver first.

{% code overflow="wrap" %}

```bash
kubectl delete pvc rwx-test -n "$NS"
kubectl config set-context --current --namespace="$NS"
```

{% endcode %}

[*Prepare the cluster*](/installation/kubernetes/prepare-the-cluster.md) covers node settings, egress and the rest. On a managed cluster, read [*Amazon EKS*](/installation/kubernetes/amazon-eks.md) or [*Azure AKS*](/installation/kubernetes/azure-aks.md) too.

### 2. Get the deployment materials — 5 minutes

{% code overflow="wrap" %}

```bash
sudo mkdir -p /usr/local/openiam && cd /usr/local/openiam
git clone <the deployment materials repository> kubernetes-docker-configuration
cd kubernetes-docker-configuration
git checkout <the revision issued for this release>

kubectl version -o json | jq -r '.serverVersion.gitVersion'
helm version --short
which envsubst openssl git
```

{% endcode %}

**Expected:** Helm 3.x and a path for each command. If `envsubst` is missing, install `gettext` — the values files are rendered with it.

### 3. Set the deployment settings — 10 minutes

Three files carry settings and **they must agree with each other.**

**`env.sh`:**

<table><thead><tr><th width="254.79998779296875">Setting</th><th>Value for this route</th></tr></thead><tbody><tr><td><code>APP_NAME</code></td><td>Your deployment name</td></tr><tr><td><code>BUILD_ENVIRONMENT</code></td><td><code>prod</code></td></tr><tr><td><code>OPENIAM_VERSION_NUMBER</code></td><td>The release you are installing</td></tr><tr><td><code>APPLICATION_DOMAIN</code></td><td>Your <code>OPENIAM_URL</code></td></tr><tr><td><code>CLOUD_PROVIDER</code></td><td><code>aws</code>, <code>azure</code>, or <code>other</code> for a cluster you run</td></tr><tr><td><code>TIME_ZONE</code></td><td>The deployment's time zone, in <code>Region/City</code> form</td></tr><tr><td><code>DOCKER_REGISTRY</code></td><td>OpenIAM's registry</td></tr></tbody></table>

**`terraform.tfvars`:** set `app_name` to **the same value as `APP_NAME`**. This route does not run Terraform, but the settings script reads the file to confirm the two are in step and waits until they match.

**`setup-no-tf.sh`**, at the top of the file:

{% code overflow="wrap" %}

```bash
BUILD_ENVIRONMENT="prod"
OPENIAM_VERSION_NUMBER="<the release you are installing>"
APP_NAME="<your deployment name>"
NAMESPACE="<your namespace>"
NUM_WORKER_NODES="1"
FLYWAY_COMMAND="migrate"
DEPLOY_IN_CLUSTER_DATABASE="true"
```

{% endcode %}

**These must match `env.sh` exactly.** The values files are rendered from `env.sh` and the deployment passes its own settings on the command line; if the two disagree, the image tags in the values files disagree with the ones actually deployed.

**Leave the database settings at their defaults.** With `DEPLOY_IN_CLUSTER_DATABASE="true"`, `DB_HOST` resolves to the bundled database's own service and the deployment creates the databases and the eight accounts itself.

> **In this release the Helm route deploys the bundled in-cluster database.** A deployment on your own database server takes a Terraform route, which carries the account provisioning this route does not. [*Deploy with Helm*](/installation/kubernetes/deploy-with-helm.md) step 4 explains why, and [*Managed databases*](/installation/rpm/cloud/managed-databases.md) covers the engines and their parameters.

### 4. Render the values files — 5 minutes

{% code overflow="wrap" %}

```bash
./setup.sh
```

{% endcode %}

**It asks how storage is provided.** Choose **Use own storage class** and give it your `RWX_CLASS`. (**Use openiam nfs pvc provider** is available if you have no `ReadWriteMany` class of your own and want the bundled one for an evaluation.)

Confirm it took:

{% code overflow="wrap" %}

```bash
grep "storageClass:" .deploy/openiam.pvc.values.yaml | sort | uniq -c
ls .deploy/ | wc -l
```

{% endcode %}

**Expected:** your class on the claims, no claim asking for a class you do not have, and `.deploy/` populated with one values file per component.

### 5. Deploy — 30 to 45 minutes, then up to 30 more

**Start a terminal multiplexer first.** The run is long and a dropped session ends it partway through:

{% code overflow="wrap" %}

```bash
sudo apt-get install -y screen
screen -S openiam-deploy
./setup-no-tf.sh
```

{% endcode %}

Detach with **Ctrl+A** then **D**; reattach with `screen -r openiam-deploy`.

**It asks for your registry credentials** the first time — they are stored in the cluster as an image pull secret and are not asked for again. **Then it asks which storage provider to use** — take the same choice you took in step 4.

**What you will see while it runs.** The configuration, database, secret-store backend and interface router releases wait for readiness; the rest are created back to back. Components restart while the tier beneath them comes up, so `Init`, `PodInitializing` and `CrashLoopBackOff` in the first fifteen minutes are the deployment settling rather than a fault. **If a release that waits times out, the run stops there** — that is the signal to look at that release rather than let the rest pile up behind it.

Watch it settle:

{% code overflow="wrap" %}

```bash
kubectl get pods -n "$NS" -w
```

{% endcode %}

**Expected, once settled:** every pod `Running` with all containers ready. Jobs show `Completed` — that is correct. **Give it the full 30 minutes** before treating a restarting pod as a problem.

### 6. Record the credentials — 5 minutes

**The deployment generates every internal credential itself** and stores them in a Kubernetes secret named `secrets` in your namespace. Nothing is printed and nothing is written to the deployment machine.

{% code overflow="wrap" %}

```bash
kubectl get secret secrets -n "$NS" -o jsonpath='{.data}' | jq -r 'keys[]'
```

{% endcode %}

**Back up this secret with the rest of the deployment** — without it the database accounts, the secret store and the search tier cannot be reconstructed. See [*Back up and restore*](/installation/kubernetes/back-up-and-restore.md).

The secret store also produces unseal keys and a root token during its own bootstrap. Record those from the secret-store bootstrap job's output and store them where your organisation keeps break-glass credentials.

### 7. Publish the entry point — 15 minutes

**The reverse proxy is the only component that should be exposed.** The interface router and the services behind it stay inside the cluster.

{% code overflow="wrap" %}

```bash
kubectl get svc -n "$NS" | grep rproxy
```

{% endcode %}

Expose it as your cluster does this — change the proxy service's type to `LoadBalancer`, or point an ingress rule at it on its HTTP port. Your provider's page has the annotations: [*Amazon EKS*](/installation/kubernetes/amazon-eks.md), [*Azure AKS*](/installation/kubernetes/azure-aks.md).

**Health check path: `/idp/login`** — configure your load balancer or ingress to use it rather than `/`.

**TLS.** Terminate at your load balancer or ingress. The proxy serves HTTP inside the cluster and your entry point presents the certificate; nothing further is needed in the deployment. To terminate at the reverse proxy instead, the certificate files must be in place **before step 5** — see [*Deploy with Helm*](/installation/kubernetes/deploy-with-helm.md) step 9.

**Point DNS at it and confirm before you sign in:**

{% code overflow="wrap" %}

```bash
nslookup "$OPENIAM_URL"
curl -s -o /dev/null -w '%{http_code}\n' "https://$OPENIAM_URL/idp/login"
```

{% endcode %}

**Expected:** a resolved address, and `200` or a `30x`.

**The address must be settled before first sign-in.** It is recorded in the platform's configuration at that point, and changing it afterwards means editing the content provider.

### 8. Verify, then sign in — 30 minutes

Work through [*Verify a Kubernetes deployment*](/installation/kubernetes/verify.md). It is a genuine check that the deployment works, not a repeat of `kubectl get pods`.

Then [*First sign-in*](/installation/rpm/rpm-single-node/first-sign-in.md). **Two of its answers are specific to Kubernetes:**

<table><thead><tr><th width="240.39999389648438">Field</th><th>Value</th></tr></thead><tbody><tr><td><strong>Application supports SSL?</strong></td><td><code>Yes</code> if users reach the platform over HTTPS, whether TLS is terminated at your load balancer or at the reverse proxy</td></tr><tr><td><strong>Application Servers</strong></td><td><code>http://$APP-nginx:8080</code> — the interface router's service, using your deployment name</td></tr></tbody></table>

***

## Verify it

One pass over the finished deployment:

{% code overflow="wrap" %}

```bash
echo "== releases"; helm list -n "$NS"
echo "== pods";     kubectl get pods -n "$NS" --field-selector=status.phase!=Running
echo "== claims";   kubectl get pvc -n "$NS"
echo "== proxy";    kubectl get svc -n "$NS" | grep rproxy
echo "== entry";    curl -s -o /dev/null -w '%{http_code}\n' "https://$OPENIAM_URL/idp/login"
```

{% endcode %}

Keep the output with your build record.

## If it goes wrong

| What you see                                         | What it means                                                      | What to do                                                                                          |
| ---------------------------------------------------- | ------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------- |
| The `ReadWriteMany` test claim stays `Pending`       | The class does not provide it, or its provisioner is not installed | Install the provider's shared-filesystem driver before deploying                                    |
| The run stops at a release that waits                | That release did not become ready                                  | Look at that release's pods and events — [*Troubleshoot*](/installation/kubernetes/troubleshoot.md) |
| Pods restarting in the first fifteen minutes         | The deployment settling as each tier comes up                      | Wait the full 30 minutes                                                                            |
| The search tier will not start                       | `vm.max_map_count` is below 262144 on the node                     | Set it in the node pool or launch template and roll the nodes                                       |
| Image pull failures                                  | Registry credentials, or no egress to the registry                 | Check the image pull secret and the cluster's egress                                                |
| `kubectl auth can-i create clusterrole` returns `no` | The account is namespace-scoped                                    | Use an account with cluster-admin for the deployment                                                |
| `Service Not Available` in the browser               | The proxy cannot reach the interface router                        | [*Troubleshoot*](/installation/kubernetes/troubleshoot.md)                                          |

## What this procedure does not cover

* **A database on your own server.** In this release that is a Terraform route — [*EKS with Terraform*](/installation/kubernetes/terraform-eks.md), [*AKS with Terraform*](/installation/kubernetes/terraform-aks.md).
* **Creating the cluster.** This route deploys into one that exists. The Terraform routes create the network, the cluster and the database together.
* **Redundancy across zones.** All deployments in this release are single-zone — [*Scope and availability*](/installation/scope-and-availability.md).
* **Backup.** Nothing here is backed up until you set it up. The bundled in-cluster database has no backup regime of its own — [*Back up and restore*](/installation/kubernetes/back-up-and-restore.md).
* **Restricted networks.** Mirroring the images and charts into your own registry is covered in [*Deploy with Helm*](/installation/kubernetes/deploy-with-helm.md).
* **Tuning and application configuration.** Default sizing, and no managed systems, roles or policies.

## Related

* [*Deploy with Helm*](/installation/kubernetes/deploy-with-helm.md) — the full procedure, and the authority
* [*Prepare the cluster*](/installation/kubernetes/prepare-the-cluster.md)
* [*Verify a Kubernetes deployment*](/installation/kubernetes/verify.md)
* [*Quick start — one host*](/installation/one-host.md)
* [*Choose your route*](/installation/choose-your-route.md)

*Applies to OpenIAM 2026.8.1.*


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.openiam.com/installation/cluster.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
