> ## Documentation Index
> Fetch the complete documentation index at: https://tyk.io/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Deploy Tyk on OpenShift

> Deploy the Tyk Helm charts on Red Hat OpenShift, including ROSA, under the restricted-v2 security context constraint. Covers the securityContext opt-out, Redis and PostgreSQL, and upgrade risks.

| Edition | Deployment Type |
| :- | :- |
| Enterprise | Self-Managed, Hybrid |

Red Hat OpenShift admits pods through [Security Context Constraints](https://docs.redhat.com/en/documentation/openshift_container_platform/4.21/html/authentication_and_authorization/managing-pod-security-policies) (SCCs), which restrict the user and group IDs a pod may request. The Tyk Helm charts ship hardened defaults that pin specific IDs, and those IDs fall outside the range OpenShift allocates to a project. This page explains how to opt out of the pinned IDs so the SCC can assign its own.

## Compatible Versions

| Requirement | Version |
| :- | :- |
| Tyk Helm Chart | v5.4.0 or later |
| Tyk components | Chart v5.4.0 defaults: Tyk Gateway and Tyk Dashboard v5.15.0, Tyk Pump v1.17.0, Tyk MDCB v2.13.0, Tyk Developer Portal v1.19.0. This page was validated with Tyk Gateway and Tyk Dashboard v5.13.1 |
| OpenShift | 4.22 (validated on 4.22.1). The approach applies to any cluster that provides the `restricted-v2` SCC |

## How Security Context Constraints Affect Tyk

When you create a project, OpenShift allocates it a block of UIDs and GIDs, typically starting above 1000000000. The `restricted-v2` SCC applies one rule to both `runAsUser` and `fsGroup`:

* If the pod leaves the field **unset**, the SCC assigns a value from the project's allocated range.
* If the pod sets the field **explicitly**, the SCC **validates** it against that range and rejects the pod if it falls outside.

The Tyk charts set both fields explicitly, so a default installation is rejected. Installing `tyk-oss` with default values on OpenShift as an unprivileged user produces two independent violations:

```console theme={null}
pods "gateway-tyk-oss-tyk-gateway" is forbidden: unable to validate against any security context constraint:
  spec.securityContext.fsGroup: Invalid value: []int64{2000}: 2000 is not an allowed group
  spec.initContainers[0].runAsUser: Invalid value: 65532: must be in the ranges: [1000670000, 1000679999]
```

Only an explicit `runAsUser` and an explicit `fsGroup` cause this. The other hardening the charts apply is compliant with `restricted-v2` and should be left in place.

<Note>
  SCCs are evaluated when a **pod** is admitted, not when a Deployment or StatefulSet is created. Rendering manifests with `helm template` does not prove that a workload will be admitted. Always verify against real pods.
</Note>

## Prerequisites

* An OpenShift 4.21 cluster, including Red Hat OpenShift Service on AWS (ROSA).
* The [`oc` CLI](https://docs.redhat.com/en/documentation/openshift_container_platform/4.21/html/cli_tools/openshift-cli-oc) and [Helm 3+](https://helm.sh/docs/intro/install/).
* An [Enterprise Edition License](/docs/apim#licensing) for `tyk-stack`, `tyk-control-plane` and `tyk-data-plane`.
* A deploying account with the **`admin`** role on the target project. The `edit` role is not sufficient, because the charts create Roles and RoleBindings for the bootstrap job.

```bash theme={null}
oc adm policy add-role-to-user admin <deployer> -n tyk
```

To see the UID and GID range allocated to your project:

```bash theme={null}
oc get namespace tyk -o jsonpath='{.metadata.annotations}' | tr ',' '\n' | grep -i openshift.io/sa.scc
```

<Note>
  If you deploy through Argo CD or Flux CD, also configure the bootstrap jobs for GitOps. See [Deploy Tyk Helm Charts with GitOps](/docs/product-stack/tyk-charts/gitops-deployment).
</Note>

## Disabling The Chart Security Contexts

Every `securityContext` block in the charts accepts an `enabled` flag. Setting `enabled: false` omits the **entire** block from the rendered manifest, which leaves the fields unset and lets the SCC assign them. The `enabled` key itself is never rendered.

```yaml highlight={4} theme={null}
tyk-gateway:
  gateway:
    securityContext:
      enabled: false
```

### The Tyk Gateway Init Container Falls Through

The Tyk Gateway runs an init container, `setupDirectories`, that prepares its working directory. The chart resolves its `runAsUser` from the first value it finds: the init container's own `securityContext`, then `gateway.containerSecurityContext`, then `gateway.securityContext`. If none sets a UID, the init container runs as `65532`, which matches the Tyk Gateway image.

Setting `enabled: false` on the init container's own block does not remove the security context outright. The template falls back to `gateway.containerSecurityContext`. To omit the init container's security context entirely, set `enabled: false` on **both** blocks. The values profiles below do this for you.

<Warning>
  Use these opt-out flags only on clusters that assign IDs, such as OpenShift. On vanilla Kubernetes nothing assigns a UID, and kubelet rejects the `busybox` init container with `CreateContainerConfigError: container has runAsNonRoot and image will run as root`.
</Warning>

## Values Profiles By Chart

Each tab below is a complete, ready-to-paste OpenShift profile. Merge it into your `values.yaml` alongside your normal configuration, then install as usual.

<Tabs>
  <Tab title="Tyk Stack">
    <br />

    ```yaml expandable theme={null}
    tyk-gateway:
      gateway:
        securityContext:
          enabled: false
        containerSecurityContext:
          enabled: false
        initContainers:
          setupDirectories:
            securityContext:
              enabled: false

    tyk-dashboard:
      dashboard:
        securityContext:
          enabled: false
        containerSecurityContext:
          enabled: false

    tyk-pump:
      pump:
        securityContext:
          enabled: false
        containerSecurityContext:
          enabled: false

    tyk-bootstrap:
      bootstrap:
        securityContext:
          enabled: false
        containerSecurityContext:
          enabled: false

    # Only required when global.components.devPortal is true
    tyk-dev-portal:
      securityContext:
        enabled: false
      containerSecurityContext:
        enabled: false

    # Only required when global.components.operator is true
    tyk-operator:
      managerPodSecurityContext:
        enabled: false

    # Required for `helm test` to pass
    tests:
      securityContext:
        enabled: false
      containerSecurityContext:
        enabled: false
    ```

    Tyk Pump is enabled by default in this chart. Tyk Developer Portal and Tyk Operator are not.
  </Tab>

  <Tab title="Tyk Control Plane">
    <br />

    ```yaml expandable theme={null}
    tyk-gateway:
      gateway:
        securityContext:
          enabled: false
        containerSecurityContext:
          enabled: false
        initContainers:
          setupDirectories:
            securityContext:
              enabled: false

    tyk-dashboard:
      dashboard:
        securityContext:
          enabled: false
        containerSecurityContext:
          enabled: false

    tyk-bootstrap:
      bootstrap:
        securityContext:
          enabled: false
        containerSecurityContext:
          enabled: false

    tyk-mdcb:
      mdcb:
        podSecurityContext:
          enabled: false
        containerSecurityContext:
          enabled: false

    # Only required when global.components.pump is true
    tyk-pump:
      pump:
        securityContext:
          enabled: false
        containerSecurityContext:
          enabled: false

    # Only required when global.components.devPortal is true
    tyk-dev-portal:
      securityContext:
        enabled: false
      containerSecurityContext:
        enabled: false

    # Only required when global.components.operator is true
    tyk-operator:
      managerPodSecurityContext:
        enabled: false
    ```

    Note that Tyk MDCB uses `podSecurityContext`, not `securityContext`, for its pod-level block. Tyk Pump is disabled by default in this chart. This chart ships no Helm test pod, so it has no `tests` section.
  </Tab>

  <Tab title="Tyk Data Plane">
    <br />

    ```yaml expandable theme={null}
    tyk-gateway:
      gateway:
        securityContext:
          enabled: false
        containerSecurityContext:
          enabled: false
        initContainers:
          setupDirectories:
            securityContext:
              enabled: false

    tyk-pump:
      pump:
        securityContext:
          enabled: false
        containerSecurityContext:
          enabled: false

    # Required for `helm test` to pass
    tests:
      securityContext:
        enabled: false
      containerSecurityContext:
        enabled: false
    ```

    Tyk Pump is enabled by default in this chart.
  </Tab>

  <Tab title="Tyk Open Source">
    <br />

    ```yaml expandable theme={null}
    tyk-gateway:
      gateway:
        securityContext:
          enabled: false
        containerSecurityContext:
          enabled: false
        initContainers:
          setupDirectories:
            securityContext:
              enabled: false

    # Only required when global.components.pump is true
    tyk-pump:
      pump:
        securityContext:
          enabled: false
        containerSecurityContext:
          enabled: false

    # Only required when global.components.operator is true
    tyk-operator:
      managerPodSecurityContext:
        enabled: false

    # Required for `helm test` to pass
    tests:
      securityContext:
        enabled: false
      containerSecurityContext:
        enabled: false
    ```

    Tyk Pump and Tyk Operator are both disabled by default in this chart.
  </Tab>
</Tabs>

## Redis And PostgreSQL

Redis and PostgreSQL are **not** dependencies of the Tyk charts. You install them as separate releases, and they have their own security contexts that OpenShift rejects independently of anything you configure in Tyk's values.

The Bitnami charts, which Tyk's installation guides use, pin uid and gid `1001` on primary **and** replica pods. Disabling the Tyk security contexts does nothing for them. Apply the opt-out to those releases as well:

```bash theme={null}
helm install tyk-redis bitnami/redis -n tyk --version 19.0.2 \
  --set master.podSecurityContext.enabled=false \
  --set master.containerSecurityContext.enabled=false \
  --set replica.podSecurityContext.enabled=false \
  --set replica.containerSecurityContext.enabled=false
```

Consult the chart documentation for whichever Redis and PostgreSQL distributions you use, and confirm the exact key names, since they differ between chart vendors and versions.

### Redis Must Be Able To Write Its Data Directory

Admission is only half the problem. A Redis pod that starts successfully can still be unusable if it cannot write to `/data`.

Container images bake in a data directory owned by the image's own user, for example `redis:redis` with mode `0755`. On OpenShift the container runs as an arbitrary assigned UID that does not own that directory, so background saves fail. Because Redis defaults to `stop-writes-on-bgsave-error yes`, it then refuses **every** write command, including `PING`:

```console theme={null}
MISCONF Redis is configured to save RDB snapshots, but it's currently unable to
persist to disk. Commands that may modify the data set are disabled...
```

The failure is also delayed. Redis only attempts its first background save once its save interval elapses, so the deployment can look healthy for an hour before writes start failing.

Mount a volume at the data directory so the SCC-assigned `fsGroup` applies to it. Any `emptyDir` or `PersistentVolumeClaim` works, because OpenShift sets group ownership and the setgid bit on mounted volumes:

```yaml highlight={8} theme={null}
volumes:
  - name: data
    emptyDir: {}
containers:
  - name: redis
    volumeMounts:
      - name: data
        mountPath: /data
```

## Verifying Admission

Install the chart, then confirm the pods were admitted and see which SCC accepted them:

```bash theme={null}
oc get pods -n tyk
oc get pods -n tyk -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.metadata.annotations.openshift\.io/scc}{"\n"}{end}'
```

Every pod should report `restricted-v2`. Confirm the SCC assigned IDs from the project range rather than the chart defaults:

```bash theme={null}
oc get pod <gateway-pod> -n tyk -o jsonpath='{.spec.securityContext}{"\n"}{.spec.containers[0].securityContext}'
```

You should see a `runAsUser` and `fsGroup` in the 1000000000 range, and no `fsGroup: 2000` or `runAsUser: 65532`. Finally, check the Tyk Gateway is healthy. Forward the service port and call the endpoint from your own machine:

```bash theme={null}
oc port-forward -n tyk svc/gateway-svc-tyk-<release>-tyk-gateway 8080:8080 &
curl -s http://127.0.0.1:8080/hello
```

A healthy Tyk Gateway returns `"status":"pass"` with a `"pass"` for each configured dependency:

```json theme={null}
{"status":"pass","version":"5.13.1","description":"Tyk GW","details":{"redis":{"status":"pass","componentType":"datastore","time":"..."}}}
```

If a pod is not created at all, the SCC rejected it. The error is on the ReplicaSet or Job, not the pod, because no pod object exists:

```bash theme={null}
oc get events -n tyk --field-selector reason=FailedCreate
oc describe replicaset -n tyk | grep -A5 'unable to validate'
```

## Running Helm Tests

The Helm test pods in `tyk-stack`, `tyk-data-plane` and `tyk-oss` pin `runAsUser: 1000` on both their pod-level and container-level blocks, because the default test image declares no `USER`. Without the opt-out, `helm test` is rejected on OpenShift even when every application pod is running. The `tests` section in the profiles above covers this. `tyk-control-plane` ships no test pod.

<Warning>
  One of the test pods is not cleaned up after a run, because it carries no `helm.sh/hook-delete-policy`. A second `helm test` against the same release then fails, because the pod name already exists. Delete the leftover pod before re-running:

  ```bash theme={null}
  oc delete pod -n tyk test-tyk-<release>-map --ignore-not-found
  helm test tyk-<release> -n tyk
  ```
</Warning>

## Upgrade Notes

From chart v5.4.0 the container-level `runAsUser` is removed from the component defaults while `runAsNonRoot: true` is retained. When no `runAsUser` is set, kubelet falls back to the `USER` directive baked into the image and verifies it is non-root.

This is safe on the default image tags, which all declare a numeric non-root `USER`. It is **not** safe if you pin older tags. On upgrade, every pod using an affected image fails admission:

```console theme={null}
CreateContainerConfigError: container has runAsNonRoot and image will run as root
```

Three cases break:

* The image declares no `USER` at all.
* The image declares `USER 0`. Tyk Gateway `v5.9.1` is an example.
* The image declares a **symbolic** `USER`, such as distroless `nonroot`. Kubelet cannot verify a non-numeric user against `runAsNonRoot` and reports `non-numeric user (nonroot), cannot verify user is non-root`.

Before upgrading, check any tag you have pinned:

```bash theme={null}
crane config <image>:<tag> | jq .config.User
```

Anything other than a numeric, non-zero value needs either a tag bump or an explicit `runAsUser` in your own values, which continues to be honored.

<Warning>
  **Tyk Dashboard tags earlier than v5.5.0 report `USER=''` and will fail this check.** If you pin such a tag, set an explicit `runAsUser` for Tyk Dashboard or move to v5.5.0 or later.
</Warning>

### Upgrading With Helm Hooks Disabled

With `bootstrap.disableHelmHooks: true`, a plain `helm upgrade` fails on the immutable bootstrap Job. See [Upgrade And Re-Run Safety](/docs/product-stack/tyk-charts/gitops-deployment#upgrade-and-re-run-safety).

### Changed Keys

* `pump.extraContainers` was previously nested inside the Tyk Pump `securityContext` condition, so sidecars silently disappeared when that block was falsy. The two are now independent.
