In the previous lessons, you created Pods using YAML manifests and learned the declarative workflow with Kubernetes. You applied manifests, verified Pods were running, and troubleshot common failures like image pull errors and crash loops. You worked with simple nginx Pods, but they used default configurations.
Real applications usually need more than just an image. They often need environment-specific settings (development, staging, production), database connection strings, API keys, feature flags, and clear documentation of which ports they listen on. Sometimes they also need a different startup command from the image's default.
Kubernetes gives you several ways to configure the containers inside your Pods without rebuilding images. In this lesson, you'll learn three core techniques: passing environment variables to containers, declaring container ports, and overriding the default startup command. You'll also use kubectl exec to verify that your configuration actually took effect.
Environment variables are a standard way to configure applications. They're simple key–value pairs available to processes at runtime. Applications read them to decide how to behave — for example, which database to connect to or which environment they're running in.
The major benefit is that you can use the same container image in multiple environments and change only the environment variables, not the code or image.
In Kubernetes, you configure environment variables in the Pod spec under the container's env field:
env is a list. Each entry has a name, which is the environment variable name inside the container (here, APP_ENV), and a value, which is the value as a string (here, "dev"). Kubernetes injects these variables into the container before it starts, and any process inside the container can read them.
In a real app you might define multiple variables like DATABASE_URL, API_KEY, LOG_LEVEL, or feature flags such as FEATURE_FLAG_NEW_UI. All environment variable values are strings; if your app needs a number or boolean, it parses the string itself.
Environment variables are set when the container starts and don't change while it's running. To change them, you need to restart the container (in practice: recreate the Pod, or update higher-level resources like Deployments, which you'll see later). By separating configuration from code, environment variables make your images reusable and your deployments more flexible.
The ports field in a Pod spec lets you declare which ports your container listens on:
This declaration does not open or expose the port. The application in the container decides which port to listen on, and if you remove the ports section, nginx still listens on port 80 by default.
Port declarations are useful for several reasons. They serve as documentation, so anyone reading the manifest can immediately see which ports the container uses. They help some Kubernetes features (like Services) and monitoring tools configure themselves more easily. They also provide a consistent place to find which ports a service uses, which helps with service discovery.
ports is a list, so you can declare multiple ports if needed. Each item can include containerPort (required), which is the port number inside the container, and optional fields like protocol (TCP, UDP, or SCTP, with TCP as the default) and name (a human-readable name such as http for 80 or https for 443).
Declaring ports doesn't expose them to other Pods or the outside world. To actually route traffic to your Pods, you'll use a Service resource in a later lesson. Port declarations are the first step because they document what exists so other resources can use that information.
Every container image has a default command defined in its Dockerfile using CMD or ENTRYPOINT. For the nginx image, the default command starts the web server in the foreground. However, sometimes you need to run a different command.
Kubernetes lets you override the default command with the command field. For example, to simply print the version and exit instead of starting the server, you could use:
command is an array of strings, where the first element is the program (nginx) and the rest are arguments (-v). This form is a direct, shell-less way of specifying the program and its arguments, which makes execution more predictable.
A note on restart behavior: A Pod's default restartPolicy is Always, which means Kubernetes automatically restarts the container every time it exits — even when it exits successfully. If you ran command: ["nginx", "-v"] in a Pod with the default restart policy, the container would print the version, exit, and then immediately be restarted, looping indefinitely. For a true one-shot command like this, you would add restartPolicy: Never to the Pod spec so the container runs once and the Pod stops cleanly.
You might override the command for debugging, such as running sleep 3600 to keep a crashing container alive so you can inspect it. You can also run the same image in different modes by changing , for example using it once as a web server and once as a background worker. Another use is adding extra flags like or without rebuilding the image.
Here is the full manifest that combines environment variables, port declarations, and a command override.
In this example, we will override the command to sleep 3600. This effectively disables the web server and just keeps the container alive. This is a common pattern for debugging: if an application keeps crashing, you can override the command to sleep to keep the Pod running, allowing you to log in and check the filesystem. Using sleep also works well with the default restartPolicy: Always — the container stays alive for an hour, giving you time to inspect it, rather than exiting immediately and restarting in a loop.
This Pod sets APP_ENV=dev for the container, documents that it normally listens on port 80, and explicitly starts a sleep command instead of nginx. To create this Pod, save the YAML as pod-configured.yaml and run:
Check that it's running:
You should see:
If the status isn't Running, use:
To confirm that your configuration actually took effect inside the container, use kubectl exec to run commands in the Pod.
To check the APP_ENV environment variable, run:
Here kubectl exec runs a command in a container, -it keeps stdin open and allocates a TTY, nginx-configured is the Pod name, -- separates kubectl options from the in-container command, and printenv APP_ENV prints the value of APP_ENV. You should see dev as the output.
To see all environment variables, run:
You'll see system variables, variables from the image, and your custom APP_ENV=dev. You can also run other commands as long as they exist in the image, for example:
or:
This opens a shell inside the container so you can inspect files, processes, or network connectivity.
Note: Many minimal, security-hardened container images (common in production) do not include a shell. In those cases, with will fail, and you must rely on logs or other debugging tools. However, since we are using the standard image here, the shell is available.
When you're done with your configured Pod, delete it to keep your cluster clean.
You should see something like:
This confirms the Pod and its container have been removed from the cluster.
In this lesson, you learned how to move from a very basic Pod to one that is more configurable and closer to real-world usage. You saw how to use environment variables through the env field to control application behavior without changing the image, keeping configuration separate from code and making the same image reusable across environments.
You also learned how to declare container ports using the ports field, understanding that this does not open the port but documents it and helps Kubernetes tools and other resources understand how to communicate with your containers. Command overrides using the command field showed you how to take control of container startup, support debugging scenarios (like using sleep to keep a container alive), and run the same image in different modes. You also saw how the default restartPolicy: Always affects short-lived commands, and that restartPolicy: Never is the right choice when you want a container to run once and stop cleanly.
Finally, you used kubectl exec to verify that configuration changes are visible inside a running container, which is a key technique for learning and troubleshooting. These configuration patterns — environment variables, port declarations, command overrides, and verification with kubectl exec — form a foundation you will use repeatedly as you build and operate applications on Kubernetes.
