Part 1 was about the mental model.
This article is about what happens after that model starts to feel stable.
Once you understand Pods, Deployments, Services, nodes, and desired state, Kubernetes stops looking mysterious. But that does not mean you are ready to run applications on it comfortably.
That next step is where many developers get stuck.
They understand the nouns, but the first real manifest still feels dense. They know a Deployment creates Pods, but they are not yet sure how to handle configuration, health checks, updates, scaling, or persistent data. They can read YAML, but not always explain which parts matter most.
That is what Part 2 is for.
This article assumes you already understand the foundations covered in Part 1. We are going to stay at the application layer and focus on the objects and behaviors developers actually touch when they move from "I know what Kubernetes is" to "I can deploy and operate a service on it without guessing."
By the end, you should understand:
- how Kubernetes handles application configuration
- when to use ConfigMaps and Secrets
- why probes matter more than they first appear
- how rolling updates actually behave
- what scaling means in practical terms
- how storage changes the workload model
- why namespaces help even before you need advanced multi-tenancy
- how to read manifests without treating every field equally
- where to start when a workload is not behaving correctly
The goal here is not to cover every Kubernetes feature. The goal is to make the day-to-day path of running an application feel much less opaque.
Running an App on Kubernetes Means Managing More Than Containers
At the beginner level, it is easy to think the basic deployment story is finished once a container starts and a Service can reach it.
In practice, that is just the beginning.
Real applications need configuration.
They need environment-specific values, database credentials, health checks, safe rollouts, memory boundaries, persistent data in some cases, and a sane way to debug when one of those pieces is wrong.
This is also the point where Kubernetes starts feeling either useful or frustrating.
If you treat it as a place to dump containers, the friction feels arbitrary.
If you treat it as a platform that wants explicit declarations for the parts operators usually manage manually, the pieces line up more cleanly.
That framing matters.
Kubernetes is asking you to describe operational intent in a structured way.
ConfigMaps: Configuration That Is Not Secret
Applications nearly always need configuration that changes across environments.
Maybe your API needs:
- a log level
- a base URL for another service
- a feature flag
- a queue name
- a cache TTL
These values are configuration, but they are not credentials.
That is where ConfigMaps come in.
What a ConfigMap is for
A ConfigMap stores non-sensitive configuration data outside the container image.
That gives you an important separation:
- the image contains the application code and runtime environment
- the ConfigMap contains environment-specific values
This keeps you from rebuilding images just because one configuration value changed.
Example ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
name: demo-api-config
data:
LOG_LEVEL: info
CACHE_TTL_SECONDS: "60"
FEATURE_SIGNUPS_ENABLED: "true"
You can then consume those values in a Pod or Deployment.
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo-api
spec:
replicas: 2
selector:
matchLabels:
app: demo-api
template:
metadata:
labels:
app: demo-api
spec:
containers:
- name: api
image: myorg/demo-api:1.2.0
envFrom:
- configMapRef:
name: demo-api-config
The important operational idea
ConfigMaps are not just a cleaner way to store key-value pairs. They are a way to keep deployments environment-aware without baking those differences into the image.
That separation becomes more valuable as environments multiply.
Common mistake
Do not put secrets into ConfigMaps just because they are convenient.
If it is a credential, token, certificate, or password, it belongs in a Secret, not a ConfigMap.
Secrets: Sensitive Configuration With Caveats
Kubernetes has a dedicated object for sensitive data: the Secret.
Typical Secret contents include:
- database passwords
- API tokens
- client certificates
- private keys
Example Secret
apiVersion: v1
kind: Secret
metadata:
name: demo-api-secrets
type: Opaque
stringData:
DATABASE_URL: postgres://app_user:supersecret@postgres:5432/app
JWT_SIGNING_KEY: change-me
Then wire it into the Deployment:
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo-api
spec:
replicas: 2
selector:
matchLabels:
app: demo-api
template:
metadata:
labels:
app: demo-api
spec:
containers:
- name: api
image: myorg/demo-api:1.2.0
envFrom:
- secretRef:
name: demo-api-secrets
What developers often misunderstand
A Kubernetes Secret is not magic encryption by default in every environment.
It is an object meant for sensitive data handling, but whether that data is encrypted at rest depends on cluster configuration. Access control also matters. If too many workloads or users can read Secrets, calling them "secrets" does not buy you much.
So the practical rule is:
Use Secrets for sensitive configuration, but do not confuse the object type with a full security strategy.
ConfigMaps and Secrets belong together
Most real applications need both.
- ConfigMap for application settings
- Secret for sensitive values
That pairing is normal, not a sign of complexity gone wrong.
Probes: How Kubernetes Decides Whether Your App Is Healthy
Probes are one of the first places where Kubernetes starts making decisions on behalf of your application.
That makes them powerful and dangerous.
If probes are configured well, Kubernetes can restart unhealthy containers, avoid sending traffic too early, and keep bad rollouts from becoming worse.
If probes are configured badly, Kubernetes can create failure loops that look like mysterious instability.
There are three probe types to know.
Liveness probe
The liveness probe answers:
Should this container be restarted?
Use it when a container can get stuck in a bad state and restarting it is a reasonable recovery action.
Readiness probe
The readiness probe answers:
Is this container ready to receive traffic?
This is often the most important probe for application behavior.
An app may be running as a process but still not ready to serve requests because:
- it is waiting for a database connection
- it is warming caches
- it is running migrations
- it has not completed startup work yet
If readiness is false, the Pod stays out of Service traffic.
Startup probe
The startup probe answers:
Should Kubernetes give this app more time to start before judging it by other probes?
This is useful for slow-starting applications that would otherwise fail liveness or readiness checks during initialization.
Example probe configuration
containers:
- name: api
image: myorg/demo-api:1.2.0
ports:
- containerPort: 3000
readinessProbe:
httpGet:
path: /ready
port: 3000
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 15
periodSeconds: 20
Common probe mistakes
- using the same endpoint for readiness and liveness without thinking about the consequences
- making the liveness probe too strict so healthy-but-busy containers get restarted
- declaring readiness too early and sending traffic before dependencies are available
- forgetting that a failing dependency does not always mean the right recovery action is container restart
The broader lesson is this:
Probes are not just status checks. They directly shape runtime behavior.
Rolling Updates: Safe by Default, Not Safe by Accident
One of the biggest practical benefits of Deployments is controlled rollout behavior.
When you update the image version or Pod template, Kubernetes does not have to destroy every old Pod and then start every new Pod. It can roll forward gradually.
That is the default deployment strategy for most stateless applications, and it is one of the first operational features teams come to rely on.
What happens during a rolling update
At a high level:
- Kubernetes creates new Pods using the updated template.
- It waits for them to become ready.
- It gradually removes old Pods.
- It continues until the new version fully replaces the old one.
This is why readiness probes matter so much. If a Pod is not truly ready, rollout safety becomes partly fiction.
Useful rollout commands
kubectl rollout status deployment/demo-api
kubectl rollout history deployment/demo-api
kubectl rollout undo deployment/demo-api
These commands are worth remembering because rollouts are one of the first places where Kubernetes feels operational rather than purely declarative.
A useful mental model
Deployments are not just "run my Pods."
They are versioned workload controllers with rollout behavior.
That makes them much more than a replica counter.
When rolling updates still go wrong
Rolling updates reduce risk, but they do not eliminate it.
You can still deploy a bad version that:
- starts successfully but serves incorrect responses
- passes probes but fails under real traffic
- breaks background jobs while the HTTP layer looks healthy
- depends on an incompatible database change
Kubernetes helps with rollout mechanics. It does not validate application correctness for you.
Scaling: More Replicas Is the Easy Part
Scaling sounds straightforward at first.
If one Pod is not enough, run more Pods.
Sometimes that really is enough.
But even at the intermediate level, it helps to separate two very different ideas:
- scaling the number of replicas
- scaling the whole system behavior safely
Horizontal scaling basics
The most direct form of scaling is increasing replica count.
spec:
replicas: 4
You can also scale imperatively:
kubectl scale deployment/demo-api --replicas=4
That tells Kubernetes to keep four Pods running instead of two.
What scaling assumes
Horizontal scaling works best when your application instances are largely interchangeable and stateless.
If each instance depends heavily on in-memory local state, horizontal scaling gets much harder.
That is why Kubernetes and modern application architecture are often discussed together. The platform works especially well when workloads are designed to be replicated cleanly.
Scaling is not only about compute
Suppose you scale your API from two Pods to eight.
That may help request throughput, but it can also increase pressure on:
- the database
- downstream APIs
- shared caches
- connection pools
- queues
The cluster may be fine while the system as a whole is not.
That is a useful habit to develop early: when you think about scaling, think about dependencies too.
Requests and Limits: How Kubernetes Thinks About Resource Usage
At some point, Kubernetes needs to decide where a Pod can run.
It cannot do that responsibly if it has no idea how much CPU or memory the workload needs.
That is where resource requests and limits come in.
Request
A request tells Kubernetes the minimum amount of a resource a container expects to need.
The scheduler uses requests when deciding where the Pod can be placed.
Limit
A limit tells Kubernetes the maximum amount of a resource a container is allowed to use.
If a container exceeds memory limits, it can be killed. CPU limits behave differently, but they still constrain runtime behavior.
Example
resources:
requests:
cpu: "250m"
memory: "256Mi"
limits:
cpu: "500m"
memory: "512Mi"
Why this matters so much
Without requests and limits, workloads can become noisy neighbors. One service may starve another, or the scheduler may pack workloads onto nodes with unrealistic assumptions.
With bad requests and limits, you get a different problem:
- requests too low mean bad scheduling assumptions
- limits too low mean unnecessary throttling or kills
- requests too high mean wasted capacity and scheduling friction
This is one of the first places where Kubernetes pushes developers toward resource honesty.
That is uncomfortable at first, but useful.
Persistent Storage Changes the Workload Story
So far, most of the examples fit stateless services nicely.
But not every workload is stateless.
Some applications need persistent data that outlives the Pod itself.
That changes the model.
Why Pod storage is not enough
Pods are replaceable. If a Pod dies and a new one appears, local writable container state inside that Pod is not something you should rely on for durable application data.
If the data matters after restart, you need persistent storage.
The intermediate-level idea to understand
You do not need to master every Kubernetes storage object yet. But you do need to understand that persistent data needs explicit handling and usually introduces tighter coupling between a workload and storage infrastructure.
In practical terms, this often means:
- volumes mounted into Pods
- claims against storage resources
- different workload choices for stateful versus stateless applications
Why this matters for developers
A surprising number of application problems come from designing something as if it were stateless when it really is not.
If the application depends on local files, queue state, or embedded data directories, you need to understand how Kubernetes will treat that state when Pods move or restart.
This is where architecture and platform behavior start affecting each other directly.
Namespaces: Lightweight Separation That Pays Off Early
Namespaces are often introduced as an organizational feature, and that is true, but underselling them as "just folders" misses why they matter.
A namespace is a way to separate resources within a cluster.
That separation is useful for:
- environments like dev, staging, and production
- teams sharing a cluster
- limiting accidental naming collisions
- scoping permissions and policies later
Why developers should care
Even before you need advanced multi-team governance, namespaces make it easier to reason about where resources live.
Without namespaces, a shared cluster becomes noisy quickly.
With namespaces, demo-api in development and demo-api in staging can be meaningfully separated.
A practical command pattern
kubectl get pods -n staging
kubectl get services -n production
kubectl describe deployment demo-api -n dev
You do not need to overcomplicate namespace strategy early on, but you should avoid pretending a cluster is one flat global space forever.
How to Read a Manifest Without Getting Lost
One reason Kubernetes YAML feels intimidating is that not every field has the same importance.
Beginners often read manifests top to bottom as if every line deserves equal attention.
That makes the file feel heavier than it really is.
Here is a more useful reading order.
1. Start with kind
What object are you looking at?
Deployment, Service, ConfigMap, Secret, StatefulSet, Job, Ingress, and so on.
If you do not know the role of the object, the rest of the file is just syntax.
2. Look at metadata.name
What is this thing called?
That helps you connect the object to commands, logs, and other references.
3. Focus on the spec
The spec is where the desired behavior is declared.
For a Deployment, the most important questions are usually:
- how many replicas?
- what labels select the Pods?
- what image is running?
- what ports, env vars, probes, and resources are configured?
For a Service, ask:
- which Pods does it select?
- what port does it expose?
- what target port does it send traffic to?
4. Follow labels and selectors carefully
Many Kubernetes problems are not exotic. They come from mismatches between objects that were supposed to connect.
Examples:
- a Service selector does not match Pod labels
- a Deployment selector does not match its template labels
- a Secret name referenced by a Pod does not exist in the same namespace
5. Ignore noise until it becomes relevant
Some fields matter only in more advanced cases. That is fine. You do not need to fully parse every field to understand the resource's core purpose.
Manifest reading gets much easier once you stop trying to absorb every line equally.
Debugging: Where to Start When the Workload Is Broken
This is where theory meets reality.
When an application is not working on Kubernetes, the problem is usually in one of a few places:
- the container never starts correctly
- the Pod starts, but fails probes
- the Service is not routing traffic where you expect
- configuration is missing or wrong
- dependencies are reachable in theory but failing in practice
- the workload was scheduled, but resources or permissions are wrong
A good debugging path is more valuable than memorizing ten commands randomly.
Step 1: Check object state
kubectl get pods
kubectl get deployments
kubectl get services
Start broad.
Are Pods running? Pending? CrashLoopBackOff? Not ready?
Step 2: Describe the failing object
kubectl describe pod demo-api-abc123
kubectl describe deployment demo-api
describe is often where the first useful clues show up:
- image pull failures
- probe failures
- scheduling constraints
- missing secrets or config maps
- recent events
Step 3: Inspect logs
kubectl logs demo-api-abc123
kubectl logs demo-api-abc123 --previous
--previous is especially useful when a container is crashing and restarting quickly.
Step 4: Verify assumptions across objects
Check that:
- the Service selector matches Pod labels
- the container is listening on the expected port
- env vars are present
- the namespace is correct
- probes point to real endpoints
Step 5: Reduce the problem to one layer
Kubernetes issues feel overwhelming when every layer is mixed together.
Try to identify which of these is failing first:
- scheduling
- startup
- readiness
- networking
- configuration
- dependency access
That makes the investigation much more manageable.
flowchart TD
A[Application not working] --> B{Are Pods running?}
B -->|No| C[Check describe events and scheduling]
B -->|Yes| D{Are Pods ready?}
D -->|No| E[Check probes and startup logs]
D -->|Yes| F{Is traffic reaching the Pods?}
F -->|No| G[Check Service selector and ports]
F -->|Yes| H[Check app config and dependencies]
The key habit is to debug in layers, not in panic.
A Practical Intermediate Deployment Shape
Once you put the common pieces together, a realistic intermediate-level application deployment often includes:
- a Deployment
- a Service
- one ConfigMap
- one Secret
- resource requests and limits
- readiness and liveness probes
- a namespace decision
That is already enough to represent a serious amount of operational intent.
Notice what happened between Part 1 and Part 2.
In Part 1, Kubernetes was mostly a control model.
In Part 2, it becomes an application runtime contract.
You are not only saying "run this app."
You are saying:
- how it should be configured
- when it is healthy
- how much it expects to consume
- how it should be updated
- what traffic path reaches it
- which values must stay secret
That is the practical center of day-to-day Kubernetes usage.
Final Takeaways
At the intermediate level, the challenge is not learning more object names for their own sake.
The challenge is understanding how Kubernetes expresses the operational needs of an application.
The most important ideas from this part are:
- ConfigMaps separate non-sensitive configuration from the image
- Secrets handle sensitive values, but still need real access control and cluster security
- probes drive traffic and restart behavior, so they need careful design
- rolling updates help with safe delivery, but they do not prove correctness
- scaling affects dependencies, not just replica counts
- requests and limits shape both scheduling and runtime behavior
- persistent storage changes the workload model significantly
- namespaces make shared clusters much easier to reason about
- debugging gets easier when you isolate the failing layer first
If Part 1 gave you the mental model, Part 2 should give you a more realistic sense of what it means to operate an application on Kubernetes without treating the platform like a black box.
In Part 3, we will move into the advanced side of the story: scheduling behavior, ingress and networking depth, RBAC, autoscaling nuance, observability, rollout safety, multi-environment design, and the tradeoffs that matter once your Kubernetes usage starts becoming production-critical.
