Install API7 Ingress Controller on OpenShift
Compared to standard Kubernetes environments, OpenShift enforces stricter security controls and platform-level constraints. As a result, several additional considerations must be addressed during deployment.
This guide explains how to deploy API7 Ingress Controller on an OpenShift cluster. It focuses on the installation for API7 Ingress Controller, which is used with API7 Enterprise.
If Red Hat OpenShift GitOps manages the installation, follow Manage with Argo CD for reconciliation and apply the OpenShift-specific SCC, non-root, and CRD requirements described here.
The installation steps for APISIX Ingress Controller on OpenShift are not covered in this document. However, they are largely similar, as both controllers share the same OpenShift-specific requirements:
- Security Context Constraints (SCC): OpenShift restricts pod security by default. The Ingress Controller must be configured to use an appropriate SCC (typically
nonroot-v2) to run successfully. - Non-root Execution Requirements: OpenShift requires containers to run as non-root users with assigned UIDs. Accordingly, the
securityContextsettings must be explicitly configured in the Helm installation commands. - Custom Resource Definition (CRD) Management: Gateway API ownership depends on the OpenShift version. OpenShift 4.18 and earlier do not install the CRDs by default; starting with 4.19, the Ingress Operator manages them. Use
--skip-crdsin this workflow and have the cluster platform team manage the required Gateway API and APISIX CRDs separately.
Prerequisites
Complete the prerequisites and installation steps in Install API7 Enterprise on OpenShift, except for the Generate and Run Deployment Script step for the gateway. This is because when working with an Ingress Controller, the gateway instance must be deployed within a gateway group of the Ingress Controller type.
Install API7 Ingress Controller
If you would like to use a different namespace or customize other configuration names, adjust the installation commands and configuration values accordingly.
Generate and Run Deployment Script
Navigate to the Dashboard:
- Select Gateway Groups from the side navigation bar and then click Add Gateway Group.
- Select Ingress Controller as Type.
- Enter
api7-ingressin the Name field. - Click Add.

The gateway group should be created, and you will be prompted to install the ingress controller and deploy the GatewayProxy configuration in the Deployment Steps panel.
Enter the namespace and the name of the Ingress Controller.

Then click Generate Script.

Manually append the highlighted flags at the end of the command. Ensure the previous line ends with a trailing backslash (\). The command should be similar to the following:
helm repo add api7 https://charts.api7.ai
helm repo update
helm upgrade --install -n api7-enterprise-project --create-namespace api7-ingress api7/api7-ingress-controller \
--version 0.1.24 \
--set "deployment.image.repository=api7/api7-ingress-controller" \
--set "deployment.image.tag=2.1.0" \
--set "config.controllerName=api7.ai/api7-ingress-controller" \
--set "config.leaderElection.id=api7-ingress-controller-leader" \
--set "adc.securityContext.runAsUser=65532" \
--skip-crds
❶ --set "adc.securityContext.runAsUser=65532": Run the API7 Ingress Controller as a non-root user (UID 65532) to comply with OpenShift's security policies.
❷ --skip-crds: Skips installing CRDs through Helm so that the cluster platform team can manage Gateway API ownership for the installed OpenShift version. APISIX CRDs must be installed separately afterward.
If you plan to use Gateway API to create resources, use Helm chart version 0.1.23 or later. Earlier chart versions do not correctly install Gateway API resources in an OpenShift environment.
Run it in the terminal. If deployed successfully, you should see a response similar to the following:
NAME: api7-ingress
LAST DEPLOYED: Mon Feb 2 15:12:59 2026
NAMESPACE: api7-enterprise-project
STATUS: deployed
REVISION: 1
TEST SUITE: None
Configure SCC for API7 Ingress Controller
Create a service account named api7-ingress, which will be referenced in the Helm chart release name. The role must be assigned to the service account used by the Ingress Controller deployment.
oc create serviceaccount api7-ingress -n api7-enterprise-project
Create a role with nonroot-v2 SCC:
oc create role api7-ingress \
--verb=use \
--resource=scc \
--resource-name=nonroot-v2 \
-n api7-enterprise-project
Bind the role to the service account:
oc create rolebinding api7-ingress \
--role=api7-ingress \
--serviceaccount=api7-enterprise-project:api7-ingress \
-n api7-enterprise-project
Install the Required CRDs
The --skip-crds flag skips both the APISIX and Gateway API CRDs bundled with the chart. Install the required CRDs according to the OpenShift version.
OpenShift 4.18 and Earlier
Gateway API CRDs are not installed by default. If you plan to use Gateway API resources, install the Gateway API CRDs from the same pinned chart version:
kubectl apply -f https://raw.githubusercontent.com/api7/api7-helm-chart/refs/tags/api7-ingress-controller-0.1.24/charts/ingress-controller/crds/gwapi-crds.yaml
Install the APISIX CRDs:
kubectl apply -f https://raw.githubusercontent.com/api7/api7-helm-chart/refs/tags/api7-ingress-controller-0.1.24/charts/ingress-controller/crds/apisix-crds.yaml
OpenShift 4.19 and Later
The OpenShift Ingress Operator installs and manages the Gateway API CRDs. Do not apply the chart's gwapi-crds.yaml file. Install only the APISIX CRDs from the same pinned chart version:
kubectl apply -f https://raw.githubusercontent.com/api7/api7-helm-chart/refs/tags/api7-ingress-controller-0.1.24/charts/ingress-controller/crds/apisix-crds.yaml
OpenShift 4.19 uses its supported Gateway API version rather than the chart's bundled experimental version. Verify that the Gateway API resources you plan to use are available on the cluster.
The APISIX CRD installation should produce output similar to the following:
customresourcedefinition.apiextensions.k8s.io/apisixconsumers.apisix.apache.org created
customresourcedefinition.apiextensions.k8s.io/apisixglobalrules.apisix.apache.org created
customresourcedefinition.apiextensions.k8s.io/apisixpluginconfigs.apisix.apache.org created
customresourcedefinition.apiextensions.k8s.io/apisixroutes.apisix.apache.org created
customresourcedefinition.apiextensions.k8s.io/apisixtlses.apisix.apache.org created
customresourcedefinition.apiextensions.k8s.io/apisixupstreams.apisix.apache.org created
customresourcedefinition.apiextensions.k8s.io/backendtrafficpolicies.apisix.apache.org created
customresourcedefinition.apiextensions.k8s.io/consumers.apisix.apache.org created
customresourcedefinition.apiextensions.k8s.io/gatewayproxies.apisix.apache.org created
customresourcedefinition.apiextensions.k8s.io/httproutepolicies.apisix.apache.org created
customresourcedefinition.apiextensions.k8s.io/pluginconfigs.apisix.apache.org created
Deploy the GatewayProxy Configuration
- Enter
api7-enterprise-projectin the Namespace field. - Enter
api7-ingressin the Name field. - Click Generate Script.

If you have not configured the Admin API Address, you will be prompted to add it first.

- Select Ingress or Gateway API tab.

- Copy the generated script and run it in the terminal.
You should get the following responses.
namespace/api7-enterprise-project configured
secret/api7-ingress-admin-secret configured
gatewayclass.gateway.networking.k8s.io/api7-ingress created
gatewayproxy.apisix.apache.org/api7-ingress created
ingressclass.networking.k8s.io/api7-ingress created
Create a Gateway Instance
Go to the Gateway Instances page and add a gateway instance.

Switch to the Kubernetes tab and fill out the parameters. Once finished, click Generate to see the deployment script.

The generated script closes without a backslash (\). Ensure the apisix.image.tag line ends with \ and that the additional securityContext settings are appended to the end of the Helm command as such:
helm repo add api7 https://charts.api7.ai
helm repo update
cat > /tmp/tls.crt <<EOF
-----BEGIN CERTIFICATE-----
MIIBhjCCATigAwIBAgICBAAwBQYDK2VwMEQxCzAJBgNVBAYTAlVTMRMwEQYDVQQI
EwpDYWxpZm9ybmlhMQ0wCwYDVQQKEwRBUEk3MREwDwYDVQQDEwhBUEk3IEluYzAe
Fw0yNTEyMzEwNzQ3MzZaFw0zNjAxMjgwNzQ3MzZaMC4xDTALBgNVBAoTBEFQSTcx
HTAbBgNVBAMTFEdBVEVXQVktQ0EtVkVSU0lPTi0xMCowBQYDK2VwAyEA0WOxi3H/
gxsh/kNEg8L9cLrfZmRqE5lB4fW0DVObre2jZDBiMA4GA1UdDwEB/wQEAwIHgDAT
BgNVHSUEDDAKBggrBgEFBQcDAjAtBgNVHQ4EJgQkYWZjNmQ0ODctMmVmZC00MjZh
LWJiNDktMTA2ZWQ2NDIyOWFkMAwGA1UdIwQFMAOAATEwBQYDK2VwA0EAJJh+f8Sv
4OWo41Um3NOpvB30tjWQEsUXpDKyh9Kh8b7ymcDVQ0Hn1J9jFyI5Y/yFT8ZSz7ek
1GGpngVMsWTiDg==
-----END CERTIFICATE-----
EOF
cat > /tmp/tls.key <<EOF
-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VwBCIEIFXsXU+dBdP8lkheZH/6SMy7ZnMionHN/xRDHBjOMAvP
-----END PRIVATE KEY-----
EOF
cat > /tmp/ca.crt <<EOF
-----BEGIN CERTIFICATE-----
MIIBeDCCASqgAwIBAgIRAP4AwnUs3br0nZ7V03gyt84wBQYDK2VwMEQxCzAJBgNV
BAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMQ0wCwYDVQQKEwRBUEk3MREwDwYD
VQQDEwhBUEk3IEluYzAgFw0yNTEyMzEwNjU1NDlaGA8yMDg2MDExNTA2NTU0OVow
RDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExDTALBgNVBAoTBEFQ
STcxETAPBgNVBAMTCEFQSTcgSW5jMCowBQYDK2VwAyEAHTkeAU7NKliDJFd5UN9b
xYIMgAz4RG3QUwEBXl2NwdCjLzAtMA4GA1UdDwEB/wQEAwIChDAPBgNVHRMBAf8E
BTADAQH/MAoGA1UdDgQDBAExMAUGAytlcANBABrSW1mdva+TFJNiOpyblbz5vSJY
Jk9HSb3vHYfa3dncCz2A5m5sOs1VNFq3QQqPVaI41dx0FvIrp89gVsFVMAk=
-----END CERTIFICATE-----
EOF
kubectl create namespace api7-enterprise-project --dry-run=client -o yaml | kubectl apply -f -
kubectl create secret generic -n api7-enterprise-project api7-ee-3-gateway-tls --from-file=tls.crt=/tmp/tls.crt --from-file=tls.key=/tmp/tls.key --from-file=ca.crt=/tmp/ca.crt
helm upgrade --install -n api7-enterprise-project --create-namespace api7-ee-3-gateway api7/gateway \
--set "etcd.auth.tls.enabled=true" \
--set "etcd.auth.tls.existingSecret=api7-ee-3-gateway-tls" \
--set "etcd.auth.tls.certFilename=tls.crt" \
--set "etcd.auth.tls.certKeyFilename=tls.key" \
--set "etcd.auth.tls.verify=true" \
--set "gateway.tls.existingCASecret=api7-ee-3-gateway-tls" \
--set "gateway.tls.certCAFilename=ca.crt" \
--set "apisix.extraEnvVars[0].name=API7_GATEWAY_GROUP_SHORT_ID" \
--set "apisix.extraEnvVars[0].value=eybqbncmefrqq" \
--set "etcd.host[0]=https://api7ee3-dp-manager.api7-enterprise-project.svc.cluster.local:7943" \
--set "apisix.replicaCount=1" \
--set "apisix.image.repository=api7/api7-ee-3-gateway" \
--set "apisix.image.tag=3.9.3" \
--set "apisix.securityContext.runAsNonRoot=true" \
--set "apisix.securityContext.runAsUser=636"
❶ --set "apisix.securityContext.runAsNonRoot=true": Ensures the APISIX pod runs as a non-root user for OpenShift security compliance.
❷ --set "apisix.securityContext.runAsUser=636": Specifies the UID (636) for the APISIX pod process, matching OpenShift SCC requirements.
Navigating back to the gateway instance, you should see a healthy gateway instance.

Verify
Check all pod statuses:
kubectl get pods -n api7-enterprise-project
Ensure the Ingress Controller and Gateway pods are in Running status:
NAME READY STATUS RESTARTS AGE
api7-ee-3-gateway-78f8fd49f9-6fkhv 1/1 Running 0 4m44s
api7-ingress-api7-ingress-controller-568867b656-v8ftd 2/2 Running 0 65m
api7-postgresql-0 1/1 Running 0 91m
api7-prometheus-server-5c9b5c98ff-mf2gl 1/1 Running 0 91m
api7ee3-dashboard-df48f5f59-x5zkt 1/1 Running 0 91m
api7ee3-developer-portal-7fbd8fdc54-jvnvc 1/1 Running 0 91m
api7ee3-dp-manager-7b449767dc-dxfrr 1/1 Running 0 91m
List all Services:
kubectl get svc -n api7-enterprise-project
You should see a gateway service available:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
api7-ee-3-gateway-gateway NodePort 172.30.128.146 <none> 80:31474/TCP,443:30130/TCP 32m
api7-ingress-api7-ingress-controller-webhook-svc ClusterIP 172.30.63.42 <none> 443/TCP 67m
api7-ingress-metrics-service ClusterIP 172.30.133.232 <none> 8443/TCP 67m
api7-postgresql ClusterIP 172.30.123.46 <none> 5432/TCP 92m
api7-postgresql-hl ClusterIP None <none> 5432/TCP 92m
api7-prometheus-server ClusterIP 172.30.10.79 <none> 9090/TCP 92m
api7ee3-dashboard ClusterIP 172.30.84.136 <none> 7080/TCP,7443/TCP 92m
api7ee3-developer-portal ClusterIP 172.30.42.148 <none> 4321/TCP 92m
api7ee3-dp-manager ClusterIP 172.30.153.144 <none> 7900/TCP,7943/TCP 92m