Kubernetes
Nginx Ingress
GCP
Load Balancer
Cloud Computing

Create kubernetes nginx ingress without GCP load-balancer

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

On GKE, the default Ingress path usually provisions a Google Cloud load balancer. If you want NGINX ingress without that managed load balancer, you need to avoid the GCE ingress controller and expose your NGINX ingress controller in another way, typically with NodePort, host networking, or an external solution such as MetalLB in non-GKE environments. The key is that the Kubernetes Ingress object alone does not create reachability; the controller's own Service type does.

Do Not Use the GCE Ingress Controller

If you create an Ingress on GKE without care, the cluster may hand it to the Google controller and provision cloud resources. That is the behavior you are trying to avoid.

Instead, deploy the NGINX ingress controller and make sure your Ingress resources are targeted at it, usually through an ingress class.

yaml
1apiVersion: networking.k8s.io/v1
2kind: Ingress
3metadata:
4  name: app-ingress
5spec:
6  ingressClassName: nginx
7  rules:
8    - host: example.local
9      http:
10        paths:
11          - path: /
12            pathType: Prefix
13            backend:
14              service:
15                name: app-service
16                port:
17                  number: 80

This tells Kubernetes which controller should watch the resource.

Expose the NGINX Controller With NodePort

The most common way to avoid a cloud load balancer is to expose the ingress controller as a NodePort Service.

yaml
1apiVersion: v1
2kind: Service
3metadata:
4  name: ingress-nginx-controller
5  namespace: ingress-nginx
6spec:
7  type: NodePort
8  selector:
9    app.kubernetes.io/name: ingress-nginx
10  ports:
11    - name: http
12      port: 80
13      targetPort: 80
14      nodePort: 30080
15    - name: https
16      port: 443
17      targetPort: 443
18      nodePort: 30443

Traffic can then reach any cluster node on those ports. You can front that with your own VM, external reverse proxy, firewall rule, or DNS pointing directly at node IPs.

This avoids the managed GCP load balancer, but it also means you take responsibility for external traffic routing.

Controller Deployment Still Matters

You still need the NGINX ingress controller itself running in the cluster. A minimal Deployment looks like this.

yaml
1apiVersion: apps/v1
2kind: Deployment
3metadata:
4  name: ingress-nginx-controller
5  namespace: ingress-nginx
6spec:
7  replicas: 1
8  selector:
9    matchLabels:
10      app.kubernetes.io/name: ingress-nginx
11  template:
12    metadata:
13      labels:
14        app.kubernetes.io/name: ingress-nginx
15    spec:
16      containers:
17        - name: controller
18          image: registry.k8s.io/ingress-nginx/controller:v1.11.1
19          args:
20            - /nginx-ingress-controller
21            - --ingress-class=nginx
22          ports:
23            - containerPort: 80
24            - containerPort: 443

In practice you would normally install the controller with Helm or the official manifest rather than hand-writing every resource, but the structure is the same.

Understand the Tradeoff

Removing the cloud load balancer changes the operational model.

You gain:

  • lower dependence on GCP-managed ingress infrastructure
  • more control over traffic entry
  • the ability to run a similar pattern across environments

You lose:

  • managed external IP lifecycle
  • automatic L4 load balancing across nodes
  • some of the operational simplicity GKE normally provides

That tradeoff is often fine for dev clusters, on-prem-style environments, or setups with an existing external reverse proxy.

Host Networking and DaemonSet Options

Another pattern is running the ingress controller on every node with host ports or host networking. That can work, but it increases scheduling and port-management complexity. For most clusters, NodePort is the cleaner first option when the goal is simply to avoid a cloud load balancer.

DNS and External Reachability Are Your Responsibility

Once the controller is exposed through NodePort, DNS must resolve to something that can actually reach those node ports. That could be:

  • node public IPs
  • a separate reverse proxy
  • a hardware load balancer outside Kubernetes
  • a VPN-reachable internal address range

Without that extra layer, the ingress exists inside Kubernetes but remains unreachable from clients.

Common Pitfalls

  • Creating an Ingress on GKE and accidentally letting the GCE controller claim it.
  • Exposing the ingress controller as LoadBalancer and then wondering why GCP resources were created.
  • Forgetting that Ingress routes traffic only after the controller itself is reachable.
  • Skipping ingressClassName and relying on controller defaults.
  • Underestimating the operational work that the managed cloud load balancer had been doing for you.

Summary

  • To avoid a GCP load balancer, use the NGINX ingress controller instead of the GCE ingress controller.
  • Expose the NGINX controller with NodePort, host networking, or another non-cloud-LB mechanism.
  • Set ingressClassName: nginx so the right controller handles the Ingress resource.
  • The Ingress object defines routing, but the controller Service defines reachability.
  • When you remove the managed load balancer, you take over external traffic delivery yourself.

Course illustration
Course illustration

All Rights Reserved.