|
| 1 | +--- |
| 2 | +layout: blog |
| 3 | +title: "Kubernetes Configuration Good Practices" |
| 4 | +date: 2025-11-11 |
| 5 | +slug: kubernetes-configuration-good-practices |
| 6 | +evergreen: true |
| 7 | +author: Kirti Goyal |
| 8 | +draft: true |
| 9 | +--- |
| 10 | + |
| 11 | +Configuration is one of those things in Kubernetes that seems small until it's not. Configuration is at the heart of every Kubernetes workload. |
| 12 | +A missing quote, a wrong API version or a misplaced YAML indent can ruin your entire deploy. |
| 13 | + |
| 14 | +This blog brings together tried-and-tested configuration best practices. The small habits that make your Kubernetes setup clean, consistent and easier to manage. |
| 15 | +Whether you are just starting out or already deploying apps daily, these are the little things that keep your cluster stable and your future self sane. |
| 16 | + |
| 17 | +## General Configuration Practices |
| 18 | + |
| 19 | +### Use the latest stable API version |
| 20 | +Kubernetes evolves fast. Older APIs eventually get deprecated and stop working. So, whenever you are defining resources, make sure you are using the latest stable API version. |
| 21 | +You can always check with |
| 22 | +```bash |
| 23 | +kubectl api-resources |
| 24 | +``` |
| 25 | +This simple step saves you from future compatibility issues. |
| 26 | + |
| 27 | +### Store configuration in version control |
| 28 | +Never apply manifest files directly from your desktop. Always keep them in a version control system like Git, it's your safety net. |
| 29 | +If something breaks, you can instantly roll back to a previous commit, compare changes or recreate your cluster setup without panic. |
| 30 | + |
| 31 | +### Write configs in YAML not JSON |
| 32 | +Write your configuration files using YAML rather than JSON. Both work technically, but YAML is just easier for humans. It's cleaner to read and less noisy and widely used in the community. |
| 33 | + |
| 34 | +YAML has some sneaky gotchas with boolean values: |
| 35 | +Use only `true` or `false`. |
| 36 | +Don't write `yes`, `no`, `on` or `off`. |
| 37 | +They might work in one version of YAML but break in another. To be safe, quote anything that looks like a Boolean (for example `"yes"`). |
| 38 | + |
| 39 | +### Keep configuration simple and minimal |
| 40 | +Avoid setting default values that are already handled by Kubernetes. Minimal manifests are easier to debug, cleaner to review and less likely to break things later. |
| 41 | + |
| 42 | +### Group related objects together |
| 43 | +If your Deployment, Service and ConfigMap all belong to one app, put them in a single manifest file. |
| 44 | +It's easier to track changes and apply them as a unit. |
| 45 | +See the [Guestbook all-in-one.yaml](https://github.com/kubernetes/examples/blob/master/web/guestbook/all-in-one/guestbook-all-in-one.yaml) file for an example of this syntax. |
| 46 | + |
| 47 | +You can even apply entire directories with: |
| 48 | +```bash |
| 49 | +kubectl apply -f configs/ |
| 50 | +``` |
| 51 | +One command and boom everything in that folder gets deployed. |
| 52 | + |
| 53 | +### Add helpful annotations |
| 54 | +Manifest files are not just for machines, they are for humans too. Use annotations to describe why something exists or what it does. A quick one-liner can save hours when debugging later and also allows better collaboration. |
| 55 | + |
| 56 | +The most helpful annotation to set is `kubernetes.io/description`. It's like using comment, except that it gets copied into the API so that everyone else can see it even after you deploy. |
| 57 | + |
| 58 | +## Managing Workloads: Pods, Deployments, and Jobs |
| 59 | + |
| 60 | +A common early mistake in Kubernetes is creating Pods directly. Pods work, but they don't reschedule themselves if something goes wrong. |
| 61 | + |
| 62 | +_Naked Pods_ (Pods not managed by a controller, such as [Deployment](/docs/concepts/workloads/controllers/deployment/) or a [StatefulSet](/docs/concepts/workloads/controllers/statefulset/)) are fine for testing, but in real setups, they are risky. |
| 63 | + |
| 64 | +Why? |
| 65 | +Because if the node hosting that Pod dies, the Pod dies with it and Kubernetes won't bring it back automatically. |
| 66 | + |
| 67 | +### Use Deployments for apps that should always be running |
| 68 | +A Deployment, which both creates a ReplicaSet to ensure that the desired number of Pods is always available, and specifies a strategy to replace Pods (such as [RollingUpdate](/docs/concepts/workloads/controllers/deployment/#rolling-update-deployment)), is almost always preferable to creating Pods directly. |
| 69 | +You can roll out a new version, and if something breaks, roll back instantly. |
| 70 | + |
| 71 | +### Use Jobs for tasks that should finish |
| 72 | +A [Job](/docs/concepts/workloads/controllers/job/) is perfect when you need something to run once and then stop like database migration or batch processing task. |
| 73 | +It will retry if the pods fails and report success when it's done. |
| 74 | + |
| 75 | +## Service Configuration and Networking |
| 76 | + |
| 77 | +Services are how your workloads talk to each other inside (and sometimes outside) your cluster. Without them, your pods exist but can't reach anyone. Let's make sure that doesn't happen. |
| 78 | + |
| 79 | +### Create Services before workloads that use them |
| 80 | +When Kubernetes starts a Pod, it automatically injects environment variables for existing Services. |
| 81 | +So, if a Pod depends on a Service, create a [Service](/docs/concepts/services-networking/service/) **before** its corresponding backend workloads (Deployments or StatefulSets), and before any workloads that need to access it. |
| 82 | + |
| 83 | +For example, if a Service named foo exists, all containers will get the following variables in their initial environment: |
| 84 | +``` |
| 85 | +FOO_SERVICE_HOST=<the host the Service runs on> |
| 86 | +FOO_SERVICE_PORT=<the port the Service runs on> |
| 87 | +``` |
| 88 | +DNS based discovery doesn't have this problem, but it's a good habit to follow anyway. |
| 89 | + |
| 90 | +### Use DNS for Service discovery |
| 91 | +If your cluster has the DNS [add-on](/docs/concepts/cluster-administration/addons/) (most do), every Service automatically gets a DNS entry. That means you can access it by name instead of IP: |
| 92 | +```bash |
| 93 | +curl http://my-service.default.svc.cluster.local |
| 94 | +``` |
| 95 | +It's one of those features that makes Kubernetes networking feel magical. |
| 96 | + |
| 97 | +### Avoid `hostPort` and `hostNetwork` unless absolutely necessary |
| 98 | +You'll sometimes see these options in manifests: |
| 99 | +```yaml |
| 100 | +hostPort: 8080 |
| 101 | +hostNetwork: true |
| 102 | +``` |
| 103 | +But here's the thing: |
| 104 | +They tie your Pods to specific nodes, making them harder to schedule and scale. Because each <`hostIP`, `hostPort`, `protocol`> combination must be unique. If you don't specify the `hostIP` and `protocol` explicitly, Kubernetes will use `0.0.0.0` as the default `hostIP` and `TCP` as the default `protocol`. |
| 105 | +Unless you're debugging or building something like a network plugin, avoid them. |
| 106 | + |
| 107 | +If you just need local access for testing, try [`kubectl port-forward`](/docs/reference/kubectl/generated/kubectl_port-forward/): |
| 108 | + |
| 109 | +```bash |
| 110 | +kubectl port-forward deployment/web 8080:80 |
| 111 | +``` |
| 112 | +See [Use Port Forwarding to access applications in a cluster](/docs/tasks/access-application-cluster/port-forward-access-application-cluster/) to learn more. |
| 113 | +Or if you really need external access, use a [`type: NodePort` Service](/docs/concepts/services-networking/service/#type-nodeport). That's the safer, Kubernetes-native way. |
| 114 | + |
| 115 | +### Use headless Services for internal discovery |
| 116 | +Sometimes, you don't want Kubernetes to load balance traffic. You want to talk directly to each Pod. That's where [headless Services](/docs/concepts/services-networking/service/#headless-services) come in. |
| 117 | + |
| 118 | +You create one by setting `clusterIP: None`. |
| 119 | +Instead of a single IP, DNS gives you a list of all Pods IPs, perfect for apps that manage connections themselves. |
| 120 | + |
| 121 | + |
| 122 | +## Working with labels effectively |
| 123 | + |
| 124 | +[Labels](/docs/concepts/overview/working-with-objects/labels/) are key/value pairs that are attached to objects such as Pods. |
| 125 | +Labels help you organize, query and group your resources. |
| 126 | +They don't do anything by themselves, but they make everything else from Services to Deployments work together smoothly. |
| 127 | + |
| 128 | +### Use semantics labels |
| 129 | +Good labels help you understand what's what, even after months later. |
| 130 | +Define and use [labels](/docs/concepts/overview/working-with-objects/labels/) that identify semantic attributes of your application or Deployment. |
| 131 | +For example; |
| 132 | +```yaml |
| 133 | +labels: |
| 134 | + app.kubernetes.io/name: myapp |
| 135 | + app.kubernetes.io/component: web |
| 136 | + tier: frontend |
| 137 | + phase: test |
| 138 | +``` |
| 139 | + - `app.kubernetes.io/name` : what the app is |
| 140 | + - `tier` : which layer it belongs to (frontend/backend) |
| 141 | + - `phase` : which stage it's in (test/prod) |
| 142 | + |
| 143 | +You can then use these labels to make powerful selectors. |
| 144 | +For example: |
| 145 | +```bash |
| 146 | +kubectl get pods -l tier=frontend |
| 147 | +``` |
| 148 | +This will list all frontend Pods across your cluster, no matter which Deployment they came from. |
| 149 | +Basically you are not manually listing Pod names; you are just describing what you want. |
| 150 | +See the [guestbook](https://github.com/kubernetes/examples/tree/master/web/guestbook/) app for examples of this approach. |
| 151 | + |
| 152 | +### Use common Kubernetes labels |
| 153 | +Kubernetes actually recommends a set of [common labels](/docs/concepts/overview/working-with-objects/common-labels/). It's a standardized way to name things across your different workloads or projects. |
| 154 | +Following this convention makes your manifests cleaner, and it means that tools such as [Headlamp](https://headlamp.dev/), [dashboard](https://github.com/kubernetes/dashboard#introduction), or third-party monitoring systems can all |
| 155 | +automatically understand what's running. |
| 156 | + |
| 157 | +### Manipulate labels for debugging |
| 158 | +Since controllers (like ReplicaSets or Deployments) use labels to manage Pods, you can remove a label to “detach” a Pod temporarily. |
| 159 | + |
| 160 | +Example: |
| 161 | +```bash |
| 162 | +kubectl label pod mypod app- |
| 163 | +``` |
| 164 | +The `app-` part removes the label key `app`. |
| 165 | +Once that happens, the controller won’t manage that Pod anymore. |
| 166 | +It’s like isolating it for inspection, a “quarantine mode” for debugging. To interactively remove or add labels, use [`kubectl label`](/docs/reference/kubectl/generated/kubectl_label/). |
| 167 | + |
| 168 | +You can then check logs, exec into it and once done, delete it manually. |
| 169 | +That’s a super underrated trick every Kubernetes engineer should know. |
| 170 | + |
| 171 | +## Handy kubectl tips |
| 172 | + |
| 173 | +These small tips make life much easier when you are working with multiple manifest files or clusters. |
| 174 | + |
| 175 | +### Apply entire directories |
| 176 | +Instead of applying one file at a time, apply the whole folder: |
| 177 | + |
| 178 | +```bash |
| 179 | +# Using server-side apply is also a good practice |
| 180 | +kubectl apply -f configs/ --server-side |
| 181 | +``` |
| 182 | +This command looks for `.yaml`, `.yml` and `.json` files in that folder and applies them all together. |
| 183 | +It's faster, cleaner and helps keep things grouped by app. |
| 184 | + |
| 185 | +### Use label selectors to get or delete resources |
| 186 | +You don't always need to type out resource names one by one. |
| 187 | +Instead, use [selectors](/docs/concepts/overview/working-with-objects/labels/#label-selectors) to act on entire groups at once: |
| 188 | + |
| 189 | +```bash |
| 190 | +kubectl get pods -l app=myapp |
| 191 | +kubectl delete pod -l phase=test |
| 192 | +``` |
| 193 | +It's especially useful in CI/CD pipelines, where you want to clean up test resources dynamically. |
| 194 | + |
| 195 | +### Quickly create Deployments and Services |
| 196 | +For quick experiments, you don't always need to write a manifest. You can spin up a Deployment right from the CLI: |
| 197 | + |
| 198 | +```bash |
| 199 | +kubectl create deployment webapp --image=nginx |
| 200 | +``` |
| 201 | + |
| 202 | +Then expose it as a Service: |
| 203 | +```bash |
| 204 | +kubectl expose deployment webapp --port=80 |
| 205 | +``` |
| 206 | +This is great when you just want to test something before writing full manifests. |
| 207 | +Also, see [Use a Service to Access an Application in a cluster](/docs/tasks/access-application-cluster/service-access-application-cluster/) for an example. |
| 208 | + |
| 209 | +## Conclusion |
| 210 | + |
| 211 | +Cleaner configuration leads to calmer cluster administrators. |
| 212 | +If you stick to a few simple habits: keep configuration simple and minimal, version-control everything, |
| 213 | +use consistent labels, and avoid relying on naked Pods, you'll save yourself hours of debugging down the road. |
| 214 | + |
| 215 | +The best part? |
| 216 | +Clean configurations stay readable. Even after months, you or anyone on your team can glance at them and know exactly what’s happening. |
| 217 | + |
0 commit comments