diff --git a/roles/deploy-env/defaults/main.yaml b/roles/deploy-env/defaults/main.yaml
index e34476c51..f1107b6fe 100644
--- a/roles/deploy-env/defaults/main.yaml
+++ b/roles/deploy-env/defaults/main.yaml
@@ -27,6 +27,12 @@ cilium_version: "1.16.0"
 flannel_setup: false
 flannel_version: v0.25.4
 
+ingress_setup: false
+ingress_nginx_version: "4.8.3"
+ingress_openstack_setup: true
+ingress_ceph_setup: true
+ingress_osh_infra_setup: false
+
 kubectl:
   user: zuul
   group: zuul
diff --git a/roles/deploy-env/tasks/ingress.yaml b/roles/deploy-env/tasks/ingress.yaml
new file mode 100644
index 000000000..33e378630
--- /dev/null
+++ b/roles/deploy-env/tasks/ingress.yaml
@@ -0,0 +1,83 @@
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+---
+- name: Add ingress-nginx helm repo
+  become_user: "{{ kubectl.user }}"
+  shell: |
+    helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
+
+- name: Deploy cluster ingress in kube-system namespace if not using metallb
+  become_user: "{{ kubectl.user }}"
+  when: not metallb_setup
+  shell: |
+    helm upgrade --install ingress-nginx-cluster ingress-nginx/ingress-nginx \
+      --version {{ ingress_nginx_version }} \
+      --namespace=kube-system \
+      --set controller.admissionWebhooks.enabled="false" \
+      --set controller.kind=DaemonSet \
+      --set controller.service.type=ClusterIP \
+      --set controller.scope.enabled="false" \
+      --set controller.hostNetwork="true" \
+      --set controller.ingressClassResource.name=nginx-cluster \
+      --set controller.ingressClassResource.controllerValue="k8s.io/ingress-nginx-cluster" \
+      --set controller.ingressClassResource.default="true" \
+      --set controller.ingressClass=nginx-cluster \
+      --set controller.labels.app=ingress-api
+
+- name: Deploy ingress in openstack namespace
+  become_user: "{{ kubectl.user }}"
+  when: ingress_openstack_setup
+  shell: |
+    helm upgrade --install --create-namespace ingress-nginx-openstack ingress-nginx/ingress-nginx \
+      --version {{ ingress_nginx_version }} \
+      --namespace=openstack \
+      --set controller.kind=DaemonSet \
+      --set controller.admissionWebhooks.enabled="false" \
+      --set controller.scope.enabled="true" \
+      --set controller.service.enabled="false" \
+      --set controller.ingressClassResource.name=nginx \
+      --set controller.ingressClassResource.controllerValue="k8s.io/ingress-nginx-openstack" \
+      --set controller.ingressClass=nginx \
+      --set controller.labels.app=ingress-api
+
+- name: Deploy ingress in ceph namespace
+  become_user: "{{ kubectl.user }}"
+  when: ingress_ceph_setup
+  shell: |
+    helm upgrade --install --create-namespace ingress-nginx-ceph ingress-nginx/ingress-nginx \
+      --version {{ ingress_nginx_version }} \
+      --namespace=ceph \
+      --set controller.kind=DaemonSet \
+      --set controller.admissionWebhooks.enabled="false" \
+      --set controller.scope.enabled="true" \
+      --set controller.service.enabled="false" \
+      --set controller.ingressClassResource.name=nginx-ceph \
+      --set controller.ingressClassResource.controllerValue="k8s.io/ingress-nginx-ceph" \
+      --set controller.ingressClass=nginx-ceph \
+      --set controller.labels.app=ingress-api
+
+- name: Deploy ingress in osh_infra namespace
+  become_user: "{{ kubectl.user }}"
+  when: ingress_osh_infra_setup
+  shell: |
+    helm upgrade --install --create-namespace ingress-nginx-osh-infra ingress-nginx/ingress-nginx \
+      --version {{ ingress_nginx_version }} \
+      --namespace=osh-infra \
+      --set controller.admissionWebhooks.enabled="false" \
+      --set controller.scope.enabled="true" \
+      --set controller.service.enabled="false" \
+      --set controller.ingressClassResource.name=nginx-osh-infra \
+      --set controller.ingressClassResource.controllerValue="k8s.io/ingress-nginx-osh-infra" \
+      --set controller.ingressClass=nginx-osh-infra \
+      --set controller.labels.app=ingress-api
+...
diff --git a/roles/deploy-env/tasks/main.yaml b/roles/deploy-env/tasks/main.yaml
index 4274c01c4..d1caef39a 100644
--- a/roles/deploy-env/tasks/main.yaml
+++ b/roles/deploy-env/tasks/main.yaml
@@ -96,4 +96,11 @@
   include_tasks:
     file: client_cluster_ssh.yaml
   when: client_cluster_ssh_setup
+
+- name: Include ingress tasks
+  include_tasks:
+    file: ingress.yaml
+  when:
+    - ingress_setup
+    - inventory_hostname in (groups['primary'] | default([]))
 ...
diff --git a/zuul.d/jobs.yaml b/zuul.d/jobs.yaml
index 4e5544b29..4729432b9 100644
--- a/zuul.d/jobs.yaml
+++ b/zuul.d/jobs.yaml
@@ -111,6 +111,7 @@
       flannel_version: v0.25.4
       metallb_setup: false
       metallb_version: "0.13.12"
+      ingress_setup: true
       helm_version: "v3.14.0"
       crictl_version: "v1.30.1"
       zuul_osh_infra_relative_path: ../openstack-helm-infra
@@ -129,7 +130,6 @@
       gate_scripts:
         - ./tools/deployment/common/prepare-k8s.sh
         - ./tools/deployment/common/prepare-charts.sh
-        - ./tools/deployment/common/ingress.sh
         - ./tools/deployment/ceph/ceph-rook.sh
         - ./tools/deployment/ceph/ceph-adapter-rook.sh
         - ./tools/deployment/common/ldap.sh
@@ -151,7 +151,6 @@
         - ./tools/deployment/common/prepare-k8s.sh
         - ./tools/deployment/common/prepare-charts.sh
         - ./tools/deployment/common/deploy-docker-registry.sh
-        - ./tools/deployment/common/ingress.sh
         - ./tools/deployment/common/nfs-provisioner.sh
         - ./tools/deployment/common/ldap.sh
         - ./tools/deployment/db/mariadb.sh
@@ -179,6 +178,7 @@
         container_distro_name: ubuntu
         container_distro_version: jammy
         feature_gates: apparmor
+      ingress_setup: false
       gate_scripts:
         - ./tools/deployment/common/prepare-k8s.sh
         - ./tools/deployment/common/prepare-charts.sh
@@ -202,7 +202,6 @@
         - ./tools/deployment/common/prepare-k8s.sh
         - ./tools/deployment/common/prepare-charts.sh
         - ./tools/deployment/common/namespace-config.sh
-        - ./tools/deployment/common/ingress.sh
         - ./tools/deployment/ceph/ceph.sh
         - ./tools/deployment/ceph/ceph-ns-activate.sh
         - ./tools/deployment/common/setup-client.sh
@@ -302,7 +301,6 @@
       gate_scripts:
         - ./tools/deployment/common/prepare-k8s.sh
         - ./tools/deployment/common/prepare-charts.sh
-        - ./tools/deployment/common/ingress.sh
         - ./tools/deployment/common/setup-client.sh
         - |
           export NAMESPACE=openstack
@@ -333,7 +331,6 @@
       gate_scripts:
         - ./tools/deployment/common/prepare-k8s.sh
         - ./tools/deployment/common/prepare-charts.sh
-        - ./tools/deployment/common/ingress.sh
         - ./tools/deployment/common/setup-client.sh
         - |
           export NAMESPACE=openstack
@@ -416,7 +413,6 @@
       gate_scripts:
         - ./tools/deployment/common/prepare-k8s.sh
         - ./tools/deployment/common/prepare-charts.sh
-        - ./tools/deployment/common/ingress.sh
         # Deploy Ceph cluster using legacy OSH charts
         - ./tools/deployment/ceph/ceph_legacy.sh
         # Deploy stateful applications