How to Deploy and Expose Applications on Kubernetes Using Deployments, Services, and Ingress
Learning how to deploy and expose applications on Kubernetes using Deployments, Services, and Ingress is one of the most valuable skills you can add to your DevOps toolkit. Kubernetes has become the standard platform for running containerized workloads at scale. Whether you’re managing a small web app or a complex microservices architecture, understanding these three core building blocks changes everything. This tutorial walks you through creating a Deployment to run your application, a Service to expose it internally, and an Ingress resource to route external HTTP traffic to it. By the end, you’ll have a working application accessible from outside your cluster.
Prerequisites and Requirements for Deploying Applications on Kubernetes
Before you start, make sure you have the following in place.
Required tools and access:
- A running Kubernetes cluster (local with Minikube or a managed cluster like GKE, EKS, or AKS)
kubectlinstalled and configured to talk to your cluster- An Ingress controller installed , this tutorial uses the NGINX Ingress Controller
- Basic familiarity with YAML and the Linux command line
- Docker or a container registry account if you plan to use a custom image
Assumed knowledge: You should understand what containers are and have run basic kubectl commands before. You don’t need to be a Kubernetes expert.
Estimated time: 30–45 minutes, depending on your cluster setup.
If you don’t have an Ingress controller yet, you can install the NGINX one quickly. Check the official NGINX Ingress Controller installation guide for your specific environment before continuing.
Step-by-Step Guide to Deploy and Expose Applications on Kubernetes
See also: How to Configure Opnsense Firewall Rules for Network Segmentation
Follow these steps in order. Each one builds on the last.
Step 1: Create a Namespace
Namespaces keep your resources organized. Create one for this tutorial.
kubectl create namespace myapp
This keeps your Deployment, Service, and Ingress separate from other cluster resources.
Step 2: Write the Deployment Manifest
A Deployment tells Kubernetes how many replicas of your app to run and which container image to use. Create a file called deployment.yaml.
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-deployment
namespace: myapp
spec:
replicas: 2
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: nginx:latest
ports:
- containerPort: 80
This uses the official NGINX image as a stand-in for your application. The replicas: 2 field means Kubernetes runs two identical pods.
Step 3: Apply the Deployment
Apply the manifest to your cluster.
kubectl apply -f deployment.yaml
Verify the pods are running.
kubectl get pods -n myapp
You should see two pods with a status of Running. If they show Pending, check your node resources.
Step 4: Create a Service
A Service gives your pods a stable internal IP address and DNS name. Without it, pods can’t reliably talk to each other or be reached by Ingress. Create a file called service.yaml.
apiVersion: v1
kind: Service
metadata:
name: myapp-service
namespace: myapp
spec:
selector:
app: myapp
ports:
- protocol: TCP
port: 80
targetPort: 80
type: ClusterIP
The ClusterIP type makes the service reachable only inside the cluster. Ingress will handle external access.
Apply it now.
kubectl apply -f service.yaml
Confirm the service exists.
kubectl get svc -n myapp
Step 5: Create the Ingress Resource
The Ingress resource defines rules for routing external HTTP traffic to your Service. Create a file called ingress.yaml.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myapp-ingress
namespace: myapp
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: nginx
rules:
- host: myapp.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: myapp-service
port:
number: 80
Replace myapp.example.com with your actual domain. If you’re testing locally with Minikube, you can add a fake entry to /etc/hosts.
Apply the Ingress.
kubectl apply -f ingress.yaml
Step 6: Test Local Access with /etc/hosts (Minikube Only)
If you’re using Minikube, get the cluster IP first.
minikube ip
Then open /etc/hosts and add this line, replacing the IP with your Minikube IP.
192.168.49.2 myapp.example.com
Now open a browser and visit http://myapp.example.com. You should see the NGINX welcome page.
Step 7: Verify Everything is Working
Run a final check on all resources.
kubectl get all -n myapp
kubectl describe ingress myapp-ingress -n myapp
The describe command shows you the routing rules and any events. This is your best debugging tool when traffic isn’t routing correctly.
Troubleshooting Common Issues When Exposing Kubernetes Applications
Even experienced engineers run into problems here. These are the most common ones.
Pods stuck in Pending state: Your nodes don’t have enough CPU or memory. Run kubectl describe pod <pod-name> -n myapp and check the Events section for resource errors.
Service not routing traffic: Double-check your label selectors. The selector in your Service must exactly match the labels in your Deployment pod template. A typo here breaks everything silently.
Ingress returns 404: The Ingress controller is running but can’t find your Service. Confirm the Service name and namespace match what’s in your Ingress manifest. Also verify ingressClassName: nginx matches your controller’s class name.
Ingress returns 502 Bad Gateway: The Ingress controller reached your Service, but the pods aren’t responding. Check pod logs with kubectl logs <pod-name> -n myapp.
No address assigned to Ingress: Your Ingress controller may not be installed or may be in a different namespace. Run kubectl get pods -n ingress-nginx to confirm it’s running.
For deeper reading on Kubernetes networking concepts, the official Kubernetes Ingress documentation is thorough and well-maintained.
Conclusion
You now know how to deploy and expose applications on Kubernetes using Deployments, Services, and Ingress. You created a Deployment to manage your pods, a ClusterIP Service to give them a stable internal address, and an Ingress resource to route external traffic in. These three pieces work together in almost every production Kubernetes setup you’ll encounter. From here, you can explore adding TLS certificates to your Ingress using cert-manager, setting up health checks and resource limits on your Deployments, or configuring multiple path-based routes in a single Ingress resource. Each of those topics builds directly on what you practiced here. Keep your YAML files version-controlled and treat your cluster configuration like code.
