> 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/verify.md).

# Verify a Kubernetes deployment

Confirm a Kubernetes deployment is genuinely running — releases, pods, jobs, storage, every tier, and the published address.

## What this covers, and how long it takes

|                |                                                                                                                                                                                                              |
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **You get**    | Evidence the deployment is running, not merely created                                                                                                                                                       |
| **Time**       | 30 minutes, plus up to 30 minutes waiting after a first deployment                                                                                                                                           |
| **Hosts**      | The machine you deployed from                                                                                                                                                                                |
| **Downtime**   | None                                                                                                                                                                                                         |
| **Reversible** | Yes. Most commands only read. Three checks run a short-lived diagnostic pod, which removes itself when the check finishes — they create nothing that outlives the check and change nothing in the deployment |

## Before you start

* A deployment completed — [*Deploy with Helm*](/installation/kubernetes/deploy-with-helm.md), [*EKS with Terraform*](/installation/kubernetes/terraform-eks.md) or [*AKS with Terraform*](/installation/kubernetes/terraform-aks.md).
* `kubectl` and Helm configured against the cluster, from the machine you deployed from.

**Give a first deployment up to 30 minutes to settle.** Components start before the tiers beneath them are ready and restart until they are, so restarts during that window are the deployment settling rather than a fault.

## Set these first

Every command below uses these, so nothing on this page has an unexplained placeholder:

{% code overflow="wrap" %}

```bash
export NS=<the namespace you deployed into>
export APP=<your deployment name>
export OPENIAM_URL=<the published address>
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 rather than pulling `latest` from a public registry.

***

## Verify it

### 1. Every release is deployed

{% code overflow="wrap" %}

```bash
helm list -n "$NS"
```

{% endcode %}

**Expected:** every release with `STATUS: deployed`. A release showing `failed` or `pending-upgrade` is where to start — nothing below it will be right.

**On a Terraform deployment**, check the apply completed and review its outputs:

{% code overflow="wrap" %}

```bash
terraform output
```

{% endcode %}

### 2. Every pod is running

{% code overflow="wrap" %}

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

{% endcode %}

**Expected:** every pod either `Running` with its full complement ready — `1/1`, `2/2` — or `Completed`.

**`Completed` is correct for jobs and wrong for services.** The schema migration and the secret store's bootstrap are jobs; they finish and stay finished. Everything else should be `Running`.

A count while you wait:

{% code overflow="wrap" %}

```bash
kubectl get pods -n "$NS" --no-headers | awk '{print $3}' | sort | uniq -c
```

{% endcode %}

**Anything in `Pending`, `Init`, `CrashLoopBackOff` or `ImagePullBackOff` after 30 minutes** is worth investigating — go to step 9.

### 3. Every job completed

{% code overflow="wrap" %}

```bash
kubectl get jobs -n "$NS"
```

{% endcode %}

**Expected:** every job showing `1/1` completions.

**The schema migration matters most.** If it did not complete, the platform has no database schema to run against:

{% code overflow="wrap" %}

```bash
kubectl logs -n "$NS" job/$(kubectl get jobs -n "$NS" -o name | grep -i flyway | head -1 | cut -d/ -f2) | tail -20
```

{% endcode %}

**Expected:** migration output ending in success, with no `ERROR` lines.

### 4. Storage is bound

{% code overflow="wrap" %}

```bash
kubectl get pvc -n "$NS"
```

{% endcode %}

**Expected:** every claim `Bound`.

A claim still `Pending` is the most common reason a pod never leaves `Init`. **Check whether the pending claims are the shared ones** — several components need `ReadWriteMany`, and a cluster with only `ReadWriteOnce` storage binds some claims and not others:

{% code overflow="wrap" %}

```bash
kubectl get pvc -n "$NS" -o custom-columns=NAME:.metadata.name,STATUS:.status.phase,MODE:.spec.accessModes[0],CLASS:.spec.storageClassName
```

{% endcode %}

[*Prepare the cluster*](/installation/kubernetes/prepare-the-cluster.md) step 3 covers what to do about it.

### 5. The secret store is unsealed

Everything the platform does needs a credential from the secret store, so a sealed store presents as many unrelated failures at once.

{% code overflow="wrap" %}

```bash
kubectl exec -n "$NS" "$(kubectl get pod -n "$NS" -l app.kubernetes.io/name=vault -o name | head -1)" -- \
  sh -c 'wget -qO- --no-check-certificate https://localhost:8200/v1/sys/seal-status'
```

{% endcode %}

**Expected:** `"sealed":false` and `"initialized":true`.

If the label selector returns nothing, find the pod by name and substitute it:

{% code overflow="wrap" %}

```bash
kubectl get pods -n "$NS" | grep -i vault
```

{% endcode %}

### 6. The secret store's backend has a leader

{% code overflow="wrap" %}

```bash
kubectl exec -n "$NS" "$(kubectl get pod -n "$NS" | grep consul-server | head -1 | awk '{print $1}')" -- \
  consul members
```

{% endcode %}

**Expected:** the server members listed as `alive`. On a simple deployment there is one.

### 7. The infrastructure tiers are answering

**Take credentials from the values you set rather than typing them into a shell.** On a Terraform deployment they are the ones you put in `terraform.tfvars`; read them back from the Kubernetes secret so they are not in your shell history:

{% code overflow="wrap" %}

```bash
export OS_USER="$(kubectl get secret -n "$NS" secrets -o jsonpath='{.data.opensearchUsername}' | base64 -d)"
export OS_PASS="$(kubectl get secret -n "$NS" secrets -o jsonpath='{.data.opensearchPassword}' | base64 -d)"
export REDIS_PASS="$(kubectl get secret -n "$NS" secrets -o jsonpath='{.data.redisPassword}' | base64 -d)"
```

{% endcode %}

If a key name differs in your deployment, list them: `kubectl get secret -n "$NS" secrets -o jsonpath='{.data}' | tr ',' '\n'`.

**Graph storage:**

{% code overflow="wrap" %}

```bash
kubectl exec -n "$NS" "$(kubectl get pod -n "$NS" | grep '^cassandra' | head -1 | awk '{print $1}')" -- \
  nodetool status
```

{% endcode %}

**Expected:** each node `UN` — up and normal.

**Message broker:**

{% code overflow="wrap" %}

```bash
kubectl exec -n "$NS" "$(kubectl get pod -n "$NS" | grep rabbitmq | head -1 | awk '{print $1}')" -- \
  rabbitmqctl cluster_status
```

{% endcode %}

**Expected:** the running node listed.

**Cache:**

{% code overflow="wrap" %}

```bash
kubectl exec -n "$NS" "$(kubectl get pod -n "$NS" | grep redis | head -1 | awk '{print $1}')" -- \
  sh -c "redis-cli -a \"$REDIS_PASS\" --no-auth-warning info replication | grep '^role'"
```

{% endcode %}

**Expected:** `role:master`.

**Search tier:**

{% code overflow="wrap" %}

```bash
kubectl exec -n "$NS" "$(kubectl get pod -n "$NS" | grep opensearch | head -1 | awk '{print $1}')" -- \
  curl -sk -u "$OS_USER:$OS_PASS" https://localhost:9200/_cluster/health
```

{% endcode %}

**Expected:** `"status":"green"` or `"yellow"`. **`yellow` is correct on a single-replica deployment** — replica shards have nowhere to go.

**Every one of these `kubectl exec` commands resolves the pod name for you.** Do not substitute a StatefulSet name; `kubectl exec` needs a pod.

### 8. The application services and interfaces are healthy

{% code overflow="wrap" %}

```bash
kubectl get pods -n "$NS" -o custom-columns=NAME:.metadata.name,READY:.status.containerStatuses[0].ready \
  | grep -Ei 'esb|idm|auth|workflow|idp|webconsole|selfservice|reportviewer|ui-static|ui-v2'
```

{% endcode %}

**Expected:** `true` on every row.

Then the interfaces' own health endpoints, from inside the cluster:

{% code overflow="wrap" %}

```bash
for a in webconsole:8082 idp:8081 selfservice:8083 \
         selfservice-ext:8084 reportviewer:8085 openiam-ui-static:8086; do
  app=${a%%:*}; port=${a##*:}
  printf '%-22s ' "$app"
  kubectl run "check-$app" -n "$NS" --rm -q -i --restart=Never --image="$DIAG_IMAGE" -- \
    wget -q -T 5 -O - "http://${APP}-${app}:${port}/${app}/actuator/health" 2>/dev/null || echo "no response"
  echo
done
```

{% endcode %}

**Expected:** a body containing `"status":"UP"` for each.

**The service names follow your deployment name.** Confirm them if a check fails:

{% code overflow="wrap" %}

```bash
kubectl get svc -n "$NS"
```

{% endcode %}

### 9. The published address answers

{% code overflow="wrap" %}

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

{% endcode %}

Then, from outside the cluster through whatever exposes that service:

{% code overflow="wrap" %}

```bash
curl -s -o /dev/null -w '%{http_code}\n' https://$OPENIAM_URL/webconsole
curl -k -I -L https://$OPENIAM_URL/idp/login
```

{% endcode %}

**Expected: `200`, or a `30x` redirect to a sign-in page, from both.** Together these exercise the reverse proxy, the interface router and the identity provider.

### 10. If a check failed

Work upward from the bottom of the stack:

1. **Storage** — a pending claim stops everything above it.
2. **The secret store and its backend.**
3. **The remaining tiers** — graph storage, cache, broker, search.
4. **The application services.**

For any pod that is not running:

{% code overflow="wrap" %}

```bash
kubectl describe pod -n "$NS" <pod>      # events explain scheduling and mount problems
kubectl logs -n "$NS" <pod> --previous   # the log from the run that failed
```

{% endcode %}

**`describe` before `logs`.** A pod that never started has no log worth reading, and its events say why.

### 11. Record the result

Keep the output of steps 1 to 9 with your build record.

***

## If it goes wrong

| What you see                                 | What it means                                            | What to do                                                                                                         |
| -------------------------------------------- | -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
| A release shows `failed`                     | That chart did not install                               | `helm status <release> -n "$NS"`, then the pod events beneath it                                                   |
| Pods `Pending`                               | Storage or capacity                                      | [*Prepare the cluster*](/installation/kubernetes/prepare-the-cluster.md) steps 2 and 3                             |
| Only the shared-storage claims are `Pending` | No `ReadWriteMany` class                                 | [*Prepare the cluster*](/installation/kubernetes/prepare-the-cluster.md) step 3                                    |
| The schema migration job failed              | The database is unreachable, or its parameters are wrong | [*Managed databases*](/installation/rpm/cloud/managed-databases.md), then delete the job and re-run the deployment |
| The secret store reports `sealed: true`      | It was restarted without a clean shutdown                | Unseal with the keys recorded at deployment                                                                        |
| The search tier restarts repeatedly          | `vm.max_map_count` on the nodes                          | Your provider's page                                                                                               |
| Every pod healthy, console does not load     | The reverse proxy has no working backend                 | [*Troubleshoot*](/installation/kubernetes/troubleshoot.md)                                                         |
| `curl` to the published address times out    | The service is not exposed, or DNS does not resolve      | Step 9                                                                                                             |

[*Troubleshoot*](/installation/kubernetes/troubleshoot.md) has the full symptom index.

## What to do next

* **Complete first sign-in** — [*First sign-in*](/installation/rpm/rpm-single-node/first-sign-in.md), using the published address as the content provider's domain pattern.
* **Connect your first managed system, define policy and synchronise users** — the [Administration Guide](/administration-guide/admin.md).
* **Take a backup** — [*Back up and restore*](/installation/rpm/rpm-single-node/back-up-and-restore.md), including the Kubernetes-specific set.

## What this procedure does not cover

Configuring the platform, and your cluster's own monitoring. This page confirms the deployment runs; [*First sign-in*](/installation/rpm/rpm-single-node/first-sign-in.md) comes next. Symptom-by-symptom diagnosis is [*Troubleshoot*](/installation/kubernetes/troubleshoot.md).

## Related

* [*Troubleshoot*](/installation/kubernetes/troubleshoot.md)
* [*Prepare the cluster*](/installation/kubernetes/prepare-the-cluster.md)
* [*Diagnostics*](/installation/diagnostics.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/verify.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.
