> 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/kubernetes/prepare-the-cluster.md).

# Prepare the cluster

What any Kubernetes cluster must provide before OpenIAM is deployed into it — capacity, storage, node settings, egress and the database.

## What this covers, and how long it takes

|                |                                                                    |
| -------------- | ------------------------------------------------------------------ |
| **You get**    | A cluster confirmed ready for an OpenIAM deployment                |
| **Time**       | 60–90 minutes, plus provisioning time for storage and the database |
| **Hosts**      | Your cluster, and a machine with `kubectl` and Helm                |
| **Downtime**   | None — nothing is deployed here                                    |
| **Reversible** | Yes                                                                |

## Before you start

* A conformant Kubernetes cluster you have administrative access to. OpenIAM creates cluster-level resources, so a namespace-scoped account is not enough.
* `kubectl` and **Helm 3**, configured against the cluster.
* The OpenIAM deployment materials for your release, from your OpenIAM account team.

## Set these first

{% code overflow="wrap" %}

```bash
export RWX_CLASS=<the storage class that supports ReadWriteMany>
export DB_HOST=<your database endpoint>
export DB_PORT=<3306, 5432, or 1433>
export DIAG_IMAGE=<a small utility image with a shell, reachable from your cluster>
```

{% endcode %}

**Pin the diagnostic image to a version.** `busybox:1.36` is a common choice. On a restricted network, mirror one into your own registry and use that — the checks below are the only thing it is used for, and an unpinned `latest` from a public registry is not something to introduce into a cluster mid-deployment.

***

## Check it

### 1. Kubernetes version

{% code overflow="wrap" %}

```bash
kubectl version -o json | jq -r '.serverVersion.gitVersion'
```

{% endcode %}

**Match the Kubernetes version recorded for this release** in [*System requirements*](/installation/system-requirements.md). Running a materially newer or older version than the release was built against is the kind of difference that surfaces late, in a component you were not looking at.

### 2. Capacity

The deployment runs about thirty pods — the application services, six interfaces, the infrastructure tiers and the reverse proxy.

|                                        | Non-production | Production |
| -------------------------------------- | -------------: | ---------: |
| **Memory available to the deployment** |          32 GB |      40 GB |
| **CPU**                                |              8 |          8 |
| **Storage**                            |          80 GB |     200 GB |

These are the totals the deployment needs, not per node. Confirm the cluster has them free **after** whatever else it is already running:

{% code overflow="wrap" %}

```bash
kubectl top nodes
kubectl describe nodes | grep -A5 'Allocated resources'
```

{% endcode %}

**A cluster that is already near capacity is the most common cause of a deployment that comes up partially.** Pods schedule, then evict, and the symptom looks like an application fault.

### 3. Storage — the requirement worth checking first

**Several components need `ReadWriteMany` volumes**, because more than one pod mounts the same data: the secret store, the workflow engine, the scripting engine, the device manager, the upload area, the interface tier and the LDAP connector among them.

{% code overflow="wrap" %}

```bash
kubectl get storageclass
```

{% endcode %}

You need:

| Requirement                            | Why                                                                                                                        |
| -------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| **A default storage class**            | The broker, cache, search, graph and database charts request storage without naming a class                                |
| **A class supporting `ReadWriteMany`** | For the components above. On EKS that means EFS; on AKS, Azure Files; on your own cluster, NFS or a distributed filesystem |
| **`allowVolumeExpansion: true`**       | So volumes can be grown without a redeploy                                                                                 |

**Confirm `ReadWriteMany` actually works before you deploy**, rather than discovering it when a pod will not start:

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

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

{% endcode %}

**Expected: `Bound` within a minute.** Then remove it:

{% code overflow="wrap" %}

```bash
kubectl delete pvc openiam-rwx-test
```

{% endcode %}

If it stays `Pending`, the class does not provide `ReadWriteMany` and the deployment will not come up. [*Amazon EKS*](/installation/kubernetes/amazon-eks.md) and [*Azure AKS*](/installation/kubernetes/azure-aks.md) cover the provider's own option.

### 4. Node settings

**The search tier requires `vm.max_map_count` of at least 262144 on every node that will run it**, and the chart does not set it for you.

{% code overflow="wrap" %}

```bash
kubectl get nodes -o name | while read -r n; do
  echo "$n"
done
```

{% endcode %}

Set it through your node configuration — a launch template's user data on EKS, a node pool's `linuxOSConfig` on AKS, or your own node image. The provider pages have the specifics.

**Confirm it before deploying**, from a pod with the privilege to read it, or from the node directly:

{% code overflow="wrap" %}

```bash
sysctl vm.max_map_count      # on a node
```

{% endcode %}

**Expected: `262144` or higher.** A lower value stops the search tier starting, and the failure reads as a search-tier crash rather than a node configuration problem.

### 5. Egress

The deployment pulls images and charts from OpenIAM. Allow outbound HTTPS from the cluster to:

| Destination            | For              |
| ---------------------- | ---------------- |
| `registry.openiam.com` | Container images |
| `openiam.jfrog.io`     | Helm charts      |

**For a cluster with no outbound access**, mirror the images into your own registry first. Your OpenIAM account team provides the mirroring tooling and the image list.

### 6. Registry credentials

Images come from OpenIAM's registry and need a pull secret. Your account team issues the credentials with your entitlement; the deployment creates the secret from them.

### 7. The database

**The database is external to the cluster.** Provision it before you deploy — the schema migration runs as part of the deployment and needs a reachable, correctly configured database to run against.

* On AWS or Azure, use the provider's managed service — [*Managed databases*](/installation/rpm/cloud/managed-databases.md) covers which engines and the parameters that must be set beforehand.
* On your own cluster, any supported platform in [*System requirements*](/installation/system-requirements.md).

**Eight schemas**, on one database server: `openiam`, `activiti`, `groovy`, `auditlog`, `batchtasks`, `pbac`, `certification` and `privacy`.

Confirm the cluster can reach it before you deploy:

{% code overflow="wrap" %}

```bash
kubectl run db-check --rm -it --restart=Never --image="$DIAG_IMAGE" -- \
  sh -c 'nc -zv $DB_HOST $DB_PORT'
```

{% endcode %}

**Expected:** a successful connection. A network policy, a security group or a private endpoint that has not been linked is much easier to find now than during a deployment.

### 8. Time and DNS

{% code overflow="wrap" %}

```bash
kubectl run dns-check --rm -it --restart=Never --image="$DIAG_IMAGE" -- \
  nslookup kubernetes.default
```

{% endcode %}

**Expected:** a resolved address. In-cluster DNS carries every connection between OpenIAM's components, so a DNS problem presents as many unrelated component failures at once.

***

## Verify it

A single pass before you deploy:

{% code overflow="wrap" %}

```bash
echo "== version";  kubectl version -o json | jq -r '.serverVersion.gitVersion'
echo "== nodes";    kubectl get nodes
echo "== capacity"; kubectl top nodes 2>/dev/null || echo "  metrics-server not installed"
echo "== classes";  kubectl get storageclass
echo "== default";  kubectl get storageclass -o json \
  | jq -r '.items[] | select(.metadata.annotations."storageclass.kubernetes.io/is-default-class"=="true") | .metadata.name'
echo "== access";   kubectl auth can-i create clusterrole
```

{% endcode %}

**Expected:** the tested Kubernetes version, nodes `Ready`, capacity above the figures in step 2, a default storage class, a `ReadWriteMany` class, and `yes` to the permission check.

## If it goes wrong

| What you see                                         | What it means                                                      | What to do                                                                         |
| ---------------------------------------------------- | ------------------------------------------------------------------ | ---------------------------------------------------------------------------------- |
| The `ReadWriteMany` test PVC stays `Pending`         | The class does not provide it, or its provisioner is not installed | Install the provider's shared-filesystem driver — see the provider pages           |
| `kubectl get storageclass` shows no default          | Charts that do not name a class cannot bind                        | Mark one default with the `storageclass.kubernetes.io/is-default-class` annotation |
| `kubectl auth can-i create clusterrole` returns `no` | The account is namespace-scoped                                    | Use an account with cluster-admin for the deployment                               |
| The database check cannot connect                    | Network policy, security group, or an unlinked private endpoint    | Resolve before deploying; the schema migration runs early and fails hard           |
| Nodes report `vm.max_map_count` below 262144         | The node configuration does not set it                             | Set it in the node pool or launch template, and roll the nodes                     |

## What this procedure does not cover

Creating the cluster itself, your organisation's cluster policies, and monitoring. The provider pages cover cluster creation on EKS and AKS.

## Related

* [*Amazon EKS*](/installation/kubernetes/amazon-eks.md) and [*Azure AKS*](/installation/kubernetes/azure-aks.md)
* [*Managed databases*](/installation/rpm/cloud/managed-databases.md) — provision this before you deploy
* [*System requirements*](/installation/system-requirements.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/kubernetes/prepare-the-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.
