Pod Resource Basics

Introduction: From Verification to Deployment

In the previous lesson, you verified that kubectl works and can connect to your Kubernetes cluster. You confirmed your environment is ready by checking the client version, cluster information, and available nodes. Now comes the exciting part: actually running something on Kubernetes.

The natural next question is: "How do I deploy my application to this cluster?" In this lesson, you'll learn how to create and manage your first Pod using a declarative configuration file. By the end, you'll understand what Pods are, why we use YAML files to define them, and how to create, verify, update, and delete Pods using kubectl. This is the foundation for everything else you'll do with Kubernetes.

What Is a Pod?

A Pod is the smallest deployable unit in Kubernetes. When you want to run an application in Kubernetes, you don't deploy containers directly — you deploy Pods. While a Pod can technically contain multiple containers, the most common pattern is one container per Pod. Think of a Pod as a wrapper around your container that provides additional capabilities like networking, storage, and lifecycle management.

Why does Kubernetes use this wrapper instead of just running containers directly? The Pod concept allows Kubernetes to group tightly coupled containers together so they share resources and context. Specifically, all containers within a single Pod share:

  1. Networking: They share the same network namespace, meaning they have the same IP address and port space. Containers inside the same Pod can communicate with each other using localhost, just as if they were processes running on the same server.
  2. Storage: They can share storage volumes, allowing them to read and write to the same files.

In real-world terms, a Pod represents one instance of your application running in the cluster. If you're running a web server, one Pod equals one running copy of that web server. If you need to handle more traffic, you'd run multiple Pods (we'll cover that in later lessons).

It is also important to understand that Pods are ephemeral (temporary). They are designed to be disposable. If a Pod dies or the node it is running on fails, the Pod is not "healed"; instead, it is usually replaced by a brand new Pod. This is why we treat Pods as disposable resources rather than unique, permanent servers.

For this lesson, we'll create a Pod that runs nginx, a popular web server. This is a perfect first example because nginx is simple, well-known, and easy to verify — once it's running, we know we have a working web server in our cluster. The Pod will contain a single nginx container, which is the typical pattern you'll use most often.

Two Approaches: Imperative vs Declarative

Before we create our Pod, you need to understand that Kubernetes offers two different approaches for managing resources: imperative and declarative.

The imperative approach means telling Kubernetes exactly what to do, step by step. For example, you might run kubectl run nginx-pod --image=nginx:1.25 to create a Pod. This command directly instructs Kubernetes to create a Pod right now with these specific settings. It's fast and convenient for quick testing or one-off tasks. However, imperative commands have a significant drawback: they're not reproducible or documented. If you create a Pod imperatively and then delete it, you'd need to remember the exact command to recreate it. There's no file you can save, version control, or share with your team.

The declarative approach means telling Kubernetes what state you want and letting Kubernetes figure out how to achieve it. You write a YAML file that describes your desired Pod, then use kubectl apply -f pod.yaml to tell Kubernetes, "make the cluster match this file." This approach is more powerful because the YAML file serves as documentation, can be stored in version control (like Git), and can be easily shared and reproduced. If you need to recreate the Pod later, you just apply the same file again.

We focus on the declarative approach in this course because it's the standard practice for production environments. When you work on real projects, you'll store your Kubernetes manifests in Git repositories alongside your application code. This makes your infrastructure reproducible and auditable. That said, imperative commands are still useful for quick experiments and troubleshooting, so it's good to know both approaches exist.

Anatomy of a Pod Manifest

Now let's look at how to write a Pod manifest. A manifest is simply a YAML file that describes a Kubernetes resource. Every Pod manifest has four essential sections that tell Kubernetes what you want to create.

Let's start with the first two sections:

YAML
apiVersion: v1
kind: Pod

The apiVersion field tells Kubernetes which version of the API to use for this resource. Different resource types use different API versions. For Pods, we use v1, which is the stable, core API. You'll see other API versions like apps/v1 or batch/v1 for different resource types in later lessons, but for now, just know that Pods use v1.

The kind field specifies what type of resource you're creating. In this case, we're creating a Pod. Kubernetes supports many different kinds of resources (Deployments, Services, ConfigMaps, etc.), and this field tells Kubernetes which one you want. The combination of apiVersion and kind tells Kubernetes exactly how to interpret the rest of your manifest.

Next comes the metadata section:

YAML
metadata:
  name: nginx-pod

The metadata section contains information about the resource itself. The most important field here is name, which gives your Pod a unique identifier within the cluster. You'll use this name when you want to view, update, or delete the Pod later. Pod names must be unique within a namespace (we'll cover namespaces in a future lesson), and they should be descriptive. Here, we're calling our Pod nginx-pod because it runs nginx — simple and clear.

Finally, we have the specification section:

YAML
spec:
  containers:
    - name: nginx
      image: nginx:1.25
      ports:
        - containerPort: 80

The spec section is where you define what you actually want the Pod to do. This is the most important part of the manifest. The containers field is a list (notice the dash - in YAML, which indicates a list item) of containers to run in this Pod. Even though we're only running one container, it's still defined as a list because Pods can technically run multiple containers.

Each container needs a name (we're calling ours nginx), an image (we're using nginx:1.25, which means version 1.25 of the nginx image from Docker Hub), and, optionally, a ports section. Notice how name, image, and ports are all indented under the first list item (the dash -) — this means they all belong to that container definition. Everything indented under a list item is part of that item until you encounter another dash at the same indentation level, which would start a new list item.

The ports field is itself a list, so we use another dash to define a port entry. The containerPort: 80 tells Kubernetes that this container listens on port 80. This doesn't expose the port outside the cluster — it's just documentation that helps Kubernetes understand your container. We'll learn how to actually expose services to the outside world in a later lesson.

Here's the complete manifest all together:

YAML
apiVersion: v1
kind: Pod
metadata:
  name: nginx-pod
spec:
  containers:
    - name: nginx
      image: nginx:1.25
      ports:
        - containerPort: 80

This manifest is declarative — it describes the desired state. It says, "I want a Pod named nginx-pod that runs one container using the nginx:1.25 image and listens on port 80." When you apply this manifest, Kubernetes will make it happen.

Creating and Managing Your First Pod

Now that you understand the manifest structure, let's use it to create your first Pod. Save the YAML content above into a file called pod.yaml. In the CodeSignal environment, you can create this file in the editor provided.

To create the Pod, run this command:

Shell
kubectl apply -f pod.yaml

The kubectl apply command tells Kubernetes to make the cluster match the state described in your file. The -f flag means "from file," and pod.yaml is the filename. When you run this command, kubectl sends the manifest to the control plane, which then schedules the Pod onto an available node and starts the container.

You should see output like this:

text
pod/nginx-pod created

This confirms that Kubernetes received your request and created the Pod. However, "created" doesn't necessarily mean "running yet" — it just means Kubernetes accepted your manifest and started the creation process.

To verify that your Pod is actually running, use this command:

Shell
kubectl get pods

This command lists all Pods in your current namespace. You should see output similar to this:

text
NAME        READY   STATUS    RESTARTS   AGE
nginx-pod   1/1     Running   0          15s

Let's break down what each column means. The NAME column shows the Pod's name (nginx-pod, which matches what we specified in our manifest). The READY column shows how many containers in the Pod are ready versus how many total containers exist. Our Pod has one container, and it's ready, so we see 1/1. The STATUS column shows the Pod's current state — Running means the container is up and executing. You might also see statuses like Pending (Kubernetes is still scheduling or pulling the image), ContainerCreating (the container is being set up), or Error (something went wrong).

The RESTARTS column shows how many times the container has restarted. A healthy Pod should have 0 or very few restarts. If you see many restarts, it usually indicates a problem with your application. The AGE column shows how long the Pod has been running — in this case, 15 seconds.

Updating Resources Declaratively

One of the most powerful features of the declarative model is how it handles updates. You don't need a separate command to update a resource; you use the exact same command you used to create it.

Suppose you want to change the image version of your Pod. In the imperative world, you might need a specific "edit" or "patch" command. In the declarative world, you simply:

  1. Edit your local pod.yaml file to reflect the new desired state.
  2. Run kubectl apply -f pod.yaml again.

Kubernetes will compare the state defined in your file against the live state of the cluster. If it detects differences, it will update the live resource to match your file.

However, it's important to know that not all fields in a Kubernetes resource are updatable. Some fields are immutable, meaning they cannot be changed after the resource is created. For Pods, most of the spec is immutable. For example, you cannot change a container's name, its ports, or its restart policy once the Pod is running. The Pod's name in the metadata section is also immutable.

So, what can you update? The most commonly updated field in a Pod's spec is the container's image. You can also add or modify metadata like labels and annotations (which we'll cover later).

If you try to change an immutable field and run kubectl apply, Kubernetes will reject the change with an error. The only way to change an immutable field is to delete the Pod and recreate it with the new configuration. This immutability is a core design principle that reinforces the idea that Pods are disposable and should be managed by higher-level controllers (like Deployments), which handle the delete-and-recreate process for you.

For example, if you changed the container image in your pod.yaml file from nginx:1.25 to nginx:1.26 and re-ran the apply command, you would see:

text
pod/nginx-pod configured

Notice it says configured instead of created. This confirms that Kubernetes found the existing resource and updated its mutable image field. This property is called idempotency — you can run the same apply command multiple times, and the result will always be the same: the cluster matches your file.

Deleting the Pod

When you're done experimenting with your Pod, you should clean it up. Kubernetes will keep running Pods indefinitely until you explicitly delete them. To delete the Pod, use the same manifest file:

Shell
kubectl delete -f pod.yaml

Notice that we use the same file for creation, updates, and deletion. This is one of the benefits of the declarative approach — the manifest serves as the source of truth for the entire lifecycle. When you run this command, you should see:

text
pod "nginx-pod" deleted

If you run kubectl get pods again, you'll see that nginx-pod is no longer listed. The Pod and its container have been completely removed from the cluster.

This workflow — kubectl apply to create or update, kubectl get to verify, and kubectl delete to clean up — is the fundamental pattern you'll use throughout your Kubernetes journey. The manifest file is your source of truth, and kubectl is your tool for making the cluster match that truth.

Lesson Summary and Next Steps

Congratulations! You've just created and managed your first Pod in Kubernetes. You learned that Pods are the smallest deployable units in Kubernetes, typically wrapping a single container but capable of sharing networking and storage across multiple containers. You understand the difference between imperative commands and declarative manifests, and why we prefer the declarative approach for its reproducibility and documentation benefits.

You can now write a Pod manifest with the four essential sections (apiVersion, kind, metadata, and spec), create and update Pods using kubectl apply -f, verify they're running with kubectl get pods, and clean them up with kubectl delete -f. This declarative workflow is the foundation for everything else in Kubernetes.

Next, you'll get hands-on practice creating and managing Pods yourself, experimenting with different container images and configurations to solidify these concepts.

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