Kubernetes 1.18 was an important release because it introduced or previewed several capabilities that eventually became standard parts of modern Kubernetes administration. But a Kubernetes 1.18 tutorial is now historical. Kubernetes 1.36 is the current minor release as of this update, while 1.18 has been unsupported for years.

The useful lesson today is not what was new in Kubernetes 1.18. It is how those features evolved and how administrators and developers use their modern equivalents.

Use kubectl run to create Pods

One change introduced with Kubernetes 1.18 remains important: kubectl run is focused on creating Pods rather than acting as a general-purpose imperative generator for Deployments, Jobs and other workload resources.

For example, create a Pod with:

kubectl run my-pod \
  --image=nginx:latest \
  --port=80

Then inspect it with:

kubectl get pod my-pod
kubectl describe pod my-pod

For a Deployment, use the resource-specific command instead:

kubectl create deployment my-app \
  --image=nginx:latest

For production workloads, declarative YAML or another configuration-management approach is generally preferable because the desired state can be reviewed, versioned and reproduced.

Modern Kubernetes debugging with kubectl debug

The early kubectl debug functionality discussed when Kubernetes 1.18 arrived has matured substantially. Modern kubectl debug can add an ephemeral debugging container to a running Pod, create a modified copy of a Pod, or create a debugging Pod for a node.

Suppose a running Pod contains a minimal application image without tools such as curl, ps or a shell. You can attach an ephemeral debugging container without rebuilding the application's image:

kubectl debug -it my-pod \
  --image=busybox:1.37 \
  --target=my-container

You can also create a debugging copy of an existing Pod:

kubectl debug my-pod -it \
  --copy-to=my-pod-debug \
  --container=my-container \
  -- sh

Node debugging is supported as well:

kubectl debug node/my-node -it \
  --image=ubuntu:24.04

This makes kubectl debug useful for troubleshooting workloads whose production images intentionally omit diagnostic utilities.

Use declarative Kubernetes manifests

A modern Pod manifest uses ordinary YAML with no HTML entities or formatting mixed into the code:

apiVersion: v1
kind: Pod
metadata:
  name: static-web
  labels:
    app: web
spec:
  containers:
    - name: web
      image: nginx:latest
      ports:
        - name: http
          containerPort: 80
          protocol: TCP

Apply the manifest with:

kubectl apply -f my-pod.yaml

Server-Side Apply is now stable

Server-Side Apply was still evolving when Kubernetes 1.18 was released. It became stable in Kubernetes 1.22 and remains an important declarative resource-management mechanism.

Use it explicitly with:

kubectl apply --server-side -f deployment.yaml

Server-Side Apply lets the Kubernetes API server track which field manager owns individual resource fields. That is more precise than simply recording who last changed an entire object, and it helps multiple users and controllers safely manage different parts of the same resource.

You can inspect managed fields with:

kubectl get deployment my-app \
  -o yaml \
  --show-managed-fields

Modern kubectl also supports user preferences through kuberc, including a preference that makes Server-Side Apply the default for kubectl apply.

Windows worker nodes in modern Kubernetes

Kubernetes supports clusters with Linux and Windows worker nodes. The control plane runs on Linux, while Windows Server nodes can host Windows container workloads.

For many applications, scheduling a Windows workload starts with the standard operating-system node label:

apiVersion: v1
kind: Pod
metadata:
  name: windows-app
spec:
  nodeSelector:
    kubernetes.io/os: windows

  containers:
    - name: app
      image: mcr.microsoft.com/windows/nanoserver:ltsc2022
      command:
        - ping
        - -t
        - localhost

Real Windows deployments must also account for Windows version compatibility, networking, storage and Windows-specific security behavior. RuntimeClass remains available when a workload needs a particular container runtime configuration, but it is not necessary simply to express that a Pod requires a Windows node.

Immutable Kubernetes Secrets

The immutable field first appeared as an emerging capability around the Kubernetes 1.18 era. Immutable Secrets and ConfigMaps became stable in Kubernetes 1.21.

A modern immutable Secret looks like this:

apiVersion: v1
kind: Secret
metadata:
  name: database-credentials
type: Opaque
immutable: true
stringData:
  username: admin
  password: change-me

For a simple command-line example, avoid putting production passwords directly into shell history. One convenient development approach is:

kubectl create secret generic database-credentials \
  --from-literal=username=admin \
  --from-file=password=./password.txt

A Kubernetes Secret is not automatically encrypted merely because it is a Secret. Base64 encoding is not encryption. Protect Secret data with appropriate RBAC, encryption at rest and secure external secret-management practices where required.

Once a Secret is marked immutable, its data cannot be changed in place. To change the value, create a replacement Secret and update the workloads that reference it.

Immutable ConfigMaps

ConfigMaps can also be immutable:

apiVersion: v1
kind: ConfigMap
metadata:
  name: application-config-v1
immutable: true
data:
  environment: production
  log.level: INFO

Immutable configuration protects applications from accidental changes and can reduce API server load because kubelets do not need to continuously watch immutable Secrets and ConfigMaps for updates.

If the configuration changes, create a new object, such as application-config-v2, and update the workload to reference the new name. This also makes configuration changes explicit and easier to roll back.

What changed since Kubernetes 1.18?

  • kubectl run remains a convenient way to create a Pod, while resource-specific commands and declarative manifests handle other workload types.
  • kubectl debug matured into a powerful troubleshooting command for Pods, workloads and nodes.
  • Server-Side Apply graduated to stable and provides field ownership and conflict detection for declarative resource management.
  • Windows worker-node support matured substantially and now has dedicated scheduling, security, networking, storage and debugging guidance.
  • Immutable Secrets and ConfigMaps graduated to stable and are useful both for safety and cluster efficiency.

Modern Kubernetes administration

Kubernetes 1.18 deserves credit for introducing several ideas that became important parts of the platform. But administrators should not copy Kubernetes 1.18 commands and manifests blindly into a current cluster.

Modern Kubernetes favors declarative configuration, Server-Side Apply where appropriate, purpose-built debugging with kubectl debug, explicit workload scheduling and carefully managed immutable configuration. Those practices provide a much better foundation for current production clusters than the experimental Kubernetes 1.18 examples from 2020.

Top Joe Rogan JRE podcasts for programmers and developers