Introduction: The Networking Challenge in Kubernetes

When you deploy applications in Kubernetes, you quickly run into a fundamental problem: Pods are temporary. They can be created, destroyed, or rescheduled at any moment, and each time a Pod is created, it gets a new IP address. If your application needs to communicate with other Pods, how can it keep track of constantly changing IP addresses? The answer is Services. A Service provides a stable endpoint — a consistent way to reach a group of Pods, even as those Pods come and go. In this lesson, we'll explore the core mechanism that makes Services work: labels and selectors. This is the foundation that powers all Service types in Kubernetes.

Labels and Selectors

You've already learned about labels — those key-value pairs you attach to Kubernetes objects like Pods. As a quick reminder, labels are tags that describe what a Pod is or what it does (like app: web or tier: frontend). They appear in your Deployment YAML under the Pod template's metadata section, and when a Deployment creates Pods, it stamps each one with the labels you've defined.

Now we'll see how labels become the foundation of Kubernetes networking through selectors. A selector is a set of matching criteria that a Service uses to find Pods. When you create a Service, you specify a selector that describes which labels you're looking for. The Service then continuously queries Kubernetes for all Pods that have matching labels, and it routes traffic to those Pods.

Here's the critical insight: Services are NOT bound to specific Pods. They don't remember individual Pod names or IP addresses. Instead, they dynamically select any Pod that matches their selector criteria. This means if a Pod dies and a new one is created with the same labels, the Service automatically starts routing traffic to the new Pod. If you scale your Deployment up or down, the Service adapts immediately.

The matching process is straightforward. A Service's selector specifies one or more key-value pairs, and a Pod must have ALL of those labels to be selected. For example, if a Service selector says app: wired-demo and tier: web, then only Pods with both of those exact labels will receive traffic from that Service.

Let's see how selectors appear in a Service definition:

The selector section is where the magic happens. This Service is looking for Pods with the labels app: wired-demo AND . Any Pod that has both of these labels will be included in the Service's routing pool. The Service doesn't care about the Pod's name, its IP address, or when it was created — it only cares about the labels.

Creating a Properly Wired Deployment and Service

Let's put labels and selectors together by creating a complete example. We'll build a simple web application Deployment and a Service that routes traffic to it. The key is ensuring that the labels on the Pods match the selector in the Service — this is what we call "wiring" them together correctly.

First, let's create our Deployment. Save this as deployment-web.yaml:

Let's break down the important parts. The metadata.labels at the top are labels for the Deployment object itself — these are optional and mainly for organization. The crucial labels are under template.metadata.labels. These are the labels that will be stamped on every Pod created by this Deployment. We're using app: wired-demo to identify this as part of our demo application, and tier: web to indicate it's the web tier.

The spec.selector.matchLabels section tells the Deployment which Pods it should manage. This must match the labels in the Pod template; otherwise, the Deployment won't be able to track its Pods. The replicas: 2 means we want two copies of this Pod running. Finally, the container spec defines a simple nginx web server listening on port 80.

Now let's create the Service that will route traffic to these Pods. Save this as service-web.yaml:

Verifying the Wiring with Endpoints

Creating a Service and a Deployment isn't enough — we need to verify that they're actually connected. Kubernetes provides a resource called Endpoints that shows us exactly which Pod IP addresses a Service is routing traffic to. Think of endpoints as the "phone book" that the Service uses — it's the list of actual destinations where traffic will be sent.

When a Service is properly wired to Pods, Kubernetes automatically creates an Endpoints object with the same name as the Service. This object contains the IP addresses and ports of all Pods that match the Service's selector. If the endpoints list is empty, it means the Service couldn't find any matching Pods — usually because the labels don't match.

Let's check our Pods first to see what labels they have:

The -l flag filters Pods by labels, and --show-labels displays all labels on each Pod. You should see output like this:

Perfect! Both Pods have the labels app=wired-demo and tier=web, which match our Service's selector. Now let's check the Service itself:

This shows basic information about the Service:

The Service has been assigned a stable Cluster IP (10.96.123.45 in this example — yours will be different). This IP won't change even if all the Pods are replaced. Now let's look at the endpoints:

You should see output like this:

Dynamic Behavior: Services Adapt to Changes

Now let's see the real power of label-based selection. Because Services use selectors rather than hardcoded Pod references, they automatically adapt when Pods change. Let's demonstrate this by scaling our Deployment up from 2 replicas to 4:

You should see:

The Deployment will immediately start creating two more Pods. Here's the crucial part: we don't need to update the Service at all. The Service's selector is still looking for Pods with app: wired-demo and tier: web, and the new Pods will have those exact labels because they're created from the same Deployment template.

Let's watch the endpoints update in real time:

The -w flag means "watch" — it will continuously display updates. You'll see something like this:

Watch how the endpoints list grows as new Pods come online! First, you see the original two IPs, then a third appears, then a fourth. The Service automatically discovers the new Pods and adds them to its routing pool. Press Ctrl+C to stop watching.

This dynamic behavior is what makes Kubernetes so powerful for running applications at scale. If a Pod crashes, the Deployment creates a new one with the same labels, and the Service automatically routes traffic to it. If you need more capacity, you scale up the Deployment, and the Service immediately starts using the new Pods. If you need less capacity, you scale down, and the Service stops routing to the terminated Pods. All of this happens automatically because of the label-selector relationship.

Let's verify the final state:

You should now see four Pods:

Summary and Practice Preparation

You've now learned the fundamental mechanism that powers Kubernetes networking: Services use selectors to dynamically find Pods based on labels. This label-selector relationship is not a one-time binding — it's a continuous query that adapts as Pods are created, destroyed, or scaled. When you create a Deployment, you define labels on the Pod template. When you create a Service, you define a selector that matches those labels. Kubernetes handles the rest, automatically maintaining the connection through the Endpoints resource.

In the upcoming practice exercises, you'll create your own Deployments and Services, verify their connections using endpoints, and observe how Services adapt when you scale Deployments. This hands-on experience will solidify your understanding of Service wiring. Remember: this foundation applies to all Service types we'll explore in future lessons — the only difference will be how traffic reaches the Service from outside the cluster.

Sign up
Join the 1M+ learners on CodeSignal
Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal