Skip to content

We now have a scalable, self-healing application running via a Deployment. But as we noted, it’s isolated inside the cluster’s private network. Let’s fix that.

Exposing Your Application: Services and Ingress

Section titled “Exposing Your Application: Services and Ingress”

Your Pods are like houses in a gated community. They can talk to each other, but no one from the outside can get in. Pods are also constantly being created and destroyed by the Deployment, each time getting a new internal IP address. So, how can we reliably connect to them?

We need a stable address and a way to route traffic from the outside world to our application Pods. This is handled by two types of objects: Services and Ingress.

A Service is a Kubernetes object that provides a stable network endpoint for a group of Pods. It acts as an internal load balancer. A Service gets a permanent IP address and a DNS name within the cluster, and it forwards any traffic it receives to one of its healthy backing Pods.

How does a Service know which Pods to send traffic to? It uses the exact same label and selector mechanism that a Deployment uses to identify its Pods.

There are three key types of Services you should know:

  • ClusterIP: (Default type) The Service gets a single, stable IP address that is only reachable from within the cluster. This is perfect for internal communication, for example, if your web application frontend needs to talk to a backend API or a database that is also running in the cluster.

  • NodePort: This type of Service exposes the application on a specific port on every Node in the cluster. You can then access your application by hitting http://<Any-Node-IP>:<NodePort>. This is great for quick testing and development but isn’t ideal for production because you have to keep track of Node IPs and specific port numbers.

  • LoadBalancer: This is the standard way to expose an application to the internet. When you create a Service of type LoadBalancer, Kubernetes works with your cloud provider (like GCP, AWS, or Azure) to provision a real, external load balancer. This load balancer gets a public IP address and automatically forwards all traffic to your Service, which in turn forwards it to your Pods.



  1. Exposing with a NodePort Service” This is the simplest way to get external traffic to your Pods. Create a file named service-nodeport.yaml:

    service-nodeport.yaml
    apiVersion: v1
    kind: Service
    metadata:
    name: my-webapp-service
    spec:
    # This selector must match the labels of the Pods in our Deployment
    selector:
    app: my-webapp
    # This defines the service type
    type: NodePort
    ports:
    - protocol: TCP
    # Port on the Service itself
    port: 80
    # The port on the Pods that the traffic should be sent to
    targetPort: 80
    # The static port on the Node. If not specified, Kubernetes picks one.
    # nodePort: 30007

    Apply it to your cluster:

    Terminal window
    kubectl apply -f service-nodeport.yaml

    Now, Minikube gives us a handy shortcut to get the accessible URL:

    Terminal window
    minikube service my-webapp-service --url

    This will output a URL like http://192.168.49.2:31234. Open that URL in your browser or use curl, and you’ll see the “Welcome to nginx!” page, served from one of your Pods.

  2. Exposing with a LoadBalancer Service This simulates how you would expose an app in a real cloud environment.

    First, let’s delete the NodePort service: kubectl delete service my-webapp-service.

    Now, create a file named service-loadbalancer.yaml:

    service-loadbalancer.yaml
    apiVersion: v1
    kind: Service
    metadata:
    name: my-webapp-service
    spec:
    selector:
    app: my-webapp
    # This is the only line that changes
    type: LoadBalancer
    ports:
    - protocol: TCP
    port: 80
    targetPort: 80

    Apply this new service:

    Terminal window
    kubectl apply -f service-loadbalancer.yaml

    Because Minikube doesn’t have a real cloud load balancer, it has a built-in tool that simulates one. Open a new, separate terminal and run:

    Terminal window
    minikube tunnel

    Leave this command running. It will ask for your password to set up network routes. The tunnel will take over your machine’s network to route traffic to the LoadBalancer IP.

    Now, in your original terminal, check the status of your service. After a few moments, you will see an EXTERNAL-IP assigned.

    Terminal window
    # Watch for the EXTERNAL-IP to appear
    kubectl get service my-webapp-service --watch

    Once you see an IP address in the EXTERNAL-IP column, you can curl that IP address directly, and it will hit your Nginx service.

A LoadBalancer service is great, but it’s a one-to-one mapping. You get one public IP for one service. What if you have 10 services (e.g., yoursite.com/api, yoursite.com/blog, admin.yoursite.com)? Provisioning 10 load balancers would be slow and expensive.

An Ingress is a smarter solution. It’s a layer 7 (HTTP/S) router that can direct traffic to different services based on the requested hostname or URL path. It acts as a single entry point for your entire cluster.

To use an Ingress, you need two things:

  1. An Ingress object: A YAML file defining the routing rules.
  2. An Ingress Controller: A Pod running in your cluster that actually implements the rules (e.g., it’s a sophisticated Nginx or proxy server).


  1. Enable the Ingress Controller Add-on Minikube comes with an Nginx Ingress Controller that you can easily enable:

    Terminal window
    minikube addons enable ingress

    Wait for it to complete. It will install the necessary controller into your cluster.

  2. Create the Ingress Rule Create a file named ingress.yaml. We will tell it to route all traffic for my-webapp.example.com to our existing service.

    ingress.yaml
    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
    name: my-webapp-ingress
    spec:
    rules:
    - host: "my-webapp.example.com"
    http:
    paths:
    - pathType: Prefix
    path: "/"
    backend:
    service:
    name: my-webapp-service
    port:
    number: 80

    Apply the rule:

    Terminal window
    kubectl apply -f ingress.yaml
  3. Map the Hostname to Minikube’s IP The world doesn’t know where my-webapp.example.com is. We need to tell our local machine to direct requests for that domain to our Minikube cluster.

    First, get the IP address of your Minikube cluster:

    Terminal window
    minikube ip

    Let’s say the IP is 192.168.49.2.

    Now, you must edit the hosts file on your computer to add this entry.

    • On macOS/Linux: sudo nano /etc/hosts
    • On Windows: Open Notepad as an Administrator and open C:\Windows\System32\drivers\etc\hosts

    Add the following line to the end of the file, then save it:

    192.168.49.2 my-webapp.example.com

    (Remember to use the actual IP from the minikube ip command)

  4. Test It! You can now access your application using the domain name. The Ingress Controller will see the request for my-webapp.example.com, look up your Ingress rule, and forward the traffic to my-webapp-service, which sends it to one of your Pods.

    Terminal window
    curl http://my-webapp.example.com

    You should see the “Welcome to nginx!” page. Success!

  • Services give you a stable endpoint to access your ephemeral Pods.
  • NodePort is for quick access during development.
  • LoadBalancer is the standard way to expose a single service to the internet.
  • Ingress is a powerful router that manages access to multiple services using hostnames and paths, saving you from needing multiple LoadBalancers.

When you are ready, we will proceed to Chapter 4, where we’ll learn how to manage application configuration and secrets without having to rebuild your container images.