Writing kubernetes manifests by hand

Table of contents

Kubernetes manifests are used to describe just about everything in a cluster, from single pods to load balancers and custom resources. Learning the exact file structure for every kind can be a near-impossible task, making manifest writing a task much more daunting than it needs to be. This article discusses strategies to deal with writing kubernetes manifests, getting help and validating them before deployment.

Generating yaml with kubectl

Discussion about kubernetes resources is often done using yaml files. They intuitively lend themselves to debate because they clearly represent a specific state of a kubernetes object. But how do you get that file in the first place? You can let kubectl generate them directly from commands!

You do this by using the -o yaml flag to format the output as yaml, and --dry-run to not accidentally make changes to the kubernetes cluster yet. The --dry-run flag can have two different values: --dry-run=client will only create the yaml locally and not interact with the server, --dry-run=server will send the yaml as a proper request to the server, without actually making changes to the kubernetes cluster.

If you want to create a deployment with 5 replicas, you can have it generated for you:

kubectl create deployment test --image=nginx:latest --replicas=5 --dry-run=client -o yaml

This returns a yaml manifest representing the resource:

apiVersion: apps/v1
kind: Deployment
metadata:
 creationTimestamp: null
 labels:
   app: test
 name: test
spec:
 replicas: 5
 selector:
   matchLabels:
     app: test
 strategy: {}
 template:
   metadata:
     creationTimestamp: null
     labels:
       app: test
   spec:
     containers:
     - image: nginx:latest
       name: nginx
       resources: {}
status: {}

This manifest has a major advantage to copying something from the internet: it is the correct version for your kubernetes version. Since kubernetes is a rapidly evolving technology, there are already outdated yaml manifests floating around the internet, waiting for newcomers to trip over.


The kubectl create command can be used as a starting point for most resources, from pods and deployments to services and ingresses. Each kind of resource has it's own flags to set options. The help output changes depending on which resource and arguments are passed to reflect that:

kubectl help create service

The output will be a list of service types the command could create. To get help for a specific service type, for example ClusterIP, run the help command with that argument:

kubectl help create service clusterip

Now the options for creating a ClusterIP service will be displayed. When looking for information on how to create resources using kubectl, refer to the help command of the specific resource you are trying to create, as arguments will differ between resource kinds.

Finding resources and detailed explanations

When working with a yaml manifest, not all parts may be immediately obvious. To help users deal with this complexity, kubectl includes the explain command, that can provide detailed explanations for every field of every manifest kind:

kubectl explain pod

This provide a list of fields for Pod manifests with descriptions and notes on how to use them. Note that only the top-level fields are displayed by default. You can view all fields at once by adding the --recursive flag:

kubectl explain pod --recursive

Note that this time, only the field names are displayed, without descriptions. This is intentional, to allow the already large output to be scanned quickly. A detailed explanation of every field can be viewed by providing the path to the field in question. For example to get help for the annotations field inside the metadata field of the Pod resource kind, simply separate them with dots:

kubectl explain pod.metadata.annotations

The last missing piece of information is knowing what kinds of resources exist in the first place. You can get a list from kubectl as well:

kubectl api-resources

The output contains a list of all resources you could create on the cluster (and view help descriptions for), along with their current api version and shortnames.

Validating yaml manifests

Now that we know how to create yaml manifests, we need a way to ensure they are correct, preferably before applying them to the cluster. To ensure your yaml file is a valid manifest, validate it with kubeval:

kubeval --strict my-deployment.yml

Note the use of the --strict flag, which treats yaml fields that are not part of the spec as errors. This helps catch misspelled field names.

If you want to catch more complex issues than syntax errors, you will need a tool like kube-score to perform static analysis on the manifest and provide pointers at possibly unwanted behaviour or pitfalls with the current configuration. You can install it as a kubectl plugin:

kubectl krew install score

Once installed, any yaml manifest can be scanned with kubectl score:

kubectl score my-deployment.yml

Since static analysis is a lot more strict and versatile than simple validity checks, this is almost certain to complain about your manifest. There is no reason to blindly follow every suggestion given in the output, they are just pointers to ensure you are aware of the consequences or possible side effects of the current configuration. Whether you find them problematic or not is up to you and your requirements.

If you cannot install the kubectl plugin, you could use the online version kube-score.com.




Writing Kubernetes resource manifests is a complex task involving a lot of research and careful configuration decisions. The kubectl command contains everything operators need to create hand-tailored configurations for every use case, with validators and static analysis being available to catch errors before they get deployed. While it can be tempting, always remember that copying manifests from the internet runs the risk of deploying outdated or even vulnerable configurations to your cluster.

More articles

PHP output buffering

Controlling what gets sent and when

Automating local domains for minikube

Local ingress domains that just work

Sharing local websites and files with zrok

Pain-free open source proxies to localhost

The downsides of source-available software licenses

And how it differs from real open-source licenses

Configure linux debian to boot into a fullscreen application

Running kiosk-mode applications with confidence

How to use ansible with vagrant environments

Painlessly connect vagrant infrastructure and ansible playbooks